<?php

namespace App\Services;

use App\Models\Purchase;
use App\Models\PurchaseItem;
use App\Models\Yarn;
use App\Models\StockLedger;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class PurchaseService
{
    public function getFiltered($request)
    {
        $query = Purchase::with(['party', 'items.yarn', 'creator']);

        if ($from = $request->from_date) {
            $query->whereDate('purchase_date', '>=', $from);
        }
        if ($to = $request->to_date) {
            $query->whereDate('purchase_date', '<=', $to);
        }
        if ($supplier = $request->supplier_id) {
            $query->where('party_id', $supplier);
        }
        if ($search = $request->search) {
            $query->where(function ($q) use ($search) {
                $q->where('voucher_no', 'like', "%{$search}%")
                  ->orWhere('invoice_no', 'like', "%{$search}%")
                  ->orWhereHas('party', fn ($q2) => $q2->where('name', 'like', "%{$search}%"));
            });
        }

        return $query->latest('purchase_date')->paginate(20)->withQueryString();
    }

    public function create(array $data): Purchase
    {
        return DB::transaction(function () use ($data) {
            $subtotal = collect($data['items'])->sum(fn ($i) => $i['quantity'] * $i['rate']);

            $purchase = Purchase::create([
                'party_id'       => $data['party_id'],
                'purchase_date'  => $data['purchase_date'],
                'invoice_no'     => $data['invoice_no'] ?? null,
                'subtotal'       => $subtotal,
                'labour_charge'  => $data['labour_charge'] ?? 0,
                'car_rent'       => $data['car_rent'] ?? 0,
                'other_charge'   => $data['other_charge'] ?? 0,
                'discount'       => $data['discount'] ?? 0,
                'grand_total'    => $subtotal
                                    + ($data['labour_charge'] ?? 0)
                                    + ($data['car_rent'] ?? 0)
                                    + ($data['other_charge'] ?? 0)
                                    - ($data['discount'] ?? 0),
                'paid_amount'    => $data['paid_amount'] ?? 0,
                'due_amount'     => 0, // will be computed in model
                'remarks'        => $data['remarks'] ?? null,
                'created_by'     => auth()->id(),
            ]);

            foreach ($data['items'] as $item) {
                PurchaseItem::create([
                    'purchase_id' => $purchase->id,
                    'yarn_id'     => $item['yarn_id'],
                    'lot_no'      => $item['lot_no'] ?? null,
                    'bags'        => $item['bags'] ?? 0,
                    'quantity'    => $item['quantity'],
                    'unit'        => $item['unit'] ?? 'Kg',
                    'rate'        => $item['rate'],
                    'amount'      => $item['quantity'] * $item['rate'],
                ]);
            }

            // Stock ledger entries
            $runningBalance = $this->getCurrentBalance($purchase->id);
            foreach ($purchase->items()->with('yarn')->get() as $item) {
                StockLedger::create([
                    'transaction_date' => $purchase->purchase_date,
                    'yarn_id'          => $item->yarn_id,
                    'lot_no'           => $item->lot_no,
                    'type'             => 'purchase',
                    'reference_type'   => Purchase::class,
                    'reference_id'     => $purchase->id,
                    'voucher_no'       => $purchase->voucher_no,
                    'in_qty'           => $item->quantity,
                    'out_qty'          => 0,
                    'rate'             => $item->rate,
                    'value'            => $item->amount,
                    'remarks'          => 'Purchase from ' . $purchase->party->name,
                ]);
            }

            // If paid amount provided, create a payment voucher
            if (($data['paid_amount'] ?? 0) > 0 && ! empty($data['account_id'])) {
                \App\Models\Voucher::create([
                    'type'         => 'payment',
                    'voucher_date' => $purchase->purchase_date,
                    'account_id'   => $data['account_id'],
                    'party_id'     => $purchase->party_id,
                    'amount'       => $data['paid_amount'],
                    'mode'         => $data['payment_mode'] ?? 'cash',
                    'description'  => 'Payment for ' . $purchase->voucher_no,
                    'created_by'   => auth()->id(),
                ]);
            }

            return $purchase->fresh(['items.yarn', 'party']);
        });
    }

    public function update(Purchase $purchase, array $data): Purchase
    {
        return DB::transaction(function () use ($purchase, $data) {
            // Reverse old stock ledger entries
            StockLedger::where('reference_type', Purchase::class)
                ->where('reference_id', $purchase->id)
                ->delete();

            $subtotal = collect($data['items'])->sum(fn ($i) => $i['quantity'] * $i['rate']);

            $purchase->update([
                'party_id'      => $data['party_id'],
                'purchase_date' => $data['purchase_date'],
                'invoice_no'    => $data['invoice_no'] ?? null,
                'subtotal'      => $subtotal,
                'labour_charge' => $data['labour_charge'] ?? 0,
                'car_rent'      => $data['car_rent'] ?? 0,
                'other_charge'  => $data['other_charge'] ?? 0,
                'discount'      => $data['discount'] ?? 0,
                'grand_total'   => $subtotal
                                   + ($data['labour_charge'] ?? 0)
                                   + ($data['car_rent'] ?? 0)
                                   + ($data['other_charge'] ?? 0)
                                   - ($data['discount'] ?? 0),
                'paid_amount'   => $data['paid_amount'] ?? 0,
                'remarks'       => $data['remarks'] ?? null,
            ]);

            $purchase->items()->delete();
            foreach ($data['items'] as $item) {
                PurchaseItem::create([
                    'purchase_id' => $purchase->id,
                    'yarn_id'     => $item['yarn_id'],
                    'lot_no'      => $item['lot_no'] ?? null,
                    'bags'        => $item['bags'] ?? 0,
                    'quantity'    => $item['quantity'],
                    'unit'        => $item['unit'] ?? 'Kg',
                    'rate'        => $item['rate'],
                    'amount'      => $item['quantity'] * $item['rate'],
                ]);
            }

            // Recreate stock ledger
            foreach ($purchase->items()->with('yarn')->get() as $item) {
                StockLedger::create([
                    'transaction_date' => $purchase->purchase_date,
                    'yarn_id'          => $item->yarn_id,
                    'lot_no'           => $item->lot_no,
                    'type'             => 'purchase',
                    'reference_type'   => Purchase::class,
                    'reference_id'     => $purchase->id,
                    'voucher_no'       => $purchase->voucher_no,
                    'in_qty'           => $item->quantity,
                    'out_qty'          => 0,
                    'rate'             => $item->rate,
                    'value'            => $item->amount,
                ]);
            }

            return $purchase->fresh(['items.yarn', 'party']);
        });
    }

    public function delete(Purchase $purchase): bool
    {
        return DB::transaction(function () use ($purchase) {
            StockLedger::where('reference_type', Purchase::class)
                ->where('reference_id', $purchase->id)
                ->delete();
            return $purchase->delete();
        });
    }

    public function getTotals($purchases): array
    {
        return [
            'count'       => $purchases->count(),
            'subtotal'    => $purchases->sum('subtotal'),
            'grand_total' => $purchases->sum('grand_total'),
            'paid'        => $purchases->sum('paid_amount'),
            'due'         => $purchases->sum('due_amount'),
        ];
    }

    private function getCurrentBalance($purchaseId): float
    {
        return 0;
    }
}
