<?php

namespace App\Services;

use App\Models\Payment;
use App\Models\PurchaseBill;
use App\Models\Vendor;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class VendorStatementBuilder
{
    public function __construct(
        private Vendor $vendor,
        private Carbon $from,
        private Carbon $to,
        private string $txnFilter = 'all',
    ) {}

    public static function for(Vendor $vendor, string $from, string $to, string $txnFilter = 'all'): self
    {
        return new self(
            $vendor,
            Carbon::parse($from)->startOfDay(),
            Carbon::parse($to)->endOfDay(),
            $txnFilter,
        );
    }

    public function build(): array
    {
        $openingSigned = $this->vendor->signedOpeningBalance();
        $priorBilled = $this->billQuery()
            ->whereDate('bill_date', '<', $this->from->toDateString())
            ->sum(DB::raw('grand_total'));
        $priorPaid = $this->paymentQuery()
            ->whereDate('payment_date', '<', $this->from->toDateString())
            ->sum('amount');

        $carryForward = round($openingSigned + $priorBilled - $priorPaid, 2);

        $rows = collect();
        $rows->push([
            'date' => $this->from->copy(),
            'transaction' => 'Carry Forward',
            'details' => '',
            'amount' => 0,
            'payment' => 0,
            'balance' => $carryForward,
            'is_carry_forward' => true,
        ]);

        $transactions = $this->periodTransactions();
        $balance = $carryForward;

        foreach ($transactions as $txn) {
            $balance = round($balance + $txn['amount'] - $txn['payment'], 2);
            $rows->push(array_merge($txn, ['balance' => $balance]));
        }

        return [
            'from' => $this->from,
            'to' => $this->to,
            'carry_forward' => $carryForward,
            'billed_amount' => round($transactions->sum('amount'), 2),
            'amount_paid' => round($transactions->sum('payment'), 2),
            'balance_due' => $balance,
            'rows' => $rows,
        ];
    }

    private function periodTransactions(): Collection
    {
        $items = collect();

        if (in_array($this->txnFilter, ['all', 'bills'], true)) {
            $bills = $this->billQuery()
                ->whereBetween('bill_date', [$this->from->toDateString(), $this->to->toDateString()])
                ->orderBy('bill_date')
                ->orderBy('id')
                ->get();

            foreach ($bills as $bill) {
                $items->push([
                    'date' => $bill->bill_date,
                    'transaction' => 'Purchase Bill',
                    'details' => $bill->bill_number,
                    'amount' => (float) $bill->grand_total,
                    'payment' => 0,
                    'is_carry_forward' => false,
                    'sort_key' => $bill->bill_date->format('Y-m-d').'-bill-'.$bill->id,
                ]);
            }
        }

        if (in_array($this->txnFilter, ['all', 'payments'], true)) {
            $payments = $this->paymentQuery()
                ->whereBetween('payment_date', [$this->from->toDateString(), $this->to->toDateString()])
                ->orderBy('payment_date')
                ->orderBy('id')
                ->get();

            foreach ($payments as $pay) {
                $items->push([
                    'date' => $pay->payment_date,
                    'transaction' => 'Payment Made',
                    'details' => $pay->payment_number ?: ($pay->transaction_ref ?: 'Payment'),
                    'amount' => 0,
                    'payment' => (float) $pay->amount,
                    'is_carry_forward' => false,
                    'sort_key' => $pay->payment_date->format('Y-m-d').'-pay-'.$pay->id,
                ]);
            }
        }

        return $items->sortBy('sort_key')->values();
    }

    private function billQuery()
    {
        return PurchaseBill::query()
            ->where('vendor_id', $this->vendor->id)
            ->where('company_id', $this->vendor->company_id)
            ->whereNotIn('status', ['cancelled', 'draft']);
    }

    private function paymentQuery()
    {
        return Payment::query()
            ->where('vendor_id', $this->vendor->id)
            ->where('company_id', $this->vendor->company_id)
            ->where('payment_type', 'made');
    }
}
