<?php

namespace App\Services;

use App\Models\InvoiceModel;
use App\Models\OrderModel;

/** Creates one invoice per order and keeps its balance in sync with payments. */
class InvoiceService
{
    public function syncForOrder(array $order): array
    {
        $invoiceModel = new InvoiceModel();
        $invoice = $invoiceModel->where('id_order', $order['id_order'])->first();

        $total = (float) ($order['total'] ?? 0);
        $remaining = max(0, (float) ($order['remaining_amount'] ?? $total));
        $status = $remaining <= 0 && $total > 0
            ? 'paid'
            : ($remaining < $total ? 'partial' : 'unpaid');

        if ($invoice) {
            $invoice = $this->ensureAccessToken($invoice);

            $changes = [
                'total_tagihan' => $total,
                'sisa_tagihan'  => $remaining,
                'status'        => $status,
            ];
            $invoiceModel->update($invoice['id_invoice'], $changes);

            return array_merge($invoice, $changes);
        }

        $invoice = [
            'id_order'            => $order['id_order'],
            'nomor_invoice'       => $this->nextNumber(),
            'access_token'        => $this->generateAccessToken(),
            'tanggal_jatuh_tempo' => date('Y-m-d', strtotime('+7 days')),
            'total_tagihan'       => $total,
            'sisa_tagihan'        => $remaining,
            'status'              => $status,
        ];
        $invoiceModel->insert($invoice);
        $invoice['id_invoice'] = $invoiceModel->insertID();

        return $invoice;
    }

    public function findByOrder(int $orderId): ?array
    {
        return (new InvoiceModel())->where('id_order', $orderId)->first();
    }

    /** Give legacy invoices a public URL token before they are sent to customers. */
    public function ensureAccessToken(array $invoice): array
    {
        if (! empty($invoice['access_token'])) {
            return $invoice;
        }

        $accessToken = $this->generateAccessToken();
        (new InvoiceModel())->update($invoice['id_invoice'], [
            'access_token' => $accessToken,
        ]);

        return array_merge($invoice, ['access_token' => $accessToken]);
    }

    private function generateAccessToken(): string
    {
        return bin2hex(random_bytes(32));
    }

    private function nextNumber(): string
    {
        $db = \Config\Database::connect();
        $setting = $db->table('system_settings')->get()->getRowArray();
        $prefix = preg_replace('/[^A-Za-z0-9-]/', '', (string) ($setting['invoice_prefix'] ?? 'INV')) ?: 'INV';
        $date = date('Ymd');
        $base = $prefix . '-' . $date . '-';
        $last = (new InvoiceModel())
            ->like('nomor_invoice', $base, 'after')
            ->orderBy('id_invoice', 'DESC')
            ->first();
        $number = $last ? ((int) substr((string) $last['nomor_invoice'], -4)) + 1 : 1;

        return $base . str_pad((string) $number, 4, '0', STR_PAD_LEFT);
    }
}
