<?php

namespace Modules\Accounting\Services;

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

class VatReturnService
{
    protected VatReturnModel $returns;
    protected CustomerInvoiceModel $invoices;
    protected CreditNoteModel $creditNotes;
    protected SupplierBillModel $bills;
    protected DebitNoteModel $debitNotes;
    protected ActivityLogModel $activity;

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

    public function create(array $input, ?int $actingUserId): array
    {
        $from = (string) ($input['period_from'] ?? '');
        $to   = (string) ($input['period_to'] ?? '');

        $dateError = $this->validateDates($from, $to);
        if ($dateError) {
            return ['success' => false, 'errors' => ['period_to' => $dateError]];
        }

        if ($this->returns->hasOverlappingPeriod($from, $to)) {
            return ['success' => false, 'errors' => ['period_to' => 'Another VAT return already covers overlapping dates.']];
        }

        $data = $this->mapInput($input);
        $data['number']     = $this->returns->nextNumber();
        $data['status']     = 'draft';
        $data['output_vat'] = 0;
        $data['input_vat']  = 0;
        $data['net_vat']    = 0;
        $data['created_by'] = $actingUserId;
        $data['updated_by'] = $actingUserId;

        if (! $this->returns->insert($data, true)) {
            return ['success' => false, 'errors' => $this->returns->errors()];
        }

        $id = (int) $this->returns->getInsertID();
        $this->activity->log('vat_returns', 'created', $actingUserId, "Created VAT return #{$id} ({$data['number']})", (string) $id);
        $this->calculate($id, $actingUserId);

        return ['success' => true, 'id' => $id];
    }

    public function update(int $id, array $input, ?int $actingUserId): array
    {
        $row = $this->returns->find($id);
        if (! $row) {
            return ['success' => false, 'errors' => ['id' => 'VAT return not found.']];
        }
        if ($row['status'] !== 'draft') {
            return ['success' => false, 'errors' => ['status' => 'Only draft VAT returns can be edited.']];
        }

        $from = (string) ($input['period_from'] ?? '');
        $to   = (string) ($input['period_to'] ?? '');

        $dateError = $this->validateDates($from, $to);
        if ($dateError) {
            return ['success' => false, 'errors' => ['period_to' => $dateError]];
        }

        if ($this->returns->hasOverlappingPeriod($from, $to, $id)) {
            return ['success' => false, 'errors' => ['period_to' => 'Another VAT return already covers overlapping dates.']];
        }

        $data = $this->mapInput($input);
        $data['updated_by'] = $actingUserId;

        if (! $this->returns->update($id, $data)) {
            return ['success' => false, 'errors' => $this->returns->errors()];
        }

        $this->activity->log('vat_returns', 'updated', $actingUserId, "Updated VAT return #{$id}", (string) $id);
        $this->calculate($id, $actingUserId);

        return ['success' => true];
    }

    public function calculate(int $id, ?int $actingUserId): array
    {
        $row = $this->returns->find($id);
        if (! $row) {
            return ['success' => false, 'error' => 'VAT return not found.'];
        }
        if ($row['status'] !== 'draft') {
            return ['success' => false, 'error' => 'Only draft VAT returns can be recalculated.'];
        }

        $from = $row['period_from'];
        $to   = $row['period_to'];

        $invoiceTax = $this->sumTax(
            $this->invoices,
            'invoice_date',
            $from,
            $to,
            ['posted', 'partially_paid', 'paid', 'overdue']
        );
        $creditTax = $this->sumTax($this->creditNotes, 'credit_date', $from, $to, ['posted']);
        $billTax   = $this->sumTax(
            $this->bills,
            'bill_date',
            $from,
            $to,
            ['posted', 'partially_paid', 'paid']
        );
        $debitTax = $this->sumTax($this->debitNotes, 'debit_date', $from, $to, ['posted']);

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

        $this->returns->update($id, [
            'output_vat' => $outputVat,
            'input_vat'  => $inputVat,
            'net_vat'    => $netVat,
            'updated_by' => $actingUserId,
        ]);

        $this->activity->log('vat_returns', 'calculated', $actingUserId, "Calculated VAT return #{$id}", (string) $id);

        return [
            'success'    => true,
            'output_vat' => $outputVat,
            'input_vat'  => $inputVat,
            'net_vat'    => $netVat,
        ];
    }

    public function submit(int $id, ?int $actingUserId): array
    {
        $row = $this->returns->find($id);
        if (! $row) {
            return ['success' => false, 'error' => 'VAT return not found.'];
        }
        if ($row['status'] !== 'draft') {
            return ['success' => false, 'error' => 'Only draft VAT returns can be submitted.'];
        }

        $calc = $this->calculate($id, $actingUserId);
        if (! $calc['success']) {
            return $calc;
        }

        $this->returns->update($id, [
            'status'       => 'submitted',
            'submitted_at' => date('Y-m-d H:i:s'),
            'updated_by'   => $actingUserId,
        ]);
        $this->activity->log('vat_returns', 'submitted', $actingUserId, "Submitted VAT return #{$id}", (string) $id);

        return ['success' => true];
    }

    public function file(int $id, ?int $actingUserId): array
    {
        $row = $this->returns->find($id);
        if (! $row) {
            return ['success' => false, 'error' => 'VAT return not found.'];
        }
        if ($row['status'] !== 'submitted') {
            return ['success' => false, 'error' => 'Only submitted VAT returns can be filed.'];
        }

        $this->returns->update($id, [
            'status'     => 'filed',
            'filed_at'   => date('Y-m-d H:i:s'),
            'updated_by' => $actingUserId,
        ]);
        $this->activity->log('vat_returns', 'filed', $actingUserId, "Filed VAT return #{$id}", (string) $id);

        return ['success' => true];
    }

    public function cancel(int $id, ?int $actingUserId): array
    {
        $row = $this->returns->find($id);
        if (! $row) {
            return ['success' => false, 'error' => 'VAT return not found.'];
        }
        if ($row['status'] !== 'draft') {
            return ['success' => false, 'error' => 'Only draft VAT returns can be cancelled.'];
        }

        $this->returns->update($id, [
            'status'     => 'cancelled',
            'updated_by' => $actingUserId,
        ]);
        $this->activity->log('vat_returns', 'cancelled', $actingUserId, "Cancelled VAT return #{$id}", (string) $id);

        return ['success' => true];
    }

    public function delete(int $id, ?int $actingUserId): array
    {
        $row = $this->returns->find($id);
        if (! $row) {
            return ['success' => false, 'error' => 'VAT return not found.'];
        }
        if ($row['status'] !== 'draft') {
            return ['success' => false, 'error' => 'Only draft VAT returns can be deleted.'];
        }

        $this->returns->delete($id);
        $this->activity->log('vat_returns', 'deleted', $actingUserId, "Deleted VAT return #{$id}", (string) $id);

        return ['success' => true];
    }

    protected function sumTax($model, string $dateField, string $from, string $to, array $statuses): float
    {
        $row = $model->selectSum('tax_amount', 'total_tax')
            ->whereIn('status', $statuses)
            ->where($dateField . ' >=', $from)
            ->where($dateField . ' <=', $to)
            ->first();

        return (float) ($row['total_tax'] ?? 0);
    }

    protected function validateDates(string $from, string $to): ?string
    {
        if ($from === '' || $to === '') {
            return 'Period from and to dates are required.';
        }
        if (strtotime($to) < strtotime($from)) {
            return 'Period end must be on or after the period start.';
        }

        return null;
    }

    protected function mapInput(array $input): array
    {
        return [
            'period_from' => $input['period_from'],
            'period_to'   => $input['period_to'],
            'notes'       => ! empty($input['notes']) ? trim((string) $input['notes']) : null,
        ];
    }
}
