<?php

namespace Modules\Accounting\Services;

use App\Models\CreditNoteModel;
use App\Models\CustomerInvoiceModel;
use App\Models\DebitNoteModel;
use App\Models\SupplierBillModel;

class TaxReportService
{
    protected CustomerInvoiceModel $invoices;
    protected CreditNoteModel $creditNotes;
    protected SupplierBillModel $bills;
    protected DebitNoteModel $debitNotes;

    public function __construct()
    {
        $this->invoices    = new CustomerInvoiceModel();
        $this->creditNotes = new CreditNoteModel();
        $this->bills       = new SupplierBillModel();
        $this->debitNotes  = new DebitNoteModel();
    }

    /**
     * @return array{
     *   from: string,
     *   to: string,
     *   output_vat: float,
     *   input_vat: float,
     *   net: float,
     *   breakdown: list<array{type: string, label: string, direction: string, path: string, count: int, tax_total: float, rows: list<array>}>
     * }
     */
    public function summary(string $from, string $to): array
    {
        $invoices = $this->fetchDocs(
            $this->invoices,
            'invoice_date',
            $from,
            $to,
            ['posted', 'partially_paid', 'paid', 'overdue'],
            'companies',
            'companies.id = customer_invoices.company_id',
            'companies.name as party_name',
            'customer_invoices'
        );
        $credits = $this->fetchDocs(
            $this->creditNotes,
            'credit_date',
            $from,
            $to,
            ['posted'],
            'companies',
            'companies.id = credit_notes.company_id',
            'companies.name as party_name',
            'credit_notes'
        );
        $bills = $this->fetchDocs(
            $this->bills,
            'bill_date',
            $from,
            $to,
            ['posted', 'partially_paid', 'paid'],
            'suppliers',
            'suppliers.id = supplier_bills.supplier_id',
            'suppliers.name as party_name',
            'supplier_bills'
        );
        $debits = $this->fetchDocs(
            $this->debitNotes,
            'debit_date',
            $from,
            $to,
            ['posted'],
            'suppliers',
            'suppliers.id = debit_notes.supplier_id',
            'suppliers.name as party_name',
            'debit_notes'
        );

        $invoiceTax = $this->totalTax($invoices);
        $creditTax  = $this->totalTax($credits);
        $billTax    = $this->totalTax($bills);
        $debitTax   = $this->totalTax($debits);

        $outputVat = round($invoiceTax - $creditTax, 2);
        $inputVat  = round($billTax - $debitTax, 2);
        $net       = round($outputVat - $inputVat, 2);

        return [
            'from'       => $from,
            'to'         => $to,
            'output_vat' => $outputVat,
            'input_vat'  => $inputVat,
            'net'        => $net,
            'breakdown'  => [
                [
                    'type'      => 'customer_invoices',
                    'label'     => 'Customer Invoices',
                    'direction' => 'output',
                    'path'      => 'accounting/customer-invoices',
                    'count'     => count($invoices),
                    'tax_total' => $invoiceTax,
                    'rows'      => $invoices,
                ],
                [
                    'type'      => 'credit_notes',
                    'label'     => 'Credit Notes',
                    'direction' => 'output_reduction',
                    'path'      => 'accounting/credit-notes',
                    'count'     => count($credits),
                    'tax_total' => $creditTax,
                    'rows'      => $credits,
                ],
                [
                    'type'      => 'supplier_bills',
                    'label'     => 'Supplier Bills',
                    'direction' => 'input',
                    'path'      => 'accounting/supplier-bills',
                    'count'     => count($bills),
                    'tax_total' => $billTax,
                    'rows'      => $bills,
                ],
                [
                    'type'      => 'debit_notes',
                    'label'     => 'Debit Notes',
                    'direction' => 'input_reduction',
                    'path'      => 'accounting/debit-notes',
                    'count'     => count($debits),
                    'tax_total' => $debitTax,
                    'rows'      => $debits,
                ],
            ],
        ];
    }

    protected function fetchDocs(
        $model,
        string $dateField,
        string $from,
        string $to,
        array $statuses,
        string $partyTable,
        string $joinOn,
        string $partySelect,
        string $alias
    ): array {
        return $model->select("{$alias}.id, {$alias}.number, {$alias}.{$dateField} as doc_date, {$alias}.status, {$alias}.tax_amount, {$alias}.total, {$partySelect}")
            ->join($partyTable, $joinOn, 'left')
            ->whereIn("{$alias}.status", $statuses)
            ->where("{$alias}.{$dateField} >=", $from)
            ->where("{$alias}.{$dateField} <=", $to)
            ->orderBy("{$alias}.{$dateField}", 'ASC')
            ->orderBy("{$alias}.id", 'ASC')
            ->findAll();
    }

    protected function totalTax(array $rows): float
    {
        $sum = 0.0;
        foreach ($rows as $row) {
            $sum += (float) ($row['tax_amount'] ?? 0);
        }

        return round($sum, 2);
    }
}
