<?php

namespace App\Services;

use App\Models\Party;
use App\Models\Purchase;
use App\Models\Sale;
use App\Models\Voucher;
use App\Models\StockLedger;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class LedgerService
{
    /**
     * Get supplier ledger entries with running balance
     */
    public function getSupplierLedger($request)
    {
        $party = Party::findOrFail($request->party_id);
        $from  = $request->from_date ?? Carbon::now()->startOfMonth()->toDateString();
        $to    = $request->to_date ?? Carbon::today()->toDateString();

        // Opening balance (before from date)
        $purchasesBefore = Purchase::where('party_id', $party->id)
            ->whereDate('purchase_date', '<', $from)
            ->sum('grand_total');

        $paymentsBefore = Voucher::where('party_id', $party->id)
            ->where('type', 'payment')
            ->whereDate('voucher_date', '<', $from)
            ->sum('amount');

        $openingBalance = $purchasesBefore - $paymentsBefore;

        // Transactions in range
        $purchases = Purchase::where('party_id', $party->id)
            ->whereBetween('purchase_date', [$from, $to])
            ->get()
            ->map(fn ($p) => (object) [
                'date'         => $p->purchase_date,
                'voucher_no'   => $p->voucher_no,
                'description'   => 'Purchase',
                'debit'        => $p->grand_total,
                'credit'       => 0,
                'remarks'      => $p->remarks,
                'reference'    => 'purchase',
            ]);

        $payments = Voucher::where('party_id', $party->id)
            ->where('type', 'payment')
            ->whereBetween('voucher_date', [$from, $to])
            ->get()
            ->map(fn ($v) => (object) [
                'date'         => $v->voucher_date,
                'voucher_no'   => $v->voucher_no,
                'description'   => $v->description ?? 'Payment',
                'debit'        => 0,
                'credit'       => $v->amount,
                'remarks'      => $v->mode ? "Via {$v->mode}" : null,
                'reference'    => 'voucher',
            ]);

        // Merge and sort
        $entries = $purchases->merge($payments)->sortBy('date')->values();

        // Build running balance
        $running = $openingBalance;
        $entries = $entries->map(function ($entry) use (&$running) {
            $running += $entry->debit - $entry->credit;
            return (object) [
                'date'       => $entry->date,
                'voucher_no' => $entry->voucher_no,
                'description'=> $entry->description,
                'debit'      => $entry->debit,
                'credit'     => $entry->credit,
                'balance'    => $running,
                'remarks'    => $entry->remarks,
                'reference'  => $entry->reference,
            ];
        });

        // Totals
        $totals = [
            'total_purchase' => $entries->sum('debit'),
            'total_paid'     => $entries->sum('credit'),
            'current_due'    => $running,
            'opening'        => $openingBalance,
        ];

        return [
            'party'   => $party,
            'from'    => $from,
            'to'      => $to,
            'entries' => $entries,
            'totals'  => $totals,
        ];
    }

    /**
     * Get customer ledger entries with running balance
     */
    public function getCustomerLedger($request)
    {
        $party = Party::findOrFail($request->party_id);
        $from  = $request->from_date ?? Carbon::now()->startOfMonth()->toDateString();
        $to    = $request->to_date ?? Carbon::today()->toDateString();

        $salesBefore = Sale::where('party_id', $party->id)
            ->whereDate('sales_date', '<', $from)
            ->sum('grand_total');

        $receiptsBefore = Voucher::where('party_id', $party->id)
            ->where('type', 'receipt')
            ->whereDate('voucher_date', '<', $from)
            ->sum('amount');

        $openingBalance = $salesBefore - $receiptsBefore;

        $sales = Sale::where('party_id', $party->id)
            ->whereBetween('sales_date', [$from, $to])
            ->get()
            ->map(fn ($s) => (object) [
                'date'       => $s->sales_date,
                'voucher_no' => $s->voucher_no,
                'description'=> 'Sale',
                'debit'      => $s->grand_total,
                'credit'     => 0,
                'remarks'    => $s->remarks,
                'reference'  => 'sale',
            ]);

        $receipts = Voucher::where('party_id', $party->id)
            ->where('type', 'receipt')
            ->whereBetween('voucher_date', [$from, $to])
            ->get()
            ->map(fn ($v) => (object) [
                'date'       => $v->voucher_date,
                'voucher_no' => $v->voucher_no,
                'description'=> $v->description ?? 'Receipt',
                'debit'      => 0,
                'credit'     => $v->amount,
                'remarks'    => $v->mode ? "Via {$v->mode}" : null,
                'reference'  => 'voucher',
            ]);

        $entries = $sales->merge($receipts)->sortBy('date')->values();

        $running = $openingBalance;
        $entries = $entries->map(function ($entry) use (&$running) {
            $running += $entry->debit - $entry->credit;
            return (object) [
                'date'       => $entry->date,
                'voucher_no' => $entry->voucher_no,
                'description'=> $entry->description,
                'debit'      => $entry->debit,
                'credit'     => $entry->credit,
                'balance'    => $running,
                'remarks'    => $entry->remarks,
                'reference'  => $entry->reference,
            ];
        });

        $totals = [
            'total_sales'   => $entries->sum('debit'),
            'total_received'=> $entries->sum('credit'),
            'current_due'   => $running,
            'opening'       => $openingBalance,
        ];

        return [
            'party'   => $party,
            'from'    => $from,
            'to'      => $to,
            'entries' => $entries,
            'totals'  => $totals,
        ];
    }

    public function getPartySummaries(): array
    {
        return [
            'suppliers' => Party::suppliers()->active()->withSum('purchases', 'grand_total')->get(),
            'customers' => Party::customers()->active()->withSum('sales', 'grand_total')->get(),
        ];
    }
}
