<?php
declare(strict_types=1);

namespace App\Controllers\Web;

use App\Middleware\AuthSession;
use App\Middleware\TenantScope;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\ProductService;
use App\Models\Tenant;
use App\Lib\Pdf;
use Core\Controller;
use Core\Session;
use Core\Tool;

final class InvoicesController extends Controller
{
    public function middleware(): array
    {
        return [AuthSession::class, TenantScope::class];
    }

    private function tenantId(): int
    {
        return (int) ($_SESSION['tenant_id'] ?? 0);
    }

    private function userId(): int
    {
        return (int) (Session::user()['user_id'] ?? 0);
    }

    public function index(): void
    {
        // Mark overdue before displaying
        Invoice::markOverdue($this->tenantId());

        $filters = [
            'status'      => $_GET['status']      ?? null,
            'customer_id' => $_GET['customer_id'] ?? null,
            'overdue'     => $_GET['overdue']     ?? null,
            'q'           => $_GET['q']           ?? null,
        ];
        $sort      = $_GET['sort'] ?? 'newest';
        $tenantId  = $this->tenantId();
        $active    = array_filter($filters);
        $perPage   = (int) ($_GET['per_page'] ?? 25);
        $perPage   = max(10, min($perPage, 100));
        $page      = max(1, (int) ($_GET['page'] ?? 1));
        $total     = Invoice::countForTenant($tenantId, $active);
        $lastPage  = max(1, (int) ceil($total / $perPage));
        if ($page > $lastPage) $page = $lastPage;

        $counts = Invoice::countsForTenant($tenantId);

        $this->render('invoices/index', [
            'invoices'  => Invoice::listForTenant($tenantId, $active, $perPage, ($page - 1) * $perPage, $sort),
            'sort'      => $sort,
            'filters'   => $filters,
            'counts'    => $counts,
            'customers' => Customer::optionsForTenant($tenantId),
            'flash'     => Session::flash('invoice_msg'),
            'pager'     => [
                'total'    => $total,
                'per_page' => $perPage,
                'page'     => $page,
                'last'     => $lastPage,
                'from'     => $total > 0 ? ($page - 1) * $perPage + 1 : 0,
                'to'       => min($page * $perPage, $total),
            ],
        ]);
    }

    public function create(): void
    {
        $tenantId = $this->tenantId();
        $this->render('invoices/form', [
            'invoice'   => null,
            'items'     => [],
            'customers' => Customer::optionsForTenant($tenantId),
            'products'  => ProductService::optionsForTenant($tenantId),
            'csrf'      => Tool::generateCsrf(),
        ]);
    }

    public function edit(int $id): void
    {
        $tenantId = $this->tenantId();
        $invoice = Invoice::findInTenant($tenantId, $id);
        if (!$invoice) { Session::flash('invoice_msg', 'Invoice not found.'); redirect('/Invoices'); }
        $this->render('invoices/form', [
            'invoice'   => $invoice,
            'items'     => Invoice::itemsFor($id),
            'customers' => Customer::optionsForTenant($tenantId),
            'products'  => ProductService::optionsForTenant($tenantId),
            'csrf'      => Tool::generateCsrf(),
        ]);
    }

    public function save(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/Invoices');
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) {
            Session::flash('invoice_msg', 'Session expired.'); redirect('/Invoices');
        }
        $tenantId = $this->tenantId();
        $userId   = $this->userId();
        $id       = (int) ($_POST['invoice_id'] ?? 0);

        if (empty($_POST['customer_id'])) {
            Session::flash('invoice_msg', 'Please select a customer.');
            redirect($id > 0 ? "/Invoices/edit/{$id}" : '/Invoices/create');
        }

        // Calculate totals from line items
        $subtotal  = 0;
        $taxTotal  = 0;
        $lineItems = [];
        if (!empty($_POST['item_description']) && is_array($_POST['item_description'])) {
            for ($i = 0, $n = count($_POST['item_description']); $i < $n; $i++) {
                $desc    = trim((string) ($_POST['item_description'][$i] ?? ''));
                $itemId  = !empty($_POST['item_product_id'][$i]) ? (int) $_POST['item_product_id'][$i] : null;
                $qty     = (float) ($_POST['item_qty'][$i] ?? 1);
                $price   = (float) ($_POST['item_price'][$i] ?? 0);
                $discPct = (float) ($_POST['item_discount'][$i] ?? 0);
                $line    = $qty * $price * (1 - $discPct / 100);
                $subtotal += $line;

                // Tax from product/service lookup
                if ($itemId) {
                    $ps = ProductService::findInTenant($tenantId, (int) $itemId);
                    if ($ps && !empty($ps['taxable']) && $ps['unit_price'] > 0) {
                        $itemTaxRate = (float) ($ps['tax_rate'] ?? 0);
                        $taxTotal += $line * ($itemTaxRate / 100);
                    }
                }

                $lineItems[] = [
                    'item_id'          => $itemId,
                    'description'      => $desc !== '' ? $desc : 'Item ' . ($i + 1),
                    'quantity'         => $qty,
                    'unit_price'       => $price,
                    'discount_percent' => $discPct,
                    'line_total'       => round($line, 2),
                    'sort_order'       => $i,
                ];
            }
        }

        $discountPct = (float) ($_POST['discount_percent'] ?? 0);
        $discountAmt = $subtotal * ($discountPct / 100);
        $grandTotal  = round($subtotal - $discountAmt + $taxTotal, 2);

        $data = [
            'customer_id'      => (int) $_POST['customer_id'],
            'status'           => $_POST['status'] ?? 'draft',
            'subtotal'         => round($subtotal, 2),
            'discount_percent' => $discountPct,
            'discount_amount'  => round($discountAmt, 2),
            'tax_total'        => round($taxTotal, 2),
            'grand_total'      => $grandTotal,
            'due_date'         => $_POST['due_date'] ?? null,
            'notes'            => $_POST['notes'] ?? null,
            'terms'            => $_POST['terms'] ?? null,
            'created_by'       => $userId,
        ];

        if ($id > 0) {
            Invoice::update($tenantId, $id, $data);
            Invoice::deleteAllItems($id);
            foreach ($lineItems as $li) {
                Invoice::addItem($id, $li);
            }
            Invoice::recalculateBalance($id);
            Session::flash('invoice_msg', 'Invoice updated.');
        } else {
            $id = Invoice::create($tenantId, $data);
            foreach ($lineItems as $li) {
                Invoice::addItem($id, $li);
            }
            Invoice::recalculateBalance($id);
            Session::flash('invoice_msg', 'Invoice created.');
        }
        redirect("/Invoices/show/{$id}");
    }

    public function show(int $id): void
    {
        $tenantId = $this->tenantId();
        $invoice = Invoice::findInTenant($tenantId, $id);
        if (!$invoice) { Session::flash('invoice_msg', 'Not found.'); redirect('/Invoices'); }
        $this->render('invoices/show', [
            'invoice'  => $invoice,
            'items'    => Invoice::itemsFor($id),
            'payments' => Invoice::paymentsFor($id),
            'flash'    => Session::flash('invoice_msg'),
            'csrf'     => Tool::generateCsrf(),
        ]);
    }

    /**
     * Record a payment against an invoice.
     * POST /Invoices/addPayment/{id}
     */
    public function addPayment(int $id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect("/Invoices/show/{$id}");
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect("/Invoices/show/{$id}");
        $tenantId = $this->tenantId();
        $userId   = $this->userId();
        $amount   = (float) ($_POST['amount'] ?? 0);

        if ($amount <= 0) {
            Session::flash('invoice_msg', 'Payment amount must be greater than zero.');
            redirect("/Invoices/show/{$id}");
        }

        Invoice::addPayment($tenantId, $id, $_POST, $userId);
        Session::flash('invoice_msg', 'Payment recorded successfully.');
        redirect("/Invoices/show/{$id}");
    }

    /**
     * Send a payment reminder / follow-up.
     * POST /Invoices/sendReminder/{id}
     */
    public function sendReminder(int $id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect("/Invoices/show/{$id}");
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect("/Invoices/show/{$id}");
        $tenantId = $this->tenantId();
        $invoice = Invoice::findInTenant($tenantId, $id);
        if (!$invoice) { Session::flash('invoice_msg', 'Invoice not found.'); redirect('/Invoices'); }

        // Log the follow-up as a note in the invoice (append)
        $note = "\n[Follow-up " . date('Y-m-d H:i') . '] Payment reminder sent for balance ' . number_format($invoice['balance_due'], 2);
        Invoice::update($tenantId, $id, [
            'notes' => ($invoice['notes'] ?? '') . $note,
        ]);

        Session::flash('invoice_msg', 'Payment reminder sent.');
        redirect("/Invoices/show/{$id}");
    }

    public function delete(int $id): void
    {
        Invoice::softDelete($this->tenantId(), $id);
        Session::flash('invoice_msg', 'Invoice deleted.');
        redirect('/Invoices');
    }

    public function pdf(int $id): void
    {
        $tenantId = $this->tenantId();
        $invoice = Invoice::findInTenant($tenantId, $id);
        if (!$invoice) { http_response_code(404); echo 'Not found'; return; }
        $tenant   = Tenant::findById($tenantId);
        $items    = Invoice::itemsFor($id);
        $payments = Invoice::paymentsFor($id);
        ob_start();
        require ROOT . '/app/Views/pdf/invoice.php';
        $html = ob_get_clean();
        Pdf::stream($html, 'Invoice-' . ($invoice['invoice_number'] ?: $id));
    }
}