<?php

namespace App\Repositories\Pos;

use App\Models\Bank;
use App\Models\Survey;
use App\Models\PosInvoice;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use App\Models\Items;
use App\Models\Location;
use App\Models\Role;
use App\Models\Sales;
use App\Models\Voucher;
use App\Models\VoucherClaim;
use App\Models\AffiliatorItem;
use App\Models\VoucherEvent;
use App\Models\AffiliatorItemCode;
use App\Models\Incentive;
use App\Helpers\AutoNumbering;
use App\Models\XenditLink;
use App\Models\ItemReference;
use App\Models\LuckyWheel;
use App\Models\SerialNumberDetail;
use App\Models\SerialNumberLog;
use App\Notifications\Crm\SurveyBroadcast;
use App\Repositories\Crm\SurveyRepository;
use Illuminate\Validation\ValidationException;

class InvoiceRepository
{

    var $surveyRepository;

    public function __construct(SurveyRepository $surveyRepository){
        $this->surveyRepository = $surveyRepository;
    }

    public function getList(array $params = [], $all = false)
    {
        $user = auth()->user();
        $location_id = @$user->employee->location_id;

        $sortColumn = [
            'number' => 'pos_invoices.number',
            'time' => 'pos_invoices.time',
            'total' => 'pos_invoices.total',
            'location.name' => 'locations.name',
            'customer' => 'customers.name',
            'sales.name' => 'sales.name',
            'sales2.name' => 'sales.name',
            'user.name' => 'users.name'
        ];

        $limit = @$params["limit"] ? (int) @$params["limit"] : 10;
        $sortColumn = @$params["sort"]["column"] ? $sortColumn[$params["sort"]["column"]] : "pos_invoices.id";
        $sortDir = @$params["sort"]["dir"] ? $params["sort"]["dir"] : "desc";

        $results = PosInvoice::selectRaw("
                pos_invoices.*,
                CASE WHEN a.first_transaction is not null THEN 1 ELSE 0 END as new_customer,
                CASE WHEN (a.first_transaction IS NOT NULL AND customers.number NOT ILIKE 'CAG%') OR customers.number = 'NONAME' THEN 'online' ELSE 'offline' END as type"
            )
            ->leftJoin(DB::raw("(select min(pos_invoices.id) as first_transaction from pos_invoices group by customer_id) as a"), 'a.first_transaction', 'pos_invoices.id')
            ->leftJoin('transactions', 'transactions.invoice_id', 'pos_invoices.id')
            ->leftJoin('locations', 'locations.id', 'pos_invoices.location_id')
            ->leftJoin('customers', 'customers.id', 'pos_invoices.customer_id')
            ->leftJoin('users', 'users.id', 'pos_invoices.user_id')
            ->leftJoin('sales', 'sales.id', 'pos_invoices.sales_id')
            ->leftJoin('sales as sales2', 'sales2.id', 'pos_invoices.sales2_id')
            ->when(@$params["search"], function ($query) use ($params) {
                $query->where(function ($query) use ($params) {
                    $query->where("pos_invoices.number", "ilike", "%" . $params["search"] . "%")
                        ->orWhere("sales.name", "ilike", "%" . $params["search"] . "%")
                        ->orWhere("sales2.name", "ilike", "%" . $params["search"] . "%")
                        ->orWhere("users.name", "ilike", "%" . $params["search"] . "%")
                        ->orWhere("customers.name", "ilike", "%" . $params["search"] . "%")
                        ->orWhere("customers.phone", "ilike", "%" . $params["search"] . "%");
 /*                       ->orWhereExists(function($subquery) use ($params) {
                            $subquery->select(DB::raw(1))
                                ->from('pos_invoice_details')
                                ->leftJoin('items', 'pos_invoice_details.item_id', 'items.id')
                                ->leftJoin('item_variants', 'item_variants.id', 'pos_invoice_details.item_variant_id')
                                ->leftJoin('item_reference', 'item_reference.id', 'pos_invoice_details.item_reference_id')
                                ->whereColumn('pos_invoice_details.invoice_id', 'pos_invoices.id')
                                ->where(function($q) use ($params) {
                                    $q->where('item_reference.number', 'ilike', '%' . $params['search'] . '%')
                                      ->orWhere('items.number', 'ilike', '%' . $params['search'] . '%')
                                      ->orWhere('items.name', 'ilike', '%' . $params['search'] . '%')
                                      ->orWhere('item_variants.description', 'ilike', '%' . $params['search'] . '%')
                                      ->orWhere('item_variants.code', 'ilike', '%' . $params['search'] . '%');
                                }); 
                        });  */
                }); 
            })
            ->when(!$all, function ($query) use ($location_id) {
                $query->where("pos_invoices.location_id", $location_id);
            })
            ->when(@$params['active'], function($query) {
                $query->whereNull('canceled_at');
            })
            ->when(@$params['filter'], function ($query) use ($params) {
                foreach ($params['filter'] as $filter) {
                    if ($filter['column'] == 'start') {
                        $query->whereDate("pos_invoices.time", ">=", $filter["query"]);
                    } else if ($filter['column'] == 'end') {
                        $query->whereDate("pos_invoices.time", "<=", $filter["query"]);
                    } else if ($filter['column'] == 'sync') {
                        if ($filter['query'] == 'BERHASIL') {
                            $query->whereNotNull('pos_invoices.sync_at');
                        }

                        if ($filter['query'] == 'GAGAL') {
                            $query->whereNull('pos_invoices.sync_at')
                                ->whereNotNull('pos_invoices.sync_log');
                        }
                    } else if ($filter['column'] == 'payment_method') {
                        $query->whereRaw("EXISTS (SELECT id FROM pos_invoice_payments WHERE invoice_id = pos_invoices.id AND method = '" . $filter['query'] . "'  AND amount > 0)");
                    } else {
                        $query->where('pos_invoices.' . $filter['column'],  $filter['query']);
                    }
                }
            })
            ->orderBy($sortColumn, $sortDir)
            ->paginate($limit);
        return $results;
    }

    public function getUnPosted()
    {
        return PosInvoice::whereNull("sync_at")
            ->whereNull("canceled_at")
            ->take(1000)->get();
    }

    public function createFromOnline($model)
    {
        DB::beginTransaction();
        try{
            $sales = Sales::where("name","ECOMMERCE")->first();
            $user = auth()->user();
            $posInvoice = PosInvoice::find((int) $model->invoice_id);
            if ($posInvoice)
                return;

            $location = $model->location;
            $auto = new AutoNumbering([
                "type" => "POS",
                "prefix" => str_replace(" ", "", $location->code),
                "location_id" => $location->id,
                "pad" => 12
            ]);
            $number = $auto->getCurrent();

            $invoice = new PosInvoice([
                "number" => $number,
                "user_id" => $user->id,
                "customer_id" => $model->customer_id,
                "time" => $model->time,
                "location_id" => $location->id,
                "sales_id" => (int) @$sales->id,
                "note" => @$model->note,
                "subtotal" => $model->subtotal,
                "voucher" => 0,
                "discount" => 0,
                "tax" => 0,
                "total" => $model->subtotal + $model->shipping_price,
                "vouchers" => "",
                "note_internal" => $model->number,
            ]);
            $invoice->save();
            $model->invoice_id = $invoice->id;
            $model->save();

            foreach ($model->details as $row) {
                $invoice->details()->create([
                    'item_reference_id' => $row->item_reference_id,
                    'item_id' => $row->item_id,
                    'item_variant_id' => $row->item_variant_id,
                    'note' => '',
                    'qty' => $row->qty,
                    'unit_price'=> $row->unit_price,
                    'unit'=> $row->unit,
                    'reference'=> $row->reference->number,
                    'discount'=> (int) @$row->discount,
                    'vat'=> 0,
                    'total'=> $row->total,
                    'unit_cost'=> $row->unit_cost
                ]);
            }

            // find biaya expedisi + asuransi
            $item  = ItemReference::where("number","9CBYEXPDAS")->first();

            if ($model->shipping_price && $item){
                $invoice->details()->create([
                    'item_reference_id' => $item->id,
                    'item_id' => $item->item_id,
                    'item_variant_id' => $item->item_variant_id,
                    'note' => $model->courier_company." ".$model->courier_type,
                    'qty' => 1,
                    'unit_price' => $model->shipping_price,
                    'unit' => 'PCS',
                    'reference' => $item->number,
                    'discount' => 0,
                    'vat' => 0,
                    'total'=> $model->shipping_price,
                    'unit_cost'=> $model->shipping_price
                ]);
            }

            foreach ($model->payments as $row) {

                if ($row->method_type == XenditLink::class){
                    if ($row->status == "PAID"){

                        $invoice->payments()->create([
                            "method" => "XENDIT",
                            "amount" => $row->amount,
                            "bank" => "XENDIT",
                            "card_number" =>  $row->method->uid,
                            "remarks" => @$row->method->payment_method." ".@$row->method->bank_code
                        ]);
                    }
                }
            }

            foreach($model->vouchers as $voucher){

                $invoice->payments()->create([
                    "method" => "VOUCHER",
                    "amount" => $voucher->nominal,
                    "bank" => "VOUCHER",
                    "card_number" =>  $voucher->number,
                    "remarks" => ""
                ]);
            }
            $model->invoice_id = $invoice->id;
            $model->save();

            $survey = Survey::where("name","AFTER PURCHASE")->first();
            if ($survey){
                $data = [
                    "channel" => "pos",
                    "customer_id" => $invoice->customer_id,
                    "invoice_id" => $invoice->id
                ];
                $this->surveyRepository->generateFeedback($survey, $data);
            }

            DB::commit();

            return $invoice;
        }catch(\Exception $e){
            DB::rollback();
            throw $e;
        }
    }

    public function create(array $data)
    {
        return DB::transaction(function () use ($data) {

            $numbering = (array) @DB::select("SELECT id, transaction, location_id, prefix, pad, current
                FROM numbering
                WHERE transaction = ? AND location_id = ?
                FOR UPDATE
            ", ["POS", $data["location_id"]])[0];

                $location = Location::findOrFail($data["location_id"]);
            if ($numbering == null) {
                $numbering = DB::table("numbering")->insert([
                    "transaction" => "POS",
                    "location_id" => $data["location_id"],
                    "prefix" => str_replace(" ", "", $location->code),
                    "pad" => 12,
                    "current" => 0
                ]);

                $numbering = (array)  DB::select("SELECT id, transaction, location_id, prefix, pad, current
                    FROM numbering
                    WHERE id = ?
                    FOR UPDATE
                ", [DB::getPdo()->lastInsertId()])[0];
            }

            if (Carbon::now()->gte("2024-01-22")) {
                $prefix_number = $numbering["prefix"];
                $next_number = $numbering["current"] + 1;
                $pad_number = $numbering["pad"] - strlen($prefix_number);
                $number = $prefix_number . str_pad($next_number, $pad_number, 0, STR_PAD_LEFT);
                DB::statement("UPDATE numbering SET current = current+1 WHERE id = ?", [$numbering["id"]]);
            } else {
                $count = DB::select("select last_value  from pos_invoices_id_seq")[0]->last_value;
                $number = "POS" . str_pad($count + 1, 6, 0, STR_PAD_LEFT);
            }


            // $count = DB::select("select last_value  from pos_invoices_id_seq")[0]->last_value;
            $data["user_id"] = auth()->user()->id;
            $data["voucher"] = (float) @$data["voucher"];
            $data["time"] = Carbon::now();
            $data["vouchers"] = @$data["vouchers"] ? implode(",", @$data["vouchers"]):"";
            $data["number"] = $number;
            $subtotal = (float) @$data["subtotal"];
            $discount = (float) @$data["discount"];
            $invoice = PosInvoice::create($data);

            $exclude_item_points = [];

            $i = 0;
            foreach ($data["details"] as $row) {
                $i++;

                $item = Items::find($row["item_id"]);
                $row["line_no"] = $i;
                $row["unit_price"] = (float) @$row["unit_price"];
                $row["total"] = (float) @$row["total"];
                $row["discount"] = (float) @$row["discount"];
                $row["vat"] = (float) @$row["vat"];
                $row["unit_cost"] = @$item ? $item->unit_cost : 0;
                $row["line_discount"] = @$row["discount"] * @$row["qty"];
                $row["invoice_discount"] = $subtotal == 0 ? 0: ($row["total"] / $subtotal) * $discount;
                $row["net_price"] = @$row['net_price'];

                if (@$row['serial_number']){
                    $this->activateSerialNumber($invoice, @$row['serial_number'],[
                        "reference" => @$row["reference"],
                        "description" => @$row["description"],
                        "item_number" => @$row["item_number"],
                        "variant_code" => @$row["variant_code"],
                        "brand" => @$item->dimension->brand
                    ]);
                }
                $row["pricelist_discount"] = @$row['pricelist_discount'];
                if (strpos($item->name, "VOUCHER") > -1) {
                    $exp = $row["unit_price"] == 0 ? 3 : 6;
                    $row["reference"] = $this->issueVoucher($invoice, $item->name, $row["reference"], (float) @$row["qty"], $exp);
                }
                $invoice->details()->create($row);
            }

            // Handle DISCOUNT vouchers - save to pos_invoice_vouchers table
            if (@$data["discount_vouchers"]) {
                $this->useDiscountVoucher($invoice, $data["discount_vouchers"]);
            }

            // Handle SALE vouchers (payment vouchers) - legacy behavior
            if (@$data["vouchers"])
                $exclude_item_points = $this->useVoucher($invoice, $data["vouchers"]);

            $this->checkPoint($invoice, $exclude_item_points);

            foreach ($data["payments"] as $row) {

                if ($row["amount"] <= 0)
                    continue;

                $invoice->payments()->create($row);

                if ($row['method'] == "POINT"){

                    DB::table("customer_points")
                    ->insert([
                        "description" => "Use points for payment transaction " . $invoice->number,
                        "point" => ($row["amount"] / 1000) * -1,
                        "customer_id" => $invoice->customer_id,
                        "reference_id" => $invoice->id,
                        "reference_type" => get_class($invoice),
                        "created_at" => Carbon::now()
                    ]);
                }else if ($row['method'] == "VOUCHER"){

                    $voucher = Voucher::where('number', $row['card_number'])->first();
                    if ($voucher){
                        // Validate voucher is SALE type for payment method
                        if (($voucher->calculation_type ?? 'SALE') != 'SALE') {
                            throw ValidationException::withMessages([
                                "voucher" => "Voucher ini adalah Voucher Discount, bukan Voucher Belanja. Tidak bisa digunakan sebagai pembayaran."
                            ]);
                        }
                        
                        $voucher->used_at = Carbon::now();
                        $voucher->reference_used_id = $invoice->id;
                        $voucher->reference_used_type = get_class($invoice);
                        $voucher->save();
                    }
                }
            }

        $luckyWheel = LuckyWheel::where("valid_at","<=", Carbon::now())
            ->where("expired_at",">=", Carbon::now())
            ->where(function($query) use ($location){
                $query->whereNull("location_id")
                    ->orWhere("location_id", @$location->id);
            })
             ->where("min_sales_total","<", $invoice->total)
            ->first();

            if ($luckyWheel){
                $ticket_counts = $luckyWheel->tickets()->count();
                if ($ticket_counts < $luckyWheel->max_ticket || $luckyWheel->max_ticket == -1){
                    $max_prize = $luckyWheel->max_prize;
                    $current_prize = $luckyWheel->gets()->whereNotNull("redeem_at")->sum("nominal");

//                    if ($max_prize > $current_prize){

                        // $ticket2 = $luckyWheel->tickets()->where("customer_id", $invoice->customer_id)->first();
                        // if ($ticket2 == null){
                        $ticket = $luckyWheel->tickets()->create([
                            "invoice_id" => $invoice->id,
                            "customer_id" => $invoice->customer_id,
                            "max_times" => 10
                        ]);
                        // }
  //                  }
                }
            }

            $survey = Survey::where("name","AFTER PURCHASE")->first();
            if ($survey){
                $data = [
                    "channel" => "pos",
                    "customer_id" => $invoice->customer_id,
                    "invoice_id" => $invoice->id
                ];
                $feedback = $this->surveyRepository->generateFeedback($survey, $data);
                if ($feedback){
                    $notif = new SurveyBroadcast($feedback);
                    $invoice->customer->notify($notif);
                }
            }

            return $invoice;
        });
    }

    public function deactivateSerialNumber($invoice, $sn){
        if (!@$sn)
            return;

        $arr = explode(",",$sn);
        SerialNumberDetail::whereIn("number",$arr)->update([
            "activated_at" => null
        ]);
        SerialNumberLog::whereIn('number',$arr)->delete();
    }

    public function activateSerialNumber($invoice, $sn, $data){
        if (!@$sn)
            return;

        $user = auth()->user();
        $arr = explode(",",$sn);
        foreach($arr as $row){
            $snDetail = SerialNumberDetail::where("number",$row)->first();
            if ($snDetail){
                $snDetail->activated_at = Carbon::now();
                $snDetail->activated_by = $user->id;
                $snDetail->invoice_id = $invoice->id;
                $snDetail->save();
            }

            $snLog = new SerialNumberLog;
            $snLog->number = $row;
            $snLog->activated_at = Carbon::now();
            $snLog->reference_number = $data["reference"] ?? "";
            $snLog->item_number = $data["item_number"] ?? "";
            $snLog->variant_code = $data["variant_code"] ?? "";
            $snLog->description = $data["description"] ?? "";
            $snLog->brand = $data["brand"] ?? "";
            $snLog->invoice_no = $invoice->number;
            $snLog->save();
        }
    }

    public function checkPoint($invoice, $item_ids)
    {

        $point = 0;
        foreach ($invoice->details as $detail) {
            $discount_items = DB::select("select apply_point from discounts left join discount_items on discounts.id = discount_items.discount_id
            where item_reference_id = ? and valid_at <= NOW() and expired_at >= NOW() order by discounts.id desc", [@$detail->item_reference_id]);

            $apply_point = count($discount_items) > 0 ? $discount_items[0]->apply_point : false;
            $discount = (float) @$detail->discount;
            $qty = (float) @$detail->qty;

            if(!$apply_point) {

                if ($discount > 0){
                    continue;
                }

                if ($detail->unit_price < $detail->item->net_price){
                    continue;
                }

                // skip is has discount from voucher
                if (in_array($detail->item_reference_id, $item_ids)){
                    continue;
                }
            }

            $rows = DB::select(
                "select point from customer_point_rules WHERE item_reference_id = ? AND qty = ?",
                [$detail->item_reference_id, $detail->qty]
            );
            if (count($rows)) {

                $row_point = (float) @$rows[0]->point;
                $detail->point = $row_point * $qty;
                $detail->save();
                $point += $detail->point;
            } else {

                $rows_general =  DB::select(
                    "select point from customer_point_rules WHERE item_reference_id = ? AND (qty is null or qty = 0)",
                    [$detail->item_reference_id]
                );

                if (count($rows_general)) {
                    $row_point = (float) @$rows_general[0]->point;
                    $detail->point = $row_point * $qty;
                    $detail->save();
                    $point += $detail->point;
                }
            }
        }

        if ($point > 0 && $invoice->location->code != "IND") {
            DB::table("customer_points")
                ->insert([
                    "description" => "Get points from transaction " . $invoice->number,
                    "point" => $point,
                    "customer_id" => $invoice->customer_id,
                    "reference_id" => $invoice->id,
                    "reference_type" => get_class($invoice),
                    "created_at" => Carbon::now()
                ]);
        }
    }

    private function useVoucher($invoice, $vouchers)
    {
        $exclude_item_points = [];
        $arr = explode(",", $vouchers);
        foreach ($arr as $voucher) {
            $item = Voucher::where("number", $voucher)->first();
            if (!$item)
                continue;

            $item_id = $this->checkAffiliatorVoucher($invoice, $item);
            if (count($item_id) > 0)
            {
                $exclude_item_points = array_merge($exclude_item_points, $item_id);
            }

            if (@$item->event){
                $item_reference_ids = $item->event->items->pluck("id")->toArray();
                $exclude_item_points = array_merge($exclude_item_points, $item_reference_ids);
            }

            $item->used_at = Carbon::now();
            $item->reference_used_id = $invoice->id;
            $item->reference_used_type = get_class($invoice);
            $item->save();
        }

        return $exclude_item_points;
    }

    private function useDiscountVoucher($invoice, $discount_vouchers)
    {
        // discount_vouchers is an array of voucher codes
        foreach ($discount_vouchers as $voucher_code) {
            $voucher = Voucher::where("number", $voucher_code)->first();
            if (!$voucher)
                continue;

            // Validate it's a DISCOUNT type voucher
            if (($voucher->calculation_type ?? 'SALE') != 'DISCOUNT') {
                throw ValidationException::withMessages([
                    "discount_voucher" => "Voucher {$voucher_code} bukan Voucher Discount."
                ]);
            }

            // Create pos_invoice_vouchers record
            \App\Models\PosInvoiceVoucher::create([
                'pos_invoice_id' => $invoice->id,
                'voucher_id' => $voucher->id,
                'nominal' => $voucher->nominal
            ]);

            // Mark voucher as used
            $voucher->used_at = Carbon::now();
            $voucher->reference_used_id = $invoice->id;
            $voucher->reference_used_type = get_class($invoice);
            $voucher->save();
        }
    }

    private function findDetail($invoice, $item_reference_ids){
        return $invoice->details->filter(function($detail) use ($item_reference_ids){
            return in_array($detail->item_reference_id , $item_reference_ids);
        })->reduce(function($acc, $detail){
            return $acc + ($detail->unit_price - $detail->unit_cost);
        },0);
    }

    public function checkAffiliatorVoucher($invoice, $voucher){
        if ($voucher->reference_issued_type != VoucherClaim::class){
            return [];
        }
        $issued = $voucher->referenceIssued;

        if ($issued->claimable_type != AffiliatorItemCode::class){
            return [];
        }

        $claimable = $issued->claimable;
        if ($claimable == null){
            return [];
        }

        $affiliator = $claimable->affiliator;

        if ($claimable->affiliatorItem != null){
            $affiliatorItem = $claimable->affiliatorItem;

            $itemReference = $affiliatorItem->item;
            $item_reference_ids = [$itemReference->id];
            $gross = $this->findDetail($invoice, $item_reference_ids);
            $fee_nominal  = $affiliatorItem->fee ? $affiliatorItem->fee :  $gross * ($affiliator->fee_percentage/100);

        }else if ($claimable->codeable_type == Affiliatoritem::class){

            $affiliatorItem = $claimable->codeable;
            $itemReference = $affiliatorItem->item;
            $item_reference_ids = [$itemReference->id];
            $gross = $this->findDetail($invoice, $item_reference_ids);
            $fee_nominal  = $affiliatorItem->fee ? $affiliatorItem->fee :  $gross * ($affiliator->fee_percentage/100);

        }else if ($claimable->codeable_type == VoucherEvent::class){
            $voucherEvent = $claimable->codeable;
            $item_reference_ids = $voucherEvent->items->pluck("id")->toArray();
            $gross = $this->findDetail($invoice, $item_reference_ids);
            $fee_nominal =  $gross * ($affiliator->fee_percentage/100);
        }else{
            return [];
        }


        $incentive = new Incentive;
        $incentive->fill([
            "employee_id" => 0,
            "person_id" => $affiliator->id,
            "person_type" => get_class($affiliator),
            "nominal" => $fee_nominal,
            "date" => Carbon::now(),
            "reference_id" => $voucher->id,
            "reference_type" => get_class($voucher),
            "status" => "OPEN"
        ]);
        $incentive->save();

        return $item_reference_ids;
    }

    private function generateVoucher($nominal)
    {
        $code = "BLJ";
        if ($nominal == 2000000) {
            $code = "2JT";
        } else if ($nominal == 1000000) {
            $code = "1JT";
        } else if ($nominal == 1500000) {
            $code = "1.5JT";
        } else if ($nominal == 2500000) {
            $code = "2.5JT";
        } else if ($nominal == 500000) {
            $code = "500RB";
        } else if ($nominal == 750000) {
            $code = "750RB";
        } else if ($nominal == 250000) {
            $code = "250RB";
        } else if ($nominal == 100000) {
            $code = "100RB";
        } else if ($nominal == 50000) {
            $code = "50RB";
        } else if ($nominal == 3000000) {
            $code = "3JT";
        }
        $voucher = null;
        $iter = 0;
        while ($voucher == null) {
            $new_code = strtoupper("EV/" . $code . "/" . date("Y") . "/" . bin2hex(openssl_random_pseudo_bytes(3)));
            $exists = Voucher::where("number", $new_code)->first();
            $voucher = $exists ? null : $new_code;
        }
        return $voucher;
    }

    private function issueVoucher($invoice, $name, $vouchers, $qty, $exp)
    {

        $nominal = str_replace(".", "", $name);
        preg_match("/[0-9]++/", $nominal, $match);
        $nominal = (float) @$match[0];

        $evoucher = true;
        if ($vouchers == "") {
            $arr = [];
            for ($i = 0; $i < $qty; $i++) {
                $arr[] = $this->generateVoucher($nominal);
            }

            $evoucher = true;
        } else {
            $arr = explode(",", $vouchers);

            $evoucher = false;
        }
        foreach ($arr as $voucher) {
            $voucher = trim($voucher);
            $item = Voucher::where("number", $voucher)->firstOrNew();
            $item->customer_id = $invoice->customer_id;
            $item->evoucher = $evoucher;
            $item->number = $voucher;
            $item->nominal = $nominal;
            $item->issued_at = Carbon::now();
            $item->expired_at = Carbon::now()->addMonth($exp);
            $item->reference_issued_id = $invoice->id;
            $item->reference_issued_type = get_class($invoice);
            $item->save();
        }

        return implode(",", $arr);
    }

    public function update(PosInvoice $invoice, array $data)
    {
        if (!$this->isAdmin()) {
            $isCreatedToday = Carbon::now()->diffInMinutes(Carbon::parse($invoice->time)) <= (60 * 24);
            if (!$isCreatedToday) {
                return abort(403, 'hanya invoice yang belum 24 jam yang bisa di edit');
            }
        }

        $data["vouchers"] = implode(",", $data["vouchers"]);

        $invoice->update($data);

        $ids = [];
        foreach ($data["details"] as $row) {
            $detail = $invoice->details()->find(@$row["id"]);
            if (!$detail){
                $item = Items::find($row["item_id"]);
                $row["unit_price"] = (float) @$row["unit_price"];
                $row["total"] = (float) @$row["total"];
                $row["unit_cost"] = @$item ? $item->unit_cost : 0;
                $detail = $invoice->details()->create($row);
            }else{
                $detail->update([
                    "qty" => $row["qty"],
                    "serial_number" => $row["serial_number"],
                    "total" => $row["total"],
                    "unit_price" => $row["unit_price"],
                    "discount" => $row["discount"],
                ]);
                $item = $detail->item;
            }

            $this->deactivateSerialNumber($invoice, @$row['serial_number']);

            $this->activateSerialNumber($invoice, @$row['serial_number'],[
                "reference" => @$row["reference"],
                "description" => @$row["description"],
                "item_number" => @$row["item_number"],
                "variant_code" => @$row["variant_code"],
                "brand" => @$item->dimension->brand
            ]);

            $ids[] = $detail->id;
        }
        $invoice->details()->whereNotIn("id",$ids)->delete($ids);

        $ids = [];
        foreach ($data["payments"] as $row) {
            if ($row["amount"] <= 0)
                continue;

            $detail = $invoice->details()->find(@$row["id"]);
            if (!$detail){
                $detail = $invoice->payments()->create($row);
            }else{
                $detail->update($row);
            }
            $ids[] = $detail->id;
        }
        $invoice->payments()->whereNotIn("id",$ids)->delete();

        return $invoice;
    }

    public function cancel(PosInvoice $item, $data)
    {
        $data["canceled_by"] = auth()->user()->id;
        $data["canceled_at"] = Carbon::now();
        $item->fill($data);
        $item->save();

        DB::table("customer_points")->where("reference_id", $item->id)
            ->where("reference_type", get_class($item))->delete();

        DB::table("vouchers")
            ->where("reference_used_id", $item->id)
            ->where("reference_used_type", get_class($item))
            ->update([
                "used_at" => null
            ]);
    }

    // public function delete(PosInvoice $item)
    // {
    //     $item->delete();
    // }

    public function findBy($column, $value)
    {
        $item = PosInvoice::where($column, $value)->firstOrFail();
        return $item;
    }

    public function isAdmin()
    {
        $admin = Role::where('name', 'ADMIN')->first();
        $user = auth()->user();

        return $user->role_id == $admin->id;
    }
}
