<?php

namespace App\Controllers\Admin;

use App\Models\Control\InvoiceLineItemModel;
use App\Models\Control\InvoiceModel;
use App\Models\Control\SubscriptionModel;
use App\Models\Control\TenantRecordModel;
use App\Models\Control\TransactionModel;
use RuntimeException;

/**
 * Platform billing a hotel for their PMS subscription — see
 * CreateInvoicesTable. Structurally mirrors App\Controllers\
 * InvoiceController (a tenant's folio invoices to ITS guests) one layer
 * up: generate → view/PDF → collect payment → mark paid.
 */
class InvoiceController extends BaseAdminController
{
    public function index()
    {
        return view('admin/invoices/index', [
            'pageTitle' => 'Billing & Invoices',
            'activeNav' => 'invoices',
            'invoices'  => (new InvoiceModel())->allWithTenant(),
        ]);
    }

    public function new(int $tenantId)
    {
        $tenant = (new TenantRecordModel())->find($tenantId);

        if ($tenant === null) {
            return redirect()->to('/admin/tenants')->with('error', 'Hotel not found.');
        }

        $subscription = (new SubscriptionModel())->currentWithPlanFor($tenantId);

        return view('admin/invoices/new', [
            'pageTitle'    => 'New Invoice',
            'activeNav'    => 'invoices',
            'tenant'       => $tenant,
            'subscription' => $subscription,
        ]);
    }

    public function create(int $tenantId)
    {
        $tenant = (new TenantRecordModel())->find($tenantId);

        if ($tenant === null) {
            return redirect()->to('/admin/tenants')->with('error', 'Hotel not found.');
        }

        $descriptions = $this->request->getPost('description') ?? [];
        $amounts      = $this->request->getPost('amount') ?? [];
        $dueDate      = $this->request->getPost('due_date');

        $lines = [];
        $total = '0.00';

        foreach ($descriptions as $i => $desc) {
            $desc = trim((string) $desc);
            $amt  = $amounts[$i] ?? null;

            if ($desc === '' || $amt === null || $amt === '') {
                continue;
            }

            $amt     = parse_money($amt);
            $lines[] = ['description' => $desc, 'amount' => $amt];
            $total   = function_exists('bcadd') ? bcadd($total, $amt, 2) : number_format((float) $total + (float) $amt, 2, '.', '');
        }

        if ($lines === []) {
            return redirect()->back()->withInput()->with('error', 'Add at least one line item with a description and amount.');
        }

        $invoices = new InvoiceModel();
        $subscription = (new SubscriptionModel())->currentFor($tenantId);

        $invoiceId = $invoices->insert([
            'tenant_id'       => $tenantId,
            'subscription_id' => $subscription->id ?? null,
            'invoice_number'  => $invoices->nextInvoiceNumber(),
            'amount'          => $total,
            'status'          => 'sent',
            'due_date'        => $dueDate ?: date('Y-m-d', strtotime('+14 days')),
        ], true);

        if ($invoiceId === false) {
            return $this->redirectWithModelErrors($invoices);
        }

        $lineItems = new InvoiceLineItemModel();

        foreach ($lines as $line) {
            $lineItems->addLine($invoiceId, $line['description'], $line['amount']);
        }

        $this->logAdminActivity('invoices', "Generated invoice for \"{$tenant->hotel_name}\" — " . format_money($total) . '.');

        return redirect()->to('/admin/invoices/' . $invoiceId)->with('success', 'Invoice generated.');
    }

    public function show(int $id)
    {
        $invoice = (new InvoiceModel())->find($id);

        if ($invoice === null) {
            return redirect()->to('/admin/invoices')->with('error', 'Invoice not found.');
        }

        $tenant = (new TenantRecordModel())->find($invoice->tenant_id);

        return view('admin/invoices/show', [
            'pageTitle'   => $invoice->invoice_number,
            'activeNav'   => 'invoices',
            'invoice'     => $invoice,
            'tenant'      => $tenant,
            'lineItems'   => (new InvoiceLineItemModel())->forInvoice($id),
            'transactions'=> array_filter((new TransactionModel())->forTenant($invoice->tenant_id), static fn ($t) => (int) $t->invoice_id === $id),
        ]);
    }

    public function pdf(int $id)
    {
        $invoice = (new InvoiceModel())->find($id);

        if ($invoice === null) {
            return redirect()->to('/admin/invoices')->with('error', 'Invoice not found.');
        }

        $tenant    = (new TenantRecordModel())->find($invoice->tenant_id);
        $lineItems = (new InvoiceLineItemModel())->forInvoice($id);

        $html = view('admin/invoices/pdf', [
            'invoice'   => $invoice,
            'tenant'    => $tenant,
            'lineItems' => $lineItems,
            'issuedAt'  => date('Y-m-d H:i'),
        ]);

        $pdf = service('pdfService')->renderHtml($html);

        $this->logAdminActivity('invoices', "Generated PDF for invoice {$invoice->invoice_number}.");

        return $this->response
            ->setHeader('Content-Type', 'application/pdf')
            ->setHeader('Content-Disposition', "inline; filename=\"{$invoice->invoice_number}.pdf\"")
            ->setBody($pdf);
    }

    public function markPaid(int $id)
    {
        $invoices = new InvoiceModel();
        $invoice  = $invoices->find($id);

        if ($invoice === null) {
            return redirect()->to('/admin/invoices')->with('error', 'Invoice not found.');
        }

        $invoices->markPaid($id);

        // Manual settlement (bank transfer, cash, ...) — record it on the
        // ledger too, same as Hubtel-confirmed payments, so Transactions
        // always shows the complete picture regardless of how a hotel
        // actually paid.
        (new TransactionModel())->insert([
            'tenant_id'  => $invoice->tenant_id,
            'invoice_id' => $id,
            'amount'     => $invoice->amount,
            'type'       => 'charge',
            'method'     => 'manual',
            'status'     => 'success',
        ]);

        $tenant = (new TenantRecordModel())->find($invoice->tenant_id);
        $this->logAdminActivity('invoices', "Marked invoice {$invoice->invoice_number} paid for \"{$tenant->hotel_name}\".");

        return redirect()->to('/admin/invoices/' . $id)->with('success', 'Invoice marked as paid.');
    }

    public function void(int $id)
    {
        $invoices = new InvoiceModel();
        $invoice  = $invoices->find($id);

        if ($invoice === null) {
            return redirect()->to('/admin/invoices')->with('error', 'Invoice not found.');
        }

        $invoices->update($id, ['status' => 'void']);

        $this->logAdminActivity('invoices', "Voided invoice {$invoice->invoice_number}.");

        return redirect()->to('/admin/invoices/' . $id)->with('success', 'Invoice voided.');
    }

    /**
     * Generates a Hubtel Checkout link against the PLATFORM's own Hubtel
     * account (never a tenant's — see Config\Services::platformHubtelService())
     * so the hotel can pay their subscription invoice. The admin doesn't
     * pay this themselves; the link is shown for them to copy/send to the
     * hotel's billing contact.
     */
    public function requestHubtelPayment(int $id)
    {
        $invoice = (new InvoiceModel())->find($id);

        if ($invoice === null) {
            return redirect()->to('/admin/invoices')->with('error', 'Invoice not found.');
        }

        $transactions = new TransactionModel();
        $clientReference = $transactions->generateClientReference();

        try {
            $checkout = service('platformHubtelService')->createCheckout(
                (float) $invoice->amount,
                'MaxYields PMS subscription — ' . $invoice->invoice_number,
                $clientReference,
                site_url('webhooks/hubtel/platform-checkout'),
                site_url('admin/invoices/' . $id),
                site_url('admin/invoices/' . $id),
            );
        } catch (RuntimeException $e) {
            return redirect()->to('/admin/invoices/' . $id)->with('error', $e->getMessage());
        }

        $transactions->insert([
            'tenant_id'        => $invoice->tenant_id,
            'invoice_id'       => $id,
            'amount'           => $invoice->amount,
            'type'             => 'charge',
            'method'           => 'hubtel_momo',
            'client_reference' => $clientReference,
            'status'           => 'pending',
        ]);

        $this->logAdminActivity('invoices', "Generated Hubtel payment link for invoice {$invoice->invoice_number}.");

        return redirect()->to('/admin/invoices/' . $id)->with('success', 'Payment link generated: ' . $checkout['checkoutUrl']);
    }
}
