<?php
/**
 * fiscal_tax_invoice.php - Fiscal Tax Invoice PDF for a booking
 *
 * Usage:  fiscal_tax_invoice.php?order_id=123
 *         fiscal_tax_invoice.php?order_id=123&finalize=1   (also sets orders.invoiced=1)
 *
 * Mirrors the Master Folio Proforma layout, but titled "FISCAL TAX INVOICE".
 *   - BEFORE fiscalization: a plain PDF invoice, watermarked "NOT YET
 *     FISCALIZED" so it can never be mistaken for a compliant tax invoice.
 *   - AFTER fiscalization (a successful row in fdms_fiscal_invoices): the
 *     ZIMRA invoice number, verification code, fiscal day number and device
 *     id are printed, and the QR code is rendered bottom-right.
 *
 * Charges come from the master folio's charge transactions - the SAME
 * classification used by accounting.php and fiscal_fdms.php, so the invoice
 * total always equals what was (or will be) fiscalized.
 *
 * Finalizing (finalize=1) sets orders.invoiced = 1 (column auto-created),
 * which the Accounting page filters on.
 */

require_once __DIR__ . '/../../includes/bootstrap.php';
require_login();

$orderId  = (int)($_GET['order_id'] ?? 0);
$finalize = !empty($_GET['finalize']) ? 1 : 0;
if ($orderId <= 0) { http_response_code(400); exit('Invalid booking.'); }

if (!function_exists('fti_column_exists')) {
    function fti_column_exists(PDO $pdo, string $table, string $column): bool {
        static $cache = [];
        $key = $table . '.' . $column;
        if (array_key_exists($key, $cache)) return $cache[$key];
        try {
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t AND COLUMN_NAME = :c");
            $stmt->execute([':t' => $table, ':c' => $column]);
            $cache[$key] = ((int)$stmt->fetchColumn() > 0);
        } catch (Throwable $e) { $cache[$key] = false; }
        return $cache[$key];
    }
}

if (!function_exists('fti_table_exists')) {
    function fti_table_exists(PDO $pdo, string $table): bool {
        static $cache = [];
        if (array_key_exists($table, $cache)) return $cache[$table];
        try {
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t");
            $stmt->execute([':t' => $table]);
            $cache[$table] = ((int)$stmt->fetchColumn() > 0);
        } catch (Throwable $e) { $cache[$table] = false; }
        return $cache[$table];
    }
}

if (!function_exists('fti_order_is_cancelled')) {
    function fti_order_is_cancelled(PDO $pdo, array $order): bool {
        foreach (['is_cancelled','is_canceled','cancelled','canceled'] as $c) {
            if (array_key_exists($c, $order) && (int)$order[$c] === 1) return true;
        }
        foreach (['cancelled_at','canceled_at','cancellation_date','cancelled_date','date_cancelled'] as $c) {
            $v = trim((string)($order[$c] ?? ''));
            if ($v !== '' && $v !== '0000-00-00' && $v !== '0000-00-00 00:00:00') return true;
        }
        foreach (['cancelled_reason','canceled_reason','cancellation_reason'] as $c) {
            if (trim((string)($order[$c] ?? '')) !== '') return true;
        }
        foreach (['booking_status','status','status_name','booking_status_name','reservation_status'] as $c) {
            $v = strtolower(trim((string)($order[$c] ?? '')));
            if ($v !== '' && (strpos($v, 'cancel') !== false || strpos($v, 'void') !== false)) return true;
        }
        return false;
    }
}

if (!function_exists('fti_allocated_room_pax')) {
    function fti_allocated_room_pax(PDO $pdo, int $orderId): array {
        if (!fti_table_exists($pdo, 'order_rooming')) return [];
        try {
            $stmt = $pdo->prepare("SELECT COALESCE(adults,0) adults, COALESCE(children,0) children FROM order_rooming WHERE order_id = :oid ORDER BY room_no ASC, id ASC");
            $stmt->execute([':oid' => $orderId]);
            return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        } catch (Throwable $e) { return []; }
    }
}
if (!function_exists('fti_ensure_invoiced_column')) {
    function fti_ensure_invoiced_column(PDO $pdo): void {
        if (!fti_column_exists($pdo, 'orders', 'invoiced')) {
            try { $pdo->exec("ALTER TABLE orders ADD COLUMN invoiced TINYINT(1) NOT NULL DEFAULT 0"); } catch (Throwable $e) {}
        }
        if (!fti_column_exists($pdo, 'orders', 'invoiced_at')) {
            try { $pdo->exec("ALTER TABLE orders ADD COLUMN invoiced_at DATETIME NULL"); } catch (Throwable $e) {}
        }
    }
}
if (!function_exists('fti_first_nonempty')) {
    function fti_first_nonempty(array $row, array $candidates): string {
        foreach ($candidates as $c) {
            if (isset($row[$c]) && trim((string)$row[$c]) !== '') return trim((string)$row[$c]);
        }
        return '';
    }
}

if (!function_exists('fti_value_ci')) {
    function fti_value_ci(array $row, array $candidates): string {
        foreach ($candidates as $candidate) {
            foreach ($row as $key => $value) {
                if (strcasecmp((string)$key, (string)$candidate) === 0 && trim((string)$value) !== '') {
                    return trim((string)$value);
                }
            }
        }
        return '';
    }
}

if (!function_exists('fti_normalise_bank_row')) {
    function fti_normalise_bank_row(array $row): array {
        return [
            'Title'       => fti_value_ci($row, ['Title','AccountTitle','BankTitle','Description','Name']),
            'Bank'        => fti_value_ci($row, ['Bank','BankName']),
            'AccountName' => fti_value_ci($row, ['AccountName','AccountHolder','BankAccountName']),
            'AccountNo'   => fti_value_ci($row, ['AccountNo','AccountNumber','BankAccountNo']),
            'Branch'      => fti_value_ci($row, ['Branch','BankBranch','BranchName']),
            'BranchCode'  => fti_value_ci($row, ['BranchCode','SortCode','SortBranchCode','RoutingCode']),
            'SwiftCode'   => fti_value_ci($row, ['SwiftCode','Swift','SWIFT','BIC']),
            'Currency'    => fti_value_ci($row, ['Currency','CurrencyCode','AccountCurrency']),
            'Address'     => fti_value_ci($row, ['Address','BankAddress','Details','Detail']),
        ];
    }
}

/* ---------------------------------------------------------------------
   Load booking, buyer, charges, and any successful fiscal record
   --------------------------------------------------------------------- */
$o = $pdo->prepare("SELECT * FROM orders WHERE id = :oid LIMIT 1");
$o->execute([':oid' => $orderId]);
$order = $o->fetch(PDO::FETCH_ASSOC);
if (!$order) { http_response_code(404); exit('Booking not found.'); }

$guest = null;
if ((int)($order['guest_id'] ?? 0) > 0) {
    $g = $pdo->prepare("SELECT * FROM guests WHERE id = :id LIMIT 1");
    $g->execute([':id' => (int)$order['guest_id']]);
    $guest = $g->fetch(PDO::FETCH_ASSOC) ?: null;
}

$agent = null;
if (fti_column_exists($pdo, 'orders', 'agent_id') && (int)($order['agent_id'] ?? 0) > 0) {
    try {
        $a = $pdo->prepare("SELECT * FROM agents WHERE id = :id LIMIT 1");
        $a->execute([':id' => (int)$order['agent_id']]);
        $agent = $a->fetch(PDO::FETCH_ASSOC) ?: null;
    } catch (Throwable $e) { $agent = null; }
}

$property = null;
try {
    $p = $pdo->prepare("SELECT * FROM properties WHERE id = :id LIMIT 1");
    $p->execute([':id' => (int)($order['property_id'] ?? 0)]);
    $property = $p->fetch(PDO::FETCH_ASSOC) ?: null;
} catch (Throwable $e) { $property = null; }

/* Banking details for this property (up to three active/configured accounts). */
$bankDetailsList = [];
try {
    if (fti_table_exists($pdo, 'tblbankdetails')) {
        $rawBanks = [];
        $propertyId = (int)($order['property_id'] ?? 0);
        if (fti_column_exists($pdo, 'tblbankdetails', 'property_id') && $propertyId > 0) {
            $bs = $pdo->prepare("SELECT * FROM tblbankdetails WHERE property_id = :pid ORDER BY ID ASC LIMIT 3");
            $bs->execute([':pid' => $propertyId]);
            $rawBanks = $bs->fetchAll(PDO::FETCH_ASSOC) ?: [];
        }
        if (!$rawBanks) {
            $bs = $pdo->query("SELECT * FROM tblbankdetails ORDER BY ID ASC LIMIT 3");
            $rawBanks = $bs ? ($bs->fetchAll(PDO::FETCH_ASSOC) ?: []) : [];
        }
        foreach ($rawBanks as $rawBank) {
            $bank = fti_normalise_bank_row($rawBank);
            if ($bank['Bank'] !== '' || $bank['AccountName'] !== '' || $bank['AccountNo'] !== '') {
                $bankDetailsList[] = $bank;
            }
        }
    }
} catch (Throwable $e) {
    error_log('Fiscal invoice bank details loading failed for order ' . $orderId . ': ' . $e->getMessage());
    $bankDetailsList = [];
}

/* ---------------------------------------------------------------------
   Charge lines - mirror proforma_invoice.php exactly
   ---------------------------------------------------------------------
   - Master folio only
   - Non-voided debit lines only
   - Exclude payment reversals/refunds and descriptions marked void/reversal/refund
   - Use linked extra item detail only when it reconciles to the transaction
   - Add room pax to accommodation lines
   - Cancelled bookings show only explicit cancellation-fee lines
   - Sort by transaction date, then transaction id
*/
$charges = [];
$folioNo = '';
$hasExtraChargeItems = fti_table_exists($pdo, 'extra_charge_items');

$extraJoin = $hasExtraChargeItems ? "
    LEFT JOIN (
        SELECT
            folio_transaction_id,
            GROUP_CONCAT(
                CONCAT(
                    COALESCE(NULLIF(item_name,''), 'Item'),
                    CASE WHEN COALESCE(item_code,'') <> '' THEN CONCAT(' [', item_code, ']') ELSE '' END,
                    ' x ', TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(qty,0), 4))),
                    ' @ ', FORMAT(COALESCE(unit_price,0), 2)
                )
                ORDER BY id ASC SEPARATOR '\n'
            ) AS linked_item_description,
            SUM(COALESCE(line_total,0)) AS linked_line_total
        FROM extra_charge_items
        GROUP BY folio_transaction_id
    ) eci ON eci.folio_transaction_id = ft.id
" : "";

$printableSql = $hasExtraChargeItems ? "
    CASE
        WHEN COALESCE(eci.linked_item_description,'') <> ''
         AND ABS(COALESCE(eci.linked_line_total,0) - COALESCE(ft.debit,0)) < 0.01
            THEN eci.linked_item_description
        ELSE ft.description
    END AS printable_description
" : "ft.description AS printable_description";

try {
    $stmt = $pdo->prepare("
        SELECT
            ft.id, ft.tx_date, ft.description, ft.debit, ft.credit,
            ft.tx_type, ft.source_table, f.folio_no,
            {$printableSql}
        FROM folio_transactions ft
        INNER JOIN folios f ON f.id = ft.folio_id
        {$extraJoin}
        WHERE ft.order_id = :oid
          AND LOWER(COALESCE(f.folio_type,'')) = 'master'
          AND COALESCE(ft.is_voided,0) = 0
          AND COALESCE(ft.debit,0) > 0
          AND LOWER(COALESCE(ft.tx_type,'')) NOT IN (
                'payment_void','void_payment','payment_reversal',
                'reversal','refund','payment_refund'
          )
          AND LOWER(COALESCE(ft.description,'')) NOT LIKE '%void%'
          AND LOWER(COALESCE(ft.description,'')) NOT LIKE '%reversal%'
          AND LOWER(COALESCE(ft.description,'')) NOT LIKE '%refund%'
        ORDER BY ft.tx_date ASC, ft.id ASC
    ");
    $stmt->execute([':oid' => $orderId]);
    $charges = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
    if ($charges) $folioNo = (string)($charges[0]['folio_no'] ?? '');
} catch (Throwable $e) {
    error_log('Fiscal invoice charge loading failed for order ' . $orderId . ': ' . $e->getMessage());
    $charges = [];
}

/* Add pax to the same accommodation rows printed by the proforma. */
$roomPaxRows = fti_allocated_room_pax($pdo, $orderId);
$roomPaxCount = count($roomPaxRows);
$accomIndex = 0;
foreach ($charges as &$charge) {
    $txType = strtolower(trim((string)($charge['tx_type'] ?? '')));
    $source = strtolower(trim((string)($charge['source_table'] ?? '')));
    $desc = strtolower(trim((string)($charge['description'] ?? '')));

    $isExtra = in_array($txType, ['extra','extras','extra_charge','addon','add_on','transfer','restaurant','bar','spar','spa','curio','minibar','laundry','park_fee'], true)
        || in_array($source, ['tblservices','restaurant_menu_items','inventory_items','extra_charge_items','extras','order_extras','booking_extras','reservation_extras','order_extra_items','manual_extra'], true);
    $isAccommodation = !$isExtra && (
        in_array($txType, ['accommodation','room','room_charge','accommodation_charge','nightly','room_night'], true)
        || $source === 'rel_orders_prices'
        || strpos($desc, 'accommodation') !== false
        || strpos($desc, 'room charge') !== false
    );
    if (!$isAccommodation) continue;

    if ($roomPaxCount > 0) {
        $rp = $roomPaxRows[$accomIndex % $roomPaxCount];
        $a = (int)($rp['adults'] ?? 0);
        $c = (int)($rp['children'] ?? 0);
        $totalPax = $a + $c;
        if ($totalPax > 0) {
            $bits = [];
            if ($a > 0) $bits[] = $a . 'A';
            if ($c > 0) $bits[] = $c . 'C';
            $suffix = ' | Pax: ' . $totalPax . ' (' . implode(' ', $bits) . ')';
            $base = trim((string)($charge['printable_description'] ?? $charge['description'] ?? 'Accommodation'));
            if (stripos($base, 'Pax:') === false) $charge['printable_description'] = $base . $suffix;
        }
    }
    $accomIndex++;
}
unset($charge);

/* Same cancelled-booking rule as the proforma: fee lines only. */
if (fti_order_is_cancelled($pdo, $order)) {
    $cancelLines = [];
    foreach ($charges as $charge) {
        $txType = strtolower(trim((string)($charge['tx_type'] ?? '')));
        $source = strtolower(trim((string)($charge['source_table'] ?? '')));
        $desc = strtolower(trim((string)($charge['description'] ?? '')));
        $printable = strtolower(trim((string)($charge['printable_description'] ?? '')));
        $isFee = in_array($txType, ['cancellation_fee','cancellation','cancel_fee','cancellation_charge','cancel_charge','late_cancellation_fee'], true)
            || in_array($source, ['cancellation_fees','cancellation_fee','booking_cancellation_fees','cancel_fees'], true)
            || strpos($desc, 'cancellation fee') !== false
            || strpos($desc, 'cancel fee') !== false
            || strpos($desc, 'cancellation charge') !== false
            || strpos($desc, 'cancel charge') !== false
            || strpos($printable, 'cancellation fee') !== false
            || strpos($printable, 'cancel fee') !== false
            || strpos($printable, 'cancellation charge') !== false
            || strpos($printable, 'cancel charge') !== false;
        if ($isFee) $cancelLines[] = $charge;
    }
    $charges = $cancelLines;
}

/* The proforma does not invent a fallback line from orders.totalamount. */

/*
   Tax decomposition (compound inclusive).
   Each accommodation/charge debit is inclusive of BOTH:
     - ZTA Levy 2%   and
     - VAT 15.5%
   on the same net base. We split every line the same way and round ZTA
   FIRST, then derive VAT from the ZTA-removed remainder, so that:
       lineZta + lineVat + lineNet = gross debit   (reconciles to the cent)
   Per-line rules (as specified):
       ZTA   = round( (2 / 117.5) * gross , 2 )
       amt   = gross - ZTA                       (VAT-inclusive line amount shown)
       VAT   = round( (15.5 / 115.5) * amt , 2 )
       net   = amt - VAT
   Summary: Subtotal(net) + ZTA + VAT = Invoice Total.
*/
$vatRate   = 15.5;
$ztaRate   = 2.0;

$totalIncl    = 0.0;   // sum of gross line debits (= invoice total)
$ztaTotal     = 0.0;   // sum of per-line ZTA
$vatTotal     = 0.0;   // sum of per-line VAT
$subtotalNet  = 0.0;   // sum of per-line net (excl both taxes)

foreach ($charges as $idx => $c) {
    $gross   = round((float)$c['debit'], 2);
    $lineZta = round((2.0 / 117.5) * $gross, 2);           // ZTA rounded first
    $lineAmt = round($gross - $lineZta, 2);                // VAT-inclusive amount shown on the line
    $lineVat = round((15.5 / 115.5) * $lineAmt, 2);        // VAT from the ZTA-removed remainder
    $lineNet = round($lineAmt - $lineVat, 2);              // true net (excl both taxes)

    // Stash the computed parts back on the charge so the line loop can print them.
    $charges[$idx]['calc_gross'] = $gross;
    $charges[$idx]['calc_zta']   = $lineZta;
    $charges[$idx]['calc_amt']   = $lineAmt;
    $charges[$idx]['calc_vat']   = $lineVat;
    $charges[$idx]['calc_net']   = $lineNet;

    $totalIncl   += $gross;
    $ztaTotal    += $lineZta;
    $vatTotal    += $lineVat;
    $subtotalNet += $lineNet;
}

$totalIncl   = round($totalIncl, 2);
$ztaTotal    = round($ztaTotal, 2);
$vatTotal    = round($vatTotal, 2);
$subtotalNet = round($subtotalNet, 2);

/* Guard: force Subtotal + ZTA + VAT to equal the invoice total exactly,
   absorbing any 1-cent rounding drift into the net subtotal. */
$reconDiff = round($totalIncl - ($subtotalNet + $ztaTotal + $vatTotal), 2);
if (abs($reconDiff) >= 0.01) {
    $subtotalNet = round($subtotalNet + $reconDiff, 2);
}

/* Payments already received (for the balance line) */
$paid = 0.0;
try {
    $ps = $pdo->prepare("SELECT COALESCE(SUM(total_payments),0) FROM folios WHERE order_id = :oid");
    $ps->execute([':oid' => $orderId]);
    $paid = round((float)$ps->fetchColumn(), 2);
} catch (Throwable $e) {}

/* Fiscal record (successful only) */
$fiscal = null;
try {
    $fs = $pdo->prepare("SELECT * FROM fdms_fiscal_invoices WHERE order_id = :oid AND success = 1 ORDER BY id DESC LIMIT 1");
    $fs->execute([':oid' => $orderId]);
    $fiscal = $fs->fetch(PDO::FETCH_ASSOC) ?: null;
} catch (Throwable $e) { $fiscal = null; }

$isFiscalized = ($fiscal !== null);

/* Finalize: mark the booking invoiced */
if ($finalize) {
    fti_ensure_invoiced_column($pdo);
    try {
        $sets = ['invoiced = 1'];
        if (fti_column_exists($pdo, 'orders', 'invoiced_at')) $sets[] = 'invoiced_at = NOW()';
        $pdo->prepare('UPDATE orders SET ' . implode(', ', $sets) . ' WHERE id = :oid')->execute([':oid' => $orderId]);
    } catch (Throwable $e) {}
}

/* Buyer / invoice-to block. Prefer the booking's explicit company invoice fields,
   then the selected agent, then the guest. */
$buyerName = fti_first_nonempty($order, ['CompanyName','company_name','invoice_company','billing_name','customer_name']);
$buyerAddressParts = [];
foreach (['CompanyAddress','company_address','invoice_address','billing_address','address','address1','address2','city','province','state','country','postal_code'] as $field) {
    $v = trim((string)($order[$field] ?? ''));
    if ($v !== '' && !in_array($v, $buyerAddressParts, true)) $buyerAddressParts[] = $v;
}
$buyerAddress = implode(', ', $buyerAddressParts);
$buyerVat = fti_first_nonempty($order, ['VATNo','vat_no','vat_number','company_vat_no','invoice_vat_no','billing_vat_no']);
$buyerTin = fti_first_nonempty($order, ['TINNo','tin_no','tin_number','BPNo','bp_no','bpno','company_tin_no','invoice_tin_no']);
$buyerPhone = fti_first_nonempty($order, ['company_phone','billing_phone','phone','telephone','mobile']);
$buyerEmail = fti_first_nonempty($order, ['company_email','billing_email','email']);

if ($agent) {
    if ($buyerName === '') {
        $buyerName = trim(fti_first_nonempty($agent, ['firstname']) . ' ' . fti_first_nonempty($agent, ['lastname']));
        if ($buyerName === '') $buyerName = fti_first_nonempty($agent, ['agent','name','company','company_name','registered_name','RegisteredName']);
    }
    if ($buyerAddress === '') {
        $parts = [];
        foreach (['address','address1','address2','physical_address','city','province','state','country','postal_code'] as $field) {
            $v = trim((string)($agent[$field] ?? ''));
            if ($v !== '' && !in_array($v, $parts, true)) $parts[] = $v;
        }
        $buyerAddress = implode(', ', $parts);
    }
    if ($buyerVat === '') $buyerVat = fti_first_nonempty($agent, ['vatno','vat_no','vat_number','vatref','vat_ref','VATNo']);
    if ($buyerTin === '') $buyerTin = fti_first_nonempty($agent, ['tinno','tin_no','tin_number','bp_no','bpn','bpno','TIN','BPNo']);
    if ($buyerPhone === '') $buyerPhone = fti_first_nonempty($agent, ['phone','telephone','mobile','cell']);
    if ($buyerEmail === '') $buyerEmail = fti_first_nonempty($agent, ['email','email_address']);
}
if ($buyerName === '' && trim((string)($order['originating_agent'] ?? '')) !== '') $buyerName = trim((string)$order['originating_agent']);
if ($buyerName === '' && $guest) $buyerName = trim(fti_first_nonempty($guest, ['firstname']) . ' ' . fti_first_nonempty($guest, ['lastname']));
if ($buyerName === '') $buyerName = 'Walk-in Guest';
if ($guest) {
    if ($buyerAddress === '') {
        $parts = [];
        foreach (['address','address1','address2','city','province','state','country','postal_code'] as $field) {
            $v = trim((string)($guest[$field] ?? ''));
            if ($v !== '' && !in_array($v, $parts, true)) $parts[] = $v;
        }
        $buyerAddress = implode(', ', $parts);
    }
    if ($buyerVat === '') $buyerVat = fti_first_nonempty($guest, ['vatno','vat_no','vat_number','VATNo']);
    if ($buyerTin === '') $buyerTin = fti_first_nonempty($guest, ['tinno','tin_no','tin_number','bp_no','bpno','TIN','BPNo']);
    if ($buyerPhone === '') $buyerPhone = fti_first_nonempty($guest, ['phone','telephone','mobile','cell']);
    if ($buyerEmail === '') $buyerEmail = fti_first_nonempty($guest, ['email','email_address']);
}

$guestName = $guest ? trim(fti_first_nonempty($guest, ['firstname']) . ' ' . fti_first_nonempty($guest, ['lastname'])) : '';
$invoiceNo = trim((string)($order['order_no'] ?? '')) !== '' ? (string)$order['order_no'] : ('RZ-' . $orderId);
$currency = strtoupper(trim((string)($order['currency'] ?? 'USD'))) ?: 'USD';

$propName = $property ? fti_first_nonempty($property, ['property_name','name','title']) : '';
$propAddress = $property ? fti_first_nonempty($property, ['address','address1','physical_address']) : '';
$propVat = $property ? fti_first_nonempty($property, ['vat_no','vat_number','vatref','VATNo','VATNO']) : '';
$propTin = $property ? fti_first_nonempty($property, ['tin','tin_number','bp_no','bpno','TIN','BPNo']) : '';
$propPhone = $property ? fti_first_nonempty($property, ['phone','tel','contact_no','telephone']) : '';
$propEmail = $property ? fti_first_nonempty($property, ['email','email_address']) : '';

/* Pioneers statutory/contact fallbacks prevent blank legal details when the
   properties table uses different column names or has incomplete records. */
if ($propName === '') $propName = 'Pioneers Lodge Pvt Ltd';
if ($propAddress === '') $propAddress = 'No. 657 Mahogany Road, Victoria Falls, Matebeleland North, Zimbabwe';
if ($propVat === '') $propVat = '220068351';
if ($propTin === '') $propTin = '2001134870';
if ($propPhone === '') $propPhone = '+263 780 728 696 / +263 867 719 7736-7';
if ($propEmail === '') $propEmail = 'info@pioneersvicfalls.com';

/* ---------------------------------------------------------------------
   PDF - FPDF (as used by the other RoomZ PDF documents)
   --------------------------------------------------------------------- */
$fpdfCandidates = [
    __DIR__ . '/../../includes/fpdf/fpdf.php',
    __DIR__ . '/../../includes/fpdf.php',
    __DIR__ . '/../../vendor/setasign/fpdf/fpdf.php',
    __DIR__ . '/../../libs/fpdf/fpdf.php',
];
if (!class_exists('FPDF')) {
    foreach ($fpdfCandidates as $cand) {
        if (is_file($cand)) { require_once $cand; break; }
    }
}
if (!class_exists('FPDF')) { http_response_code(500); exit('FPDF library not found. Checked: ' . implode(', ', $fpdfCandidates)); }

class FiscalInvoicePDF extends FPDF
{
    public $docTitle = 'FISCAL TAX INVOICE';
    public $propName = 'Pioneers Lodge Pvt Ltd';
    public $propAddress = '657, Mahogany Road, Victoria Falls, Matebeleland';
    public $propVat = '220068351';
    public $propTin = '2001134870';
    public $propPhone = '0775162358';
    public $propEmail = 'accounts@batonkaguestlodge.com';
    public $notFiscalized = false;

    function Header()
    {
        /* Premium corporate header */
        $navy = [24, 38, 58];
        $red  = [153, 27, 42];
        $gold = [184, 145, 70];

        // Subtle watermark for non-fiscalized documents
        if ($this->notFiscalized) {
            $this->SetFont('Arial', 'B', 34);
            $this->SetTextColor(244, 230, 232);
            $this->SetXY(15, 122);
            $this->Cell(180, 16, 'NOT YET FISCALIZED', 0, 0, 'C');
        }

        // Top identity band
        $this->SetFillColor($navy[0], $navy[1], $navy[2]);
        $this->Rect(0, 0, 210, 7, 'F');
        $this->SetFillColor($red[0], $red[1], $red[2]);
        $this->Rect(0, 7, 210, 1.4, 'F');

        // Property identity
        $this->SetXY(10, 13);
        $this->SetTextColor($navy[0], $navy[1], $navy[2]);
        $this->SetFont('Arial', 'B', 15.5);
        $this->Cell(118, 7, $this->propName, 0, 1, 'L');

        $this->SetFont('Arial', '', 7.6);
        $this->SetTextColor(70, 78, 88);
        if ($this->propAddress !== '') {
            $this->SetX(10);
            $this->Cell(118, 4, $this->propAddress, 0, 1, 'L');
        }
        $contact = [];
        if ($this->propPhone !== '') $contact[] = 'Tel: ' . $this->propPhone;
        if ($this->propEmail !== '') $contact[] = 'Email: ' . $this->propEmail;
        if ($contact) {
            $this->SetX(10);
            $this->Cell(118, 4, implode('   |   ', $contact), 0, 1, 'L');
        }
        $tax = [];
        if ($this->propTin !== '') $tax[] = 'TIN/BP: ' . $this->propTin;
        if ($this->propVat !== '') $tax[] = 'VAT No: ' . $this->propVat;
        if ($tax) {
            $this->SetX(10);
            $this->Cell(118, 4, implode('   |   ', $tax), 0, 1, 'L');
        }

        // Document title panel
        $this->SetFillColor(248, 249, 251);
        $this->SetDrawColor(218, 222, 228);
        $this->RoundedRect(132, 13, 68, 20, 2.5, 'DF');
        $this->SetXY(136, 16);
        $this->SetTextColor($red[0], $red[1], $red[2]);
        $this->SetFont('Arial', 'B', 13.2);
        $this->Cell(60, 6, $this->docTitle, 0, 1, 'R');
        $this->SetX(136);
        $this->SetFont('Arial', '', 7.2);
        $this->SetTextColor(95, 102, 112);
        $this->Cell(60, 4, 'Official booking revenue document', 0, 1, 'R');

        // Gold finishing rule
        $this->SetDrawColor($gold[0], $gold[1], $gold[2]);
        $this->SetLineWidth(0.55);
        $this->Line(10, 38, 200, 38);
        $this->SetLineWidth(0.2);
        $this->SetDrawColor(0, 0, 0);
        $this->SetTextColor(0, 0, 0);
        $this->SetY(41);
    }

    function RoundedRect($x, $y, $w, $h, $r, $style = '')
    {
        $k = $this->k;
        $hp = $this->h;
        if ($style == 'F') $op = 'f';
        elseif ($style == 'FD' || $style == 'DF') $op = 'B';
        else $op = 'S';
        $MyArc = 4/3 * (sqrt(2) - 1);
        $this->_out(sprintf('%.2F %.2F m', ($x+$r)*$k, ($hp-$y)*$k));
        $xc = $x+$w-$r; $yc = $y+$r;
        $this->_out(sprintf('%.2F %.2F l', $xc*$k, ($hp-$y)*$k));
        $this->_arc($xc+$r*$MyArc, $yc-$r, $xc+$r, $yc-$r*$MyArc, $xc+$r, $yc);
        $xc = $x+$w-$r; $yc = $y+$h-$r;
        $this->_out(sprintf('%.2F %.2F l', ($x+$w)*$k, ($hp-$yc)*$k));
        $this->_arc($xc+$r, $yc+$r*$MyArc, $xc+$r*$MyArc, $yc+$r, $xc, $yc+$r);
        $xc = $x+$r; $yc = $y+$h-$r;
        $this->_out(sprintf('%.2F %.2F l', $xc*$k, ($hp-($y+$h))*$k));
        $this->_arc($xc-$r*$MyArc, $yc+$r, $xc-$r, $yc+$r*$MyArc, $xc-$r, $yc);
        $xc = $x+$r; $yc = $y+$r;
        $this->_out(sprintf('%.2F %.2F l', $x*$k, ($hp-$yc)*$k));
        $this->_arc($xc-$r, $yc-$r*$MyArc, $xc-$r*$MyArc, $yc-$r, $xc, $yc-$r);
        $this->_out($op);
    }

    function _arc($x1, $y1, $x2, $y2, $x3, $y3)
    {
        $h = $this->h;
        $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c',
            $x1*$this->k, ($h-$y1)*$this->k,
            $x2*$this->k, ($h-$y2)*$this->k,
            $x3*$this->k, ($h-$y3)*$this->k));
    }

    function Footer()
    {
        $this->SetY(-17);
        $this->SetDrawColor(214, 218, 224);
        $this->Line(10, $this->GetY(), 200, $this->GetY());
        $this->Ln(2.2);
        $this->SetFont('Arial', '', 6.7);
        $this->SetTextColor(112, 118, 128);
        $this->Cell(70, 4, 'Computer-generated document', 0, 0, 'L');
        $this->Cell(50, 4, 'Generated ' . date('d M Y, H:i'), 0, 0, 'C');
        $this->SetFont('Arial', 'B', 7);
        $this->Cell(70, 4, 'PAGE ' . $this->PageNo() . ' OF {nb}', 0, 0, 'R');
        $this->SetTextColor(0, 0, 0);
    }

    function LabelValue(float $x, float $y, string $label, string $value, float $labelW = 26, float $valueW = 68): void
    {
        $this->SetXY($x, $y);
        $this->SetFont('Arial', 'B', 8);
        $this->Cell($labelW, 4.6, $label, 0, 0, 'L');
        $this->SetFont('Arial', '', 8);
        $this->Cell($valueW, 4.6, $value, 0, 1, 'L');
    }
}

$pdf = new FiscalInvoicePDF('P', 'mm', 'A4');
$pdf->propName = $propName;
$pdf->propAddress = $propAddress;
$pdf->propVat = $propVat;
$pdf->propTin = $propTin;
$pdf->propPhone = $propPhone;
$pdf->propEmail = $propEmail;
$pdf->notFiscalized = !$isFiscalized;
$pdf->AliasNbPages();
$pdf->SetAutoPageBreak(true, 45); // room for totals + QR block
$pdf->AddPage();

/* --- Invoice meta + complete customer details --- */
$top = $pdf->GetY() + 1;
$pdf->SetFont('Arial', 'B', 8.5);
$pdf->SetXY(10, $top);
$pdf->SetFillColor(24, 38, 58);
$pdf->SetTextColor(255, 255, 255);
$pdf->SetDrawColor(24, 38, 58);
$pdf->Cell(92, 6, '  INVOICED TO', 1, 0, 'L', true);
$pdf->SetX(108);
$pdf->Cell(92, 6, '  INVOICE DETAILS', 1, 1, 'L', true);
$pdf->SetTextColor(0, 0, 0);

$boxY = $pdf->GetY();
$customerLines = array_values(array_filter([
    $buyerAddress,
    $buyerVat !== '' ? 'VAT No: ' . $buyerVat : '',
    $buyerTin !== '' ? 'TIN/BP No: ' . $buyerTin : '',
    $buyerPhone !== '' ? 'Tel: ' . $buyerPhone : '',
    $buyerEmail !== '' ? 'Email: ' . $buyerEmail : '',
    ($guestName !== '' && strcasecmp($guestName, $buyerName) !== 0) ? 'Guest: ' . $guestName : '',
], static function ($v) { return trim((string)$v) !== ''; }));
$leftHeight = 7 + max(1, count($customerLines)) * 4.2;
$detailRows = 6 + ($folioNo !== '' ? 1 : 0);
$rightHeight = 3 + $detailRows * 4.6;
$boxHeight = max(31, $leftHeight, $rightHeight) + 2;

/* Draw filled cards BEFORE text. The previous version drew them afterwards,
   which covered the invoice-detail and customer text. */
$pdf->SetDrawColor(215, 219, 225);
$pdf->SetFillColor(251, 252, 253);
$pdf->Rect(10, $boxY, 92, $boxHeight, 'DF');
$pdf->Rect(108, $boxY, 92, $boxHeight, 'DF');

$pdf->SetTextColor(20, 28, 40);
$pdf->SetFont('Arial', 'B', 9.4);
$pdf->SetXY(12, $boxY + 2);
$pdf->Cell(88, 5, $buyerName, 0, 1, 'L');
$pdf->SetFont('Arial', '', 7.7);
foreach ($customerLines as $line) {
    $pdf->SetX(12);
    $pdf->MultiCell(87, 4.2, $line, 0, 'L');
}

$y = $boxY + 2;
$pdf->LabelValue(110, $y, 'Invoice No:', $invoiceNo, 27, 60); $y += 4.6;
$pdf->LabelValue(110, $y, 'Invoice Date:', date('d/m/Y'), 27, 60); $y += 4.6;
$pdf->LabelValue(110, $y, 'Booking Ref:', '#' . $orderId, 27, 60); $y += 4.6;
if ($folioNo !== '') { $pdf->LabelValue(110, $y, 'Folio No:', $folioNo, 27, 60); $y += 4.6; }
$pdf->LabelValue(110, $y, 'Check-in:', substr((string)$order['check_in'], 0, 10), 27, 60); $y += 4.6;
$pdf->LabelValue(110, $y, 'Check-out:', substr((string)$order['check_out'], 0, 10), 27, 60); $y += 4.6;
$pdf->LabelValue(110, $y, 'Currency:', $currency, 27, 60);

$pdf->SetDrawColor(0, 0, 0);
$pdf->SetTextColor(0, 0, 0);
$pdf->SetY($boxY + $boxHeight + 4);

/* --- Charge lines --- */
$pdf->SetFont('Arial', 'B', 8.5);
$pdf->SetFillColor(24, 38, 58);
$pdf->SetTextColor(255, 255, 255);
$pdf->Cell(24, 6.5, '  DATE', 1, 0, 'L', true);
$pdf->Cell(102, 6.5, '  DESCRIPTION', 1, 0, 'L', true);
$pdf->Cell(16, 6.5, 'QTY', 1, 0, 'C', true);
$pdf->Cell(24, 6.5, 'VAT %', 1, 0, 'C', true);
$pdf->Cell(24, 6.5, 'AMOUNT ', 1, 1, 'R', true);
$pdf->SetTextColor(0, 0, 0);

$pdf->SetFont('Arial', '', 8);
$fill = false;
foreach ($charges as $c) {
    $pdf->SetFillColor(246, 248, 250);
    $desc = (string)($c['printable_description'] ?? $c['description'] ?? 'Charge');
    if (strlen($desc) > 68) $desc = substr($desc, 0, 65) . '...';
    $pdf->Cell(24, 5.4, ' ' . substr((string)$c['tx_date'], 0, 10), 'LR', 0, 'L', $fill);
    $pdf->Cell(102, 5.4, ' ' . $desc, 'LR', 0, 'L', $fill);
    $pdf->Cell(16, 5.4, '1', 'LR', 0, 'C', $fill);
    $pdf->Cell(24, 5.4, number_format($vatRate, 1) . '%', 'LR', 0, 'C', $fill);
    $pdf->Cell(24, 5.4, number_format((float)($c['calc_amt'] ?? $c['debit']), 2) . ' ', 'LR', 1, 'R', $fill);
    $fill = !$fill;
}

/* ZTA Levy tax row.
   Presentational tax line shown after the accommodation charges. It is NOT a
   charge line: the per-line amounts above are already ZTA-exclusive, so this
   row surfaces the levy that was carried out of each line. Styled distinctly
   (shaded, no Qty / VAT%) so it reads as a tax component, not another charge. */
if ($ztaTotal > 0.005) {
    $pdf->SetFont('Arial', 'B', 8);
    $pdf->SetFillColor(238, 232, 220);
    $pdf->Cell(24, 5.4, '', 'LR', 0, 'L', true);
    $pdf->Cell(102, 5.4, ' ZTA Levy (' . number_format($ztaRate, 1) . '%)', 'LR', 0, 'L', true);
    $pdf->Cell(16, 5.4, '', 'LR', 0, 'C', true);
    $pdf->Cell(24, 5.4, '', 'LR', 0, 'C', true);
    $pdf->Cell(24, 5.4, number_format($ztaTotal, 2) . ' ', 'LR', 1, 'R', true);
    $pdf->SetFont('Arial', '', 8);
}
$pdf->Cell(190, 0, '', 'T', 1);

/* --- Totals --- */
$pdf->Ln(3);
$pdf->SetDrawColor(215, 219, 225);
$pdf->Line(142, $pdf->GetY(), 200, $pdf->GetY());
$pdf->Ln(1.5);
$pdf->SetFont('Arial', '', 8.5);
$pdf->Cell(142, 5.2, '', 0, 0);
$pdf->Cell(24, 5.2, 'Subtotal', 0, 0, 'R');
$pdf->Cell(24, 5.2, number_format($subtotalNet, 2) . ' ', 0, 1, 'R');

$pdf->Cell(142, 5.2, '', 0, 0);
$pdf->Cell(24, 5.2, 'ZTA Levy @ ' . number_format($ztaRate, 1) . '%', 0, 0, 'R');
$pdf->Cell(24, 5.2, number_format($ztaTotal, 2) . ' ', 0, 1, 'R');

$pdf->Cell(142, 5.2, '', 0, 0);
$pdf->Cell(24, 5.2, 'VAT @ ' . number_format($vatRate, 1) . '%', 0, 0, 'R');
$pdf->Cell(24, 5.2, number_format($vatTotal, 2) . ' ', 0, 1, 'R');

$pdf->SetFont('Arial', 'B', 9.5);
$pdf->SetFillColor(24, 38, 58);
$pdf->SetTextColor(255, 255, 255);
$pdf->Cell(142, 6.4, '', 0, 0);
$pdf->Cell(24, 6.4, 'TOTAL (' . $currency . ')', 1, 0, 'R', true);
$pdf->Cell(24, 6.4, number_format($totalIncl, 2) . ' ', 1, 1, 'R', true);
$pdf->SetTextColor(0, 0, 0);

if ($paid > 0.005) {
    $pdf->SetFont('Arial', '', 8.5);
    $pdf->Cell(142, 5.2, '', 0, 0);
    $pdf->Cell(24, 5.2, 'Payments received', 0, 0, 'R');
    $pdf->Cell(24, 5.2, '-' . number_format($paid, 2) . ' ', 0, 1, 'R');
    $pdf->SetFont('Arial', 'B', 9);
    $pdf->Cell(142, 5.8, '', 0, 0);
    $pdf->Cell(24, 5.8, 'BALANCE DUE', 0, 0, 'R');
    $pdf->Cell(24, 5.8, number_format($totalIncl - $paid, 2) . ' ', 0, 1, 'R');
}

/* --- Fiscal block: ZIMRA details + QR code --- */
$pdf->Ln(5);
$blockY = $pdf->GetY();

if ($isFiscalized) {
    $pdf->SetFont('Arial', 'B', 8.5);
    $pdf->SetFillColor(242, 242, 242);
    $pdf->SetXY(10, $blockY);
    $pdf->Cell(190, 5, ' ZIMRA FISCALIZATION', 1, 1, 'L', true);
    $detailTop = $pdf->GetY();

    $y = $detailTop + 1.5;
    $pdf->LabelValue(11, $y, 'ZIMRA Invoice No:', (string)($fiscal['zimra_invoice_no'] ?? ''), 34, 70); $y += 4.6;
    $pdf->LabelValue(11, $y, 'Verification Code:', (string)($fiscal['verification_code'] ?? ''), 34, 70); $y += 4.6;
    $pdf->LabelValue(11, $y, 'Fiscal Day No:', (string)($fiscal['fiscal_day_number'] ?? ''), 34, 70); $y += 4.6;
    $pdf->LabelValue(11, $y, 'Fiscal Invoice ID:', (string)($fiscal['fiscal_id'] ?? ''), 34, 70); $y += 4.6;
    $pdf->LabelValue(11, $y, 'Fiscalized On:', substr((string)($fiscal['created_at'] ?? ''), 0, 16), 34, 70); $y += 4.6;

    /* QR code, tucked bottom-right of the fiscal block. Rendered from the
       qrCodeUrl returned by FiscalPro - if it is an image URL we embed it;
       otherwise we generate a QR image encoding that URL so the ZIMRA
       verification page is always scannable. Failure to fetch never breaks
       the invoice - the URL is printed as text instead. */
    $qrUrl = trim((string)($fiscal['qr_code_url'] ?? ''));
    $qrDrawn = false;
    if ($qrUrl !== '') {
        $qrX = 158;
        $qrY = $detailTop + 1.5;
        $qrSize = 30;
        $tmpFile = null;
        try {
            if (preg_match('/\.(png|jpg|jpeg|gif)(\?|$)/i', $qrUrl)) {
                $imgData = @file_get_contents($qrUrl);
                if ($imgData !== false && strlen($imgData) > 100) {
                    $ext = strtolower(preg_replace('/^.*\.([a-z]+)(\?.*)?$/i', '$1', $qrUrl));
                    if (!in_array($ext, ['png', 'jpg', 'jpeg', 'gif'], true)) $ext = 'png';
                    $tmpFile = sys_get_temp_dir() . '/rz_qr_' . $orderId . '.' . $ext;
                    file_put_contents($tmpFile, $imgData);
                    $pdf->Image($tmpFile, $qrX, $qrY, $qrSize, $qrSize);
                    $qrDrawn = true;
                }
            }
            if (!$qrDrawn) {
                // Generate a QR image encoding the verification URL
                $gen = 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=' . rawurlencode($qrUrl);
                $imgData = @file_get_contents($gen);
                if ($imgData !== false && strlen($imgData) > 100) {
                    $tmpFile = sys_get_temp_dir() . '/rz_qr_' . $orderId . '.png';
                    file_put_contents($tmpFile, $imgData);
                    $pdf->Image($tmpFile, $qrX, $qrY, $qrSize, $qrSize);
                    $qrDrawn = true;
                }
            }
        } catch (Throwable $e) { $qrDrawn = false; }
        if ($tmpFile && is_file($tmpFile)) @unlink($tmpFile);

        if ($qrDrawn) {
            $pdf->SetFont('Arial', '', 6.5);
            $pdf->SetXY($qrX, $qrY + $qrSize + 0.5);
            $pdf->Cell($qrSize, 3.4, 'Scan to verify', 0, 0, 'C');
        } else {
            $pdf->SetFont('Arial', '', 6.5);
            $pdf->SetXY(11, $y);
            $pdf->MultiCell(140, 3.4, 'Verify at: ' . $qrUrl, 0, 'L');
            $y = $pdf->GetY();
        }
    }

    $blockBottom = max($y, $detailTop + 36) + 1;
    $pdf->Rect(10, $detailTop, 190, $blockBottom - $detailTop);
    $pdf->SetY($blockBottom + 3);

    $pdf->SetFont('Arial', 'I', 7);
    $pdf->SetTextColor(90, 90, 90);
    $pdf->Cell(0, 3.6, 'This is a fiscal tax invoice issued through the ZIMRA Fiscalisation Data Management System (FDMS).', 0, 1, 'C');
    $pdf->SetTextColor(0, 0, 0);
} else {
    $pdf->SetFont('Arial', 'B', 8);
    $pdf->SetFillColor(252, 244, 245);
    $pdf->SetDrawColor(153, 27, 42);
    $pdf->SetTextColor(150, 20, 20);
    $pdf->Cell(190, 7, '  NOT YET FISCALIZED - this document is not a valid fiscal tax invoice until fiscalized with ZIMRA.', 1, 1, 'L', true);
    $pdf->SetTextColor(0, 0, 0);
    $pdf->SetDrawColor(0, 0, 0);
}

/* --- Banking details --- */
if ($pdf->GetY() > 225) $pdf->AddPage();
$pdf->Ln(5);
$bankStartY = $pdf->GetY();
$pdf->SetFillColor(24, 38, 58);
$pdf->SetTextColor(255, 255, 255);
$pdf->SetFont('Arial', 'B', 8.5);
$pdf->Cell(190, 6, '  BANKING DETAILS', 1, 1, 'L', true);
$pdf->SetTextColor(0, 0, 0);

if (!$bankDetailsList) {
    $pdf->SetFont('Arial', 'I', 7.5);
    $pdf->SetFillColor(251, 252, 253);
    $pdf->Cell(190, 7, '  No banking details are configured for this property.', 1, 1, 'L', true);
} else {
    foreach ($bankDetailsList as $bankIndex => $bank) {
        if ($pdf->GetY() > 255) $pdf->AddPage();
        $heading = $bank['Title'] !== '' ? $bank['Title'] : ($bank['Bank'] !== '' ? $bank['Bank'] : 'Bank Account');
        $pdf->SetFillColor(245, 247, 250);
        $pdf->SetDrawColor(215, 219, 225);
        $pdf->SetFont('Arial', 'B', 8);
        $pdf->Cell(190, 5.2, '  ' . $heading, 1, 1, 'L', true);
        $rows = [
            ['Account Name', $bank['AccountName']], ['Bank', $bank['Bank']],
            ['Account Number', $bank['AccountNo']], ['Branch', $bank['Branch']],
            ['Branch Code', $bank['BranchCode']], ['SWIFT Code', $bank['SwiftCode']],
            ['Currency', $bank['Currency'] !== '' ? $bank['Currency'] : 'USD'],
            ['Bank Address', $bank['Address']],
        ];
        foreach ($rows as $row) {
            if (trim((string)$row[1]) === '') continue;
            $pdf->SetFont('Arial', 'B', 7.3);
            $pdf->Cell(35, 4.3, '  ' . $row[0] . ':', 'L', 0, 'L');
            $pdf->SetFont('Arial', '', 7.3);
            $pdf->Cell(155, 4.3, (string)$row[1], 'R', 1, 'L');
        }
        $pdf->Cell(190, 0, '', 'T', 1);
        if ($bankIndex < count($bankDetailsList) - 1) {
            $pdf->SetFont('Arial', 'B', 7.2);
            $pdf->Cell(190, 4, 'OR', 0, 1, 'C');
        }
    }
    $pdf->SetFont('Arial', 'I', 6.8);
    $pdf->SetTextColor(95, 102, 112);
    $pdf->MultiCell(190, 3.8, 'Please verify banking details with our offices before making payment. Our banking details will not be changed by email notification.', 0, 'L');
    $pdf->SetTextColor(0, 0, 0);
}

$filename = 'Fiscal_Tax_Invoice_' . preg_replace('/[^A-Za-z0-9_\-]/', '_', $invoiceNo) . '.pdf';
$pdf->Output('I', $filename);
