<?php

namespace App\Services;

use App\Models\Consignment;
use App\Models\DeliveryNote;
use App\Models\GlobalSetting;
use App\Models\Invoice;
use App\Models\Payment;

class InvoiceService
{
    // ── Generate invoice ──────────────────────────────────────────
    // Always recalculates from scratch.
    // Caller is responsible for deleting any existing invoice first
    // (HasDocumentActions does this before calling this method).

    public function generateForConsignment(Consignment $consignment): Invoice
    {
        // Load fresh — don't trust cached relationship
        $consignment->load('shipment');

        $shipment = $consignment->shipment;
        $type     = $shipment?->type ?? 'air';
        $route    = $shipment
            ? ($shipment->origin . $shipment->destination)
            : null;

        $extra  = (float) ($consignment->extra_charge_usd ?? 0);
        $exRate = GlobalSetting::exchangeRate();

        // ── Calculate fresh from global/route rates ───────────────
        if ($type === 'air') {
            $kgs       = (float) ($consignment->actual_weight_kgs ?? 0);
            $rate      = GlobalSetting::airRateForRoute($route);
            $amountUsd = round(($kgs * $rate) + $extra, 2);
            $amountKes = round($amountUsd * $exRate, 2);

        } else {
            $cbm       = (float) ($consignment->cbm ?? 0);
            $rate      = GlobalSetting::seaRateForRoute($route);
            $amountUsd = round($extra, 2);
            $amountKes = round(($cbm * $rate) + ($extra * $exRate), 2);
        }

        // Patch the consignment so its stored amounts are correct too
        $consignment->updateQuietly([
            'amount_usd' => $amountUsd,
            'amount_kes' => $amountKes,
        ]);

        // Forget cached invoice relation before creating new one
        $consignment->unsetRelation('invoice');

        $invoiceNumber = $this->nextInvoiceNumber();

        return Invoice::create([
            'consignment_id'   => $consignment->id,
            'invoice_number'   => $invoiceNumber,
            'amount_usd'       => $amountUsd,
            'amount_kes'       => $amountKes,
            'extra_charge_usd' => $extra,
            'amount_paid_kes'  => 0,
            'outstanding_kes'  => $amountKes,
            'payment_status'   => 'unpaid',
            'due_date'         => now()->addDays(14),
            'created_by'       => auth()->id(),
        ]);
    }

    // ── Recalculate balance after payment ─────────────────────────

    public function recalculateInvoiceBalance(Invoice $invoice): void
    {
        $totalPaid   = Payment::where('invoice_id', $invoice->id)->sum('amount_kes');
        $outstanding = max(0, $invoice->amount_kes - $totalPaid);

        $status = match (true) {
            $totalPaid <= 0   => 'unpaid',
            $outstanding <= 0 => 'paid',
            default           => 'partial',
        };

        $invoice->update([
            'amount_paid_kes' => $totalPaid,
            'outstanding_kes' => $outstanding,
            'payment_status'  => $status,
        ]);

        if ($status === 'paid') {
            $invoice->consignment->update(['status' => 'ready_for_pickup']);

            \App\Models\ConsignmentStatusHistory::create([
                'consignment_id' => $invoice->consignment_id,
                'status'         => 'ready_for_pickup',
                'note'           => 'Fully paid — cleared for pickup',
                'changed_by'     => auth()->id(),
            ]);
        }
    }

    // ── Generate delivery note ────────────────────────────────────

    public function generateDeliveryNote(Consignment $consignment): DeliveryNote
    {
        // Caller deletes existing first — just create fresh
        $consignment->unsetRelation('deliveryNote');

        return DeliveryNote::create([
            'consignment_id' => $consignment->id,
            'dn_number'      => $this->nextDnNumber(),
            'issued_at'      => now(),
            'created_by'     => auth()->id(),
        ]);
    }

    // ── Number generators ─────────────────────────────────────────

    private function nextInvoiceNumber(): string
    {
        $last = Invoice::orderByDesc('id')->value('invoice_number');
        $next = $last
            ? (int) substr($last, strrpos($last, '-') + 1) + 1
            : 1;

        return 'INV-' . date('Y') . '-' . str_pad($next, 5, '0', STR_PAD_LEFT);
    }

    private function nextDnNumber(): string
    {
        $last = DeliveryNote::orderByDesc('id')->value('dn_number');
        $next = $last
            ? (int) substr($last, strrpos($last, '-') + 1) + 1
            : 1;

        return 'DN-' . date('Y') . '-' . str_pad($next, 5, '0', STR_PAD_LEFT);
    }
}