403Webshell
Server IP : 72.60.21.38  /  Your IP : 216.73.217.140
Web Server : LiteSpeed
System : Linux uk-fast-web1372.main-hosting.eu 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User : u390967363 ( 390967363)
PHP Version : 8.2.30
Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/u390967363/domains/aibenproperties.com/public_html/app/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/u390967363/domains/aibenproperties.com/public_html/app/allocation-letters.php
<?php
if (session_status() === PHP_SESSION_NONE) { session_start(); }
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/functions.php';
require_once __DIR__ . '/includes/mailer.php';
require_once __DIR__ . '/includes/doc_templates.php';
require_once __DIR__ . '/includes/doc_generator.php';
$role = $_SESSION['user_role'] ?? 'guest';
$roleNorm = strtolower(trim((string)$role));
$roleNorm = str_replace([' ', '-'], '_', $roleNorm);
$uid = (int)($_SESSION['user_id'] ?? 0);
$allowed = in_array($roleNorm, ['admin','head_admin','admin_head','admin_officer','super_admin','estate_manager','sales_manager','chairman_ceo','executive'], true);
if (!$allowed) { header('Location: dashboard.php'); exit; }
$companyId = function_exists('getCurrentCompanyId') ? getCurrentCompanyId() : ($_SESSION['company_id'] ?? null);
if (function_exists('ensureAllocationBillingTable')) { ensureAllocationBillingTable($pdo); }
if (function_exists('ensureAllocationLetterSectionsTable')) { ensureAllocationLetterSectionsTable($pdo); }
if (function_exists('ensureAllocationLetterDataTable')) { ensureAllocationLetterDataTable($pdo); }
function colx($t,$c){ return function_exists('tableHasColumn') ? tableHasColumn($t,$c) : true; }
function pickColumnToken($pdo, $table, $column, array $candidates) {
    $tokens = array_values(array_filter(array_unique(array_map(static function ($value) {
        return is_string($value) ? trim($value) : '';
    }, $candidates))));
    if (empty($tokens)) { return ''; }
    try {
        $stmt = $pdo->prepare("SELECT DATA_TYPE, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1");
        $stmt->execute([$table, $column]);
        $meta = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
        if (strtolower((string)($meta['DATA_TYPE'] ?? '')) === 'enum') {
            preg_match_all("/'((?:[^'\\\\]|\\\\.)*)'/", (string)($meta['COLUMN_TYPE'] ?? ''), $matches);
            $allowed = array_map(static function ($value) {
                return str_replace("\\'", "'", (string)$value);
            }, $matches[1] ?? []);
            foreach ($tokens as $token) {
                if (in_array($token, $allowed, true)) {
                    return $token;
                }
            }
            return (string)($allowed[0] ?? $tokens[0]);
        }
    } catch (Throwable $e) {}
    return $tokens[0];
}

function allocationLetterMoney(float $n): string {
    return '₦' . number_format($n, 2);
}

function allocationLetterInlineImage(string $url): string {
    $u = trim($url);
    if ($u === '' || preg_match('/^https?:\/\//i', $u) || preg_match('/^data:image\//i', $u)) { return $u; }
    $root = __DIR__;
    $abs = $root . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, ltrim($u, "/\\"));
    if (!file_exists($abs)) { return $u; }
    $bin = @file_get_contents($abs);
    if ($bin === false) { return $u; }
    $mime = 'image/png';
    if (function_exists('mime_content_type')) { $mt = @mime_content_type($abs); if ($mt) $mime = $mt; }
    return 'data:' . $mime . ';base64,' . base64_encode($bin);
}

function loadAllocationLetterContext(PDO $pdo, int $allocationId, $companyId, bool $isSuperAdmin): array {
    $ctx = [
        'allocation_id' => $allocationId,
        'client' => [],
        'property' => [],
        'billing' => [],
        'computed' => [],
        'sections' => ['details' => [], 'terms' => [], 'guidelines' => [], 'acknowledgement' => []],
        'assets' => ['page2' => '', 'cover' => ''],
    ];
    if ($allocationId <= 0) { return $ctx; }
    $alloc = [];
    try {
        $sql = "SELECT * FROM allocations WHERE id = ?";
        $params = [$allocationId];
        if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) { $sql .= " AND (company_id = ? OR company_id IS NULL)"; $params[] = (int)$companyId; }
        $sql .= " LIMIT 1";
        $st = $pdo->prepare($sql);
        $st->execute($params);
        $alloc = $st->fetch(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e) { $alloc = []; }
    if (!$alloc) { return $ctx; }

    $clientId = (int)($alloc['user_id'] ?? 0);
    $propertyId = (int)($alloc['property_id'] ?? 0);
    $dealId = (int)($alloc['deal_id'] ?? 0);
    $estateId = (int)($alloc['estate_id'] ?? 0);

    $user = [];
    if ($clientId > 0) {
        try {
            $st = $pdo->prepare("SELECT * FROM users WHERE id = ? LIMIT 1");
            $st->execute([$clientId]);
            $user = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        } catch (Throwable $e) { $user = []; }
    }
    $formData = [];
    if ($clientId > 0) {
        try {
            $st = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY updated_at DESC, created_at DESC LIMIT 1");
            $st->execute([$clientId]);
            $raw = (string)($st->fetchColumn() ?: '');
            if ($raw !== '') {
                $tmp = json_decode($raw, true);
                if (is_array($tmp)) { $formData = $tmp; }
            }
        } catch (Throwable $e) { $formData = []; }
    }

    $property = [];
    if ($propertyId > 0) {
        try {
            $st = $pdo->prepare("SELECT * FROM properties WHERE id = ? LIMIT 1");
            $st->execute([$propertyId]);
            $property = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        } catch (Throwable $e) { $property = []; }
        if ($estateId <= 0 && !empty($property['estate_id'])) { $estateId = (int)$property['estate_id']; }
    }
    $estate = [];
    if ($estateId > 0) {
        try {
            $st = $pdo->prepare("SELECT * FROM estates WHERE id = ? LIMIT 1");
            $st->execute([$estateId]);
            $estate = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        } catch (Throwable $e) { $estate = []; }
    }
    $deal = [];
    if ($dealId > 0) {
        try {
            $st = $pdo->prepare("SELECT * FROM deals_submit WHERE id = ? LIMIT 1");
            $st->execute([$dealId]);
            $deal = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        } catch (Throwable $e) { $deal = []; }
    }
    $dealMeta = [];
    if (!empty($deal['meta_json'])) {
        try {
            $tmp = json_decode((string)$deal['meta_json'], true);
            if (is_array($tmp)) { $dealMeta = $tmp; }
        } catch (Throwable $e) { $dealMeta = []; }
    }

    $fullName = trim((string)($user['name'] ?? ''));
    if ($fullName === '') { $fullName = trim((string)($user['full_name'] ?? '')); }
    if ($fullName === '') {
        $fullName = trim((string)(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
    }
    if ($fullName === '') { $fullName = 'Client #' . $clientId; }
    $phone = '';
    if (!empty($user['phone'])) { $phone = (string)$user['phone']; }
    elseif (!empty($user['phone_number'])) { $phone = (string)$user['phone_number']; }
    elseif (!empty($formData['phone'])) { $phone = (string)$formData['phone']; }
    $email = '';
    if (!empty($user['email'])) { $email = (string)$user['email']; }
    elseif (!empty($user['email_address'])) { $email = (string)$user['email_address']; }
    elseif (!empty($formData['email'])) { $email = (string)$formData['email']; }
    $address = (string)($formData['address'] ?? ($formData['residential_address'] ?? ($user['address'] ?? '')));
    $passportUrl = (string)($formData['passport_photo_path'] ?? '');
    if ($passportUrl === '') {
        foreach (['passport_url','photo_url','avatar','profile_photo'] as $k) {
            if (!empty($user[$k]) && is_scalar($user[$k])) { $passportUrl = (string)$user[$k]; break; }
        }
    }
    if ($passportUrl === '' && function_exists('getClientAvatarUrl')) {
        try { $passportUrl = (string)getClientAvatarUrl($pdo, $clientId); } catch (Throwable $e) { $passportUrl = ''; }
    }

    $estateName = trim((string)($estate['name'] ?? ''));
    if ($estateName === '') { $estateName = trim((string)($deal['estate_name'] ?? ($dealMeta['estate_name'] ?? ''))); }
    if ($estateName === '') { $estateName = trim((string)($deal['project_name'] ?? ($dealMeta['project_name'] ?? ''))); }
    $propertyName = trim((string)($property['title'] ?? ($property['name'] ?? '')));
    if ($propertyName === '') { $propertyName = trim((string)($deal['project_desc'] ?? ($dealMeta['project_desc'] ?? ''))); }
    if ($propertyName === '') { $propertyName = trim((string)($deal['project_name'] ?? ($dealMeta['project_name'] ?? ''))); }
    if ($propertyName === '') { $propertyName = 'Allocation #' . $allocationId; }
    $plotNumber = '';
    if (!empty($alloc['plot_number'])) { $plotNumber = (string)$alloc['plot_number']; }
    elseif (!empty($alloc['building_number'])) { $plotNumber = (string)$alloc['building_number']; }
    elseif (!empty($alloc['unit_number'])) { $plotNumber = (string)$alloc['unit_number']; }
    elseif (!empty($property['code'])) { $plotNumber = (string)$property['code']; }
    elseif (!empty($property['plot_code'])) { $plotNumber = (string)$property['plot_code']; }
    $sqm = '';
    if (!empty($alloc['plot_size'])) { $sqm = (string)$alloc['plot_size']; }
    elseif (!empty($alloc['space_size'])) { $sqm = (string)$alloc['space_size']; }
    elseif (!empty($property['plot_size'])) { $sqm = (string)$property['plot_size']; }
    elseif (!empty($property['area_sqm'])) { $sqm = (string)$property['area_sqm']; }
    elseif (!empty($deal['sqm'])) { $sqm = (string)$deal['sqm']; }
    elseif (!empty($dealMeta['sqm'])) { $sqm = (string)$dealMeta['sqm']; }
    $buildingUse = trim((string)($alloc['building_use'] ?? ''));
    if ($buildingUse === '') { $buildingUse = trim((string)($property['purpose'] ?? ($property['type'] ?? ''))); }
    if ($buildingUse === '') { $buildingUse = 'Residential'; }
    $houseType = trim((string)($alloc['house_type'] ?? ''));
    if ($houseType === '') {
        foreach (['house_type','building_type','title_type','property_type'] as $k) {
            if (!empty($property[$k]) && is_scalar($property[$k])) { $houseType = trim((string)$property[$k]); break; }
        }
    }
    $preferredHouseType = '';
    foreach (['preferred_property','preferred_house_type','house_type','house_type_preference','preferred_type'] as $k) {
        if (!empty($formData[$k]) && is_scalar($formData[$k])) { $preferredHouseType = trim((string)$formData[$k]); break; }
    }
    if ($preferredHouseType !== '') {
        $htLow = strtolower(trim($houseType));
        if ($houseType === '' || $htLow === 'residential') { $houseType = $preferredHouseType; }
    }

    $allocationDate = (string)($alloc['allocation_date'] ?? ($alloc['created_at'] ?? date('Y-m-d')));
    $ref = '';
    if (!empty($alloc['allocation_ref'])) { $ref = (string)$alloc['allocation_ref']; }
    if ($ref === '') { $ref = 'AL-' . str_pad((string)$allocationId, 6, '0', STR_PAD_LEFT); }
    $fileNo = $ref;

    $sections = function_exists('allocationLetterSectionsGet') ? allocationLetterSectionsGet($pdo, $allocationId) : ['details'=>[],'terms'=>[],'guidelines'=>[],'acknowledgement'=>[]];

    $detailsOverride = is_array($sections['details'] ?? null) ? $sections['details'] : [];
    $preparedBy = '';
    $notes = '';

    try {
        if (function_exists('allocationLetterDataGet')) {
            $ld = allocationLetterDataGet($pdo, $allocationId);
            if (!empty($ld)) {
                if (trim((string)($ld['phone'] ?? '')) !== '') { $phone = trim((string)$ld['phone']); }
                if (trim((string)($ld['email'] ?? '')) !== '') { $email = trim((string)$ld['email']); }
                if (trim((string)($ld['address'] ?? '')) !== '') { $address = (string)$ld['address']; }
                if (trim((string)($ld['passport'] ?? '')) !== '') { $passportUrl = trim((string)$ld['passport']); }
                if (trim((string)($ld['plot_no'] ?? '')) !== '') { $plotNumber = trim((string)$ld['plot_no']); }
                if (trim((string)($ld['sqm'] ?? '')) !== '') { $sqm = trim((string)$ld['sqm']); }
                if (trim((string)($ld['house_type'] ?? '')) !== '') { $houseType = trim((string)$ld['house_type']); }
                if (trim((string)($ld['prepared_by'] ?? '')) !== '') { $preparedBy = trim((string)$ld['prepared_by']); }
                if (trim((string)($ld['notes'] ?? '')) !== '') { $notes = trim((string)$ld['notes']); }
            }
        }
    } catch (Throwable $e) {}

    $ov = trim((string)($detailsOverride['full_name'] ?? '')); if ($ov !== '') { $fullName = $ov; }
    $ov = trim((string)($detailsOverride['phone'] ?? '')); if ($ov !== '') { $phone = $ov; }
    $ov = trim((string)($detailsOverride['email'] ?? '')); if ($ov !== '') { $email = $ov; }
    $ovRaw = (string)($detailsOverride['address'] ?? ''); if (trim($ovRaw) !== '') { $address = $ovRaw; }
    $ov = trim((string)($detailsOverride['passport_url'] ?? '')); if ($ov !== '') { $passportUrl = $ov; }
    $ov = trim((string)($detailsOverride['estate_name'] ?? '')); if ($ov !== '') { $estateName = $ov; }
    $ov = trim((string)($detailsOverride['property_name'] ?? '')); if ($ov !== '') { $propertyName = $ov; }
    $ov = trim((string)($detailsOverride['plot_number'] ?? '')); if ($ov !== '') { $plotNumber = $ov; }
    $ov = trim((string)($detailsOverride['sqm'] ?? '')); if ($ov !== '') { $sqm = $ov; }
    $ov = trim((string)($detailsOverride['property_type'] ?? '')); if ($ov !== '') { $buildingUse = $ov; }
    $ov = trim((string)($detailsOverride['house_type'] ?? '')); if ($ov !== '') { $houseType = $ov; }
    $ov = trim((string)($detailsOverride['allocation_date'] ?? '')); if ($ov !== '') { $allocationDate = $ov; }
    $ov = trim((string)($detailsOverride['file_number'] ?? '')); if ($ov !== '') { $fileNo = $ov; }
    $ov = trim((string)($detailsOverride['reference_number'] ?? '')); if ($ov !== '') { $ref = $ov; }
    $ov = trim((string)($detailsOverride['prepared_by'] ?? '')); if ($ov !== '') { $preparedBy = $ov; }
    $ov = trim((string)($detailsOverride['notes'] ?? '')); if ($ov !== '') { $notes = $ov; }

    $baseLandCost = 0.0;
    if (!empty($alloc['total_price'])) { $baseLandCost = (float)parseMoneyValue($alloc['total_price']); }
    if ($baseLandCost <= 0 && !empty($alloc['price'])) { $baseLandCost = (float)parseMoneyValue($alloc['price']); }
    if ($baseLandCost <= 0 && !empty($deal['amount_offered'])) { $baseLandCost = (float)parseMoneyValue($deal['amount_offered']); }
    if ($baseLandCost <= 0 && !empty($property['price'])) { $baseLandCost = (float)parseMoneyValue($property['price']); }

    $billing = [];
    try {
        $st = $pdo->prepare("SELECT * FROM allocation_billing WHERE allocation_id = ? LIMIT 1");
        $st->execute([$allocationId]);
        $billing = $st->fetch(PDO::FETCH_ASSOC) ?: [];
    } catch (Throwable $e) { $billing = []; }
    $dealPlanRaw = trim((string)($deal['payment_plan'] ?? ($dealMeta['payment_plan'] ?? ($dealMeta['plan_type'] ?? ''))));
    $dealPlanMonths = 3;
    if (!empty($deal['custom_months'])) {
        $dealPlanMonths = max(1, (int)$deal['custom_months']);
    } elseif ($dealPlanRaw !== '') {
        if ($dealPlanRaw === 'full' || $dealPlanRaw === 'outright') {
            $dealPlanMonths = 1;
        } elseif ($dealPlanRaw === '3_months') {
            $dealPlanMonths = 3;
        } elseif ($dealPlanRaw === '6_months') {
            $dealPlanMonths = 6;
        } elseif ($dealPlanRaw === '8_months') {
            $dealPlanMonths = 8;
        } elseif (preg_match('/custom_(\d+)_months/i', $dealPlanRaw, $mPlan)) {
            $dealPlanMonths = max(1, (int)$mPlan[1]);
        } elseif ($dealPlanRaw === 'installment') {
            $dealPlanMonths = 3;
        }
    }

    if ($billing) {
        $billUpdatedAt = trim((string)($billing['updated_at'] ?? ''));
        $billMonths = (int)($billing['payment_plan_months'] ?? 0);
        $billLooksDefault = ($billMonths <= 0) || ($billMonths === 3);
        if ($dealPlanMonths > 0 && $billLooksDefault && $billUpdatedAt === '' && $billMonths !== $dealPlanMonths) {
            $billing['payment_plan_months'] = $dealPlanMonths;
            try {
                $pdo->prepare("UPDATE allocation_billing SET payment_plan_months = ?, updated_at = NOW() WHERE allocation_id = ?")->execute([(int)$dealPlanMonths, (int)$allocationId]);
            } catch (Throwable $e) {}
        }
    }

    if (!$billing) {
        $billing = [
            'allocation_id' => $allocationId,
            'company_id' => $companyId,
            'land_cost' => $baseLandCost,
            'infra_mode' => 'percent',
            'infra_percent' => function_exists('getSetting') ? (float)parseMoneyValue(getSetting('infra_lease_percent', '20')) : 20.0,
            'infra_amount' => 0,
            'excavation_fee' => 0,
            'construction_supervision' => 0,
            'approval_fee' => 0,
            'application_form_fee' => function_exists('getSetting') ? (float)parseMoneyValue(getSetting('application_fee', '0')) : 0.0,
            'fence_gate_cost' => 0,
            'carcass_cost' => 0,
            'exterior_finishing_cost' => 0,
            'dpc_cost' => 0,
            'include_fence' => 0,
            'include_carcass' => 0,
            'include_exterior_finishing' => 0,
            'include_dpc' => 0,
            'vat_percent' => function_exists('getSetting') ? (float)parseMoneyValue(getSetting('vat_percent', '7.5')) : 7.5,
            'payment_plan_months' => $dealPlanMonths,
        ];
    }
    $computed = function_exists('computeAllocationBilling') ? computeAllocationBilling($billing) : ['grand_total' => (float)($billing['land_cost'] ?? 0)];

    $coverAsset = function_exists('getSetting') ? ((getSetting('allocation_letter_cover_img_path', '') ?: '') ?: ((getSetting('allocation_letter_cover_path', '') ?: '') ?: (getSetting('allocation_letter_cover_image', '') ?: ''))) : '';
    $page2Asset = function_exists('getSetting') ? ((getSetting('allocation_letter_page2_img_path', '') ?: '') ?: ((getSetting('allocation_letter_page2_path', '') ?: '') ?: (getSetting('allocation_letter_page2_image', '') ?: ''))) : '';

    $ctx['client'] = [
        'id' => $clientId,
        'full_name' => $fullName,
        'phone' => $phone,
        'email' => $email,
        'address' => $address,
        'passport_url' => $passportUrl,
    ];
    $ctx['property'] = [
        'estate_name' => $estateName,
        'property_name' => $propertyName,
        'plot_number' => $plotNumber,
        'sqm' => $sqm,
        'property_type' => $buildingUse,
        'house_type' => $houseType,
    ];
    $ctx['meta'] = [
        'allocation_date' => $allocationDate,
        'file_number' => $fileNo,
        'reference_number' => $ref,
        'prepared_by' => $preparedBy,
        'notes' => $notes,
    ];
    $ctx['billing'] = $billing;
    $ctx['computed'] = $computed;
    $ctx['sections'] = $sections;
    $ctx['assets'] = ['cover' => $coverAsset, 'page2' => $page2Asset];
    return $ctx;
}
$isSuperAdmin = ($roleNorm === 'super_admin');
$isHeadAdmin = function_exists('isHeadAdminRole') ? isHeadAdminRole($roleNorm) : in_array($roleNorm, ['head_admin','admin_head'], true);
$isAdminOfficer = function_exists('isAdminOfficerRole') ? isAdminOfficerRole($roleNorm) : ($roleNorm === 'admin_officer');
$canManageDraft = in_array($roleNorm, ['admin','head_admin','admin_head','admin_officer','super_admin','estate_manager','sales_manager'], true);
$canSendToExecutive = $isSuperAdmin || $isHeadAdmin;
$canReleaseToClient = $isSuperAdmin || $isHeadAdmin;
$canManageApproval = $canManageDraft;
$canReviewExecutiveQueue = in_array($roleNorm, ['executive','chairman_ceo','super_admin'], true);
$viewMode = strtolower(trim((string)($_REQUEST['view'] ?? '')));
$allowedViewModes = ['admin', 'executive'];
if (!in_array($viewMode, $allowedViewModes, true)) {
    $viewMode = $canManageApproval ? 'admin' : 'executive';
}
if ($viewMode === 'admin' && !$canManageDraft) {
    $viewMode = 'executive';
}
if ($viewMode === 'executive' && !$canReviewExecutiveQueue) {
    $viewMode = 'admin';
}
$allocationLettersBaseHref = 'allocation-letters.php?view=' . urlencode($viewMode);
$action = $_GET['action'] ?? '';
$allocId = isset($_GET['allocation_id']) ? (int)$_GET['allocation_id'] : (isset($_GET['alloc_id']) ? (int)$_GET['alloc_id'] : 0);
$statusPreset = strtolower(trim((string)($_GET['status'] ?? 'all')));
if (!in_array($statusPreset, ['all','pending','approved','completed','rejected'], true)) {
    $statusPreset = 'all';
}
$focusedAllocationId = $allocId > 0 ? $allocId : 0;

if ($action === 'send_for_approval' && $_SERVER['REQUEST_METHOD'] === 'GET' && $allocId > 0 && $canSendToExecutive) {
    require_once __DIR__ . '/includes/header.php';
    ?>
    <div class="container-fluid px-4 py-4">
        <div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
            <div>
                <h1 class="h4 mb-1">Send Allocation Letter for Executive Approval</h1>
                <div class="text-muted small">This will move the allocation letter to the executive approval queue.</div>
            </div>
            <a href="allocations.php" class="btn btn-outline-secondary btn-sm">Back</a>
        </div>
        <div class="card shadow-sm border-0">
            <div class="card-body">
                <div class="alert alert-warning mb-3">Confirm you want to send allocation #<?= (int)$allocId ?> to the executive queue.</div>
                <form method="post" action="allocation-letters.php">
                    <input type="hidden" name="form_action" value="send_for_approval">
                    <input type="hidden" name="view" value="admin">
                    <input type="hidden" name="allocation_id" value="<?= (int)$allocId ?>">
                    <button type="submit" class="btn btn-primary"><i class="fa-solid fa-paper-plane me-2"></i>Send for Approval</button>
                    <a href="allocation-letters.php?view=admin" class="btn btn-light border ms-2">Open Workflow</a>
                </form>
            </div>
        </div>
    </div>
    <?php
    require_once __DIR__ . '/includes/footer.php';
    exit;
}
if ($viewMode === 'executive' && $_SERVER['REQUEST_METHOD'] === 'GET' && $allocId > 0 && in_array($action, ['letter_details','terms','guidelines','final_preview'], true)) {
    header('Location: allocation-letters.php?view=executive&action=details&allocation_id=' . (int)$allocId);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $allocId > 0 && $canManageDraft && in_array($action, ['letter_details','terms','guidelines','final_preview'], true)) {
    $ctx = loadAllocationLetterContext($pdo, $allocId, $companyId, $isSuperAdmin);
    if (empty($ctx['client'])) {
        $_SESSION['error_msg'] = 'Allocation not found.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $openPreviewInNewTab = isset($_GET['open_preview']) && (string)$_GET['open_preview'] !== '0';
    $steps = [
        'letter_details' => 'Letter Details',
        'terms' => 'Terms & Conditions',
        'guidelines' => 'Building Guidelines',
        'final_preview' => 'Final Preview',
    ];
    $stepNav = '<div class="d-flex flex-wrap gap-2">';
    foreach ($steps as $k => $label) {
        $active = $k === $action ? 'btn btn-primary' : 'btn btn-outline-primary';
        $stepNav .= '<a class="' . $active . '" href="allocation-letters.php?view=' . urlencode($viewMode) . '&action=' . urlencode($k) . '&allocation_id=' . (int)$allocId . '">' . htmlspecialchars($label) . '</a>';
    }
    $stepNav .= '</div>';
    $backHref = 'allocation-letters.php?view=' . urlencode($viewMode) . '&allocation_id=' . (int)$allocId;
    require_once __DIR__ . '/includes/header.php';
    echo '<div class="container-fluid px-4 py-4">';
    echo '<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">';
    echo '<div><h1 class="h4 mb-1">Allocation Letter — Draft Builder</h1><div class="text-muted small">Allocation #' . str_pad((string)$allocId, 6, '0', STR_PAD_LEFT) . '</div></div>';
    echo '<div class="d-flex flex-wrap gap-2"><a class="btn btn-outline-secondary" href="' . htmlspecialchars($allocationLettersBaseHref) . '">Back to List</a><a class="btn btn-outline-dark" href="' . htmlspecialchars($backHref) . '">Back to Row</a></div>';
    echo '</div>';
    echo $stepNav;
    echo '<div class="row g-3 mt-2">';
    echo '<div class="col-12 col-lg-8">';
    if ($action === 'letter_details') {
        $p = $ctx['property'];
        $c = $ctx['client'];
        $m = $ctx['meta'];
        $passport = allocationLetterInlineImage((string)($c['passport_url'] ?? ''));
        $b = $ctx['computed'];
        echo '<div class="card shadow-sm border-0"><div class="card-body">';
        echo '<h5 class="mb-3">Client</h5>';
        echo '<form method="post" enctype="multipart/form-data" action="allocation-letters.php?view=' . urlencode($viewMode) . '&action=letter_details&allocation_id=' . (int)$allocId . '">';
        echo '<input type="hidden" name="form_action" value="save_letter_details">';
        echo '<input type="hidden" name="allocation_id" value="' . (int)$allocId . '">';
        echo '<div class="row g-3">';
        echo '<div class="col-12 col-md-8">';
        echo '<label class="form-label">Full Name</label><input class="form-control" name="full_name" value="' . htmlspecialchars((string)($c['full_name'] ?? '')) . '">';
        echo '</div>';
        echo '<div class="col-12 col-md-4">';
        echo '<label class="form-label">Phone</label><input class="form-control" name="phone" value="' . htmlspecialchars((string)($c['phone'] ?? '')) . '">';
        echo '</div>';
        echo '<div class="col-12 col-md-6">';
        echo '<label class="form-label">Email</label><input class="form-control" name="email" value="' . htmlspecialchars((string)($c['email'] ?? '')) . '">';
        echo '</div>';
        echo '<div class="col-12">';
        echo '<label class="form-label">Address</label><textarea class="form-control" name="address" rows="3">' . htmlspecialchars((string)($c['address'] ?? '')) . '</textarea>';
        echo '</div>';
        echo '<div class="col-12">';
        echo '<label class="form-label">Passport Photo URL</label><input class="form-control" name="passport_url" value="' . htmlspecialchars((string)($c['passport_url'] ?? '')) . '">';
        echo '</div>';
        echo '<div class="col-12">';
        echo '<label class="form-label">Passport Upload</label><input class="form-control" type="file" name="passport_file" accept="image/*">';
        echo '</div>';
        echo '</div>';
        echo '<hr class="my-4">';
        echo '<h5 class="mb-3">Property</h5>';
        echo '<div class="row g-3">';
        echo '<div class="col-12 col-md-6"><label class="form-label">Estate Name</label><input class="form-control" name="estate_name" value="' . htmlspecialchars((string)($p['estate_name'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-6"><label class="form-label">Property Name</label><input class="form-control" name="property_name" value="' . htmlspecialchars((string)($p['property_name'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">Plot Number</label><input class="form-control" name="plot_number" value="' . htmlspecialchars((string)($p['plot_number'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">SQM</label><input class="form-control" name="sqm" value="' . htmlspecialchars((string)($p['sqm'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">Building Use</label><input class="form-control" name="property_type" value="' . htmlspecialchars((string)($p['property_type'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-6"><label class="form-label">House Type</label><input class="form-control" name="house_type" value="' . htmlspecialchars((string)($p['house_type'] ?? '')) . '"></div>';
        echo '</div>';
        echo '<hr class="my-4">';
        echo '<h5 class="mb-3">Meta</h5>';
        echo '<div class="row g-3">';
        echo '<div class="col-12 col-md-4"><label class="form-label">Allocation Date</label><input class="form-control" name="allocation_date" value="' . htmlspecialchars((string)($m['allocation_date'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">File Number</label><input class="form-control" name="file_number" value="' . htmlspecialchars((string)($m['file_number'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">Reference Number</label><input class="form-control" name="reference_number" value="' . htmlspecialchars((string)($m['reference_number'] ?? '')) . '"></div>';
        echo '<div class="col-12 col-md-6"><label class="form-label">Prepared By</label><input class="form-control" name="prepared_by" value="' . htmlspecialchars((string)($m['prepared_by'] ?? '')) . '"></div>';
        echo '<div class="col-12"><label class="form-label">Notes</label><textarea class="form-control" name="notes" rows="3">' . htmlspecialchars((string)($m['notes'] ?? '')) . '</textarea></div>';
        echo '</div>';
        echo '<div class="d-flex justify-content-between align-items-center mt-4 flex-wrap gap-2">';
        echo '<div class="d-flex gap-2 flex-wrap">';
        echo '<button class="btn btn-primary" type="submit" name="next_action" value="">Save Letter Details</button>';
        echo '<button class="btn btn-outline-primary" type="submit" name="next_action" value="terms">Save &amp; Next: Terms</button>';
        echo '</div>';
        echo '<button class="btn btn-outline-success" type="submit" name="next_action" value="preview">Save &amp; Preview Draft</button>';
        echo '</div>';
        echo '</form>';
        echo '</div></div>';

        echo '<div class="card shadow-sm border-0 mt-3"><div class="card-body">';
        echo '<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">';
        echo '<h5 class="mb-0">Billing Summary</h5>';
        echo '<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#billingModal">Edit Billing</button>';
        echo '</div>';
        echo '<div class="text-muted small mb-2">Billing summary is read-only here. Use Edit Billing to change amounts.</div>';
        echo '<div class="row g-2">';
        echo '<div class="col-12 col-md-4"><div class="p-3 border rounded"><div class="text-muted small">Sub Total</div><div class="fw-bold" id="billingSubTotalText">' . allocationLetterMoney((float)($b['sub_total'] ?? 0)) . '</div></div></div>';
        echo '<div class="col-12 col-md-4"><div class="p-3 border rounded"><div class="text-muted small">VAT</div><div class="fw-bold" id="billingVatText">' . allocationLetterMoney((float)($b['vat'] ?? 0)) . '</div></div></div>';
        echo '<div class="col-12 col-md-4"><div class="p-3 border rounded bg-success bg-opacity-10 border-success"><div class="text-success small">Grand Total</div><div class="fw-bold text-success" id="billingGrandTotalText">' . allocationLetterMoney((float)($b['grand_total'] ?? 0)) . '</div></div></div>';
        echo '</div>';
        echo '</div></div>';

        echo '<div class="modal fade" id="billingModal" tabindex="-1" aria-hidden="true">';
        echo '  <div class="modal-dialog modal-lg modal-dialog-scrollable">';
        echo '    <form class="modal-content" id="billingForm">';
        echo '      <div class="modal-header">';
        echo '        <div><h5 class="modal-title mb-0">Edit Billing</h5><div class="text-muted small">Allocation #' . str_pad((string)$allocId, 6, '0', STR_PAD_LEFT) . '</div></div>';
        echo '        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>';
        echo '      </div>';
        echo '      <div class="modal-body">';
        echo '        <input type="hidden" name="form_action" value="save_billing">';
        echo '        <input type="hidden" name="xhr" value="1">';
        echo '        <input type="hidden" name="allocation_id" value="' . (int)$allocId . '">';
        echo '        <div class="row g-3">';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Land Cost</label><input type="number" step="0.01" min="0" class="form-control" name="land_cost" id="billLandCost"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">VAT %</label><input type="number" step="0.01" min="0" class="form-control" name="vat_percent" id="billVatPercent"></div>';
        echo '          <div class="col-12"><label class="form-label">Infrastructure Charge Mode</label><div class="d-flex gap-3 flex-wrap">';
        echo '            <div class="form-check"><input class="form-check-input" type="radio" name="infra_mode" id="infraModePercent" value="percent" checked><label class="form-check-label" for="infraModePercent">Percentage of base cost</label></div>';
        echo '            <div class="form-check"><input class="form-check-input" type="radio" name="infra_mode" id="infraModeFixed" value="fixed"><label class="form-check-label" for="infraModeFixed">Fixed amount</label></div>';
        echo '          </div></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Infrastructure %</label><input type="number" step="0.01" min="0" class="form-control" name="infra_percent" id="billInfraPercent"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Infrastructure Amount</label><input type="number" step="0.01" min="0" class="form-control" name="infra_amount" id="billInfraAmount"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Excavation Fee</label><input type="number" step="0.01" min="0" class="form-control" name="excavation_fee" id="billExcavationFee"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Construction Supervision</label><input type="number" step="0.01" min="0" class="form-control" name="construction_supervision" id="billSupervision"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Approval Fee</label><input type="number" step="0.01" min="0" class="form-control" name="approval_fee" id="billApprovalFee"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Application Form Fee</label><input type="number" step="0.01" min="0" class="form-control" name="application_form_fee" id="billApplicationFee"></div>';
        echo '          <div class="col-12"><div class="fw-bold small text-uppercase text-muted">Optional Items (show in letter only if enabled)</div></div>';
        echo '          <div class="col-12 col-md-6"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="include_fence" id="includeFence"><label class="form-check-label" for="includeFence">Fence / Gate</label></div><input type="number" step="0.01" min="0" class="form-control" name="fence_gate_cost" id="billFenceCost" placeholder="Fence / Gate cost"></div>';
        echo '          <div class="col-12 col-md-6"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="include_carcass" id="includeCarcass"><label class="form-check-label" for="includeCarcass">Carcass</label></div><input type="number" step="0.01" min="0" class="form-control" name="carcass_cost" id="billCarcassCost" placeholder="Carcass cost"></div>';
        echo '          <div class="col-12 col-md-6"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="include_exterior_finishing" id="includeExterior"><label class="form-check-label" for="includeExterior">Exterior Finishing</label></div><input type="number" step="0.01" min="0" class="form-control" name="exterior_finishing_cost" id="billExteriorCost" placeholder="Exterior finishing cost"></div>';
        echo '          <div class="col-12 col-md-6"><div class="form-check mb-2"><input class="form-check-input" type="checkbox" name="include_dpc" id="includeDpc"><label class="form-check-label" for="includeDpc">DPC</label></div><input type="number" step="0.01" min="0" class="form-control" name="dpc_cost" id="billDpcCost" placeholder="DPC cost"></div>';
        echo '          <div class="col-12 col-md-6"><label class="form-label">Payment Plan (months)</label><input type="number" step="1" min="1" class="form-control" name="payment_plan_months" id="paymentPlanMonths"></div>';
        echo '        </div>';
        echo '        <div class="alert alert-danger mt-3 d-none" id="billingError"></div>';
        echo '        <div class="alert alert-success mt-3 d-none" id="billingSuccess"></div>';
        echo '      </div>';
        echo '      <div class="modal-footer">';
        echo '        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>';
        echo '        <button type="submit" class="btn btn-primary" id="billingSaveBtn">Save Billing</button>';
        echo '      </div>';
        echo '    </form>';
        echo '  </div>';
        echo '</div>';
    } elseif ($action === 'terms') {
        $b = $ctx['billing'];
        $t = $ctx['sections']['terms'] ?? [];
        $page2 = (string)($ctx['assets']['page2'] ?? '');
        $refund = (string)($t['refund_charge_percent'] ?? '');
        echo '<div class="card shadow-sm border-0"><div class="card-body">';
        echo '<h5 class="mb-3">Terms & Conditions (Variables)</h5>';
        echo '<form method="post" action="allocation-letters.php?view=' . urlencode($viewMode) . '&action=terms&allocation_id=' . (int)$allocId . '">';
        echo '<input type="hidden" name="form_action" value="save_terms"><input type="hidden" name="allocation_id" value="' . (int)$allocId . '">';
        echo '<div class="row g-3">';
        echo '<div class="col-12 col-md-4"><label class="form-label">Payment Duration (months)</label><input class="form-control" type="number" min="1" step="1" name="payment_plan_months" value="' . htmlspecialchars((string)($b['payment_plan_months'] ?? 3)) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">VAT %</label><input class="form-control" type="number" min="0" step="0.01" name="vat_percent" value="' . htmlspecialchars((string)($b['vat_percent'] ?? 0)) . '"></div>';
        echo '<div class="col-12 col-md-4"><label class="form-label">Refund Charge %</label><input class="form-control" type="number" min="0" step="0.01" name="refund_charge_percent" value="' . htmlspecialchars($refund) . '"></div>';
        echo '<div class="col-12">';
        echo '<label class="form-label">Infrastructure Charge</label>';
        $mode = strtolower((string)($b['infra_mode'] ?? 'percent'));
        $isFixed = $mode === 'fixed';
        echo '<div class="d-flex gap-3 flex-wrap">';
        echo '<div class="form-check"><input class="form-check-input" type="radio" name="infra_mode" value="percent" ' . ($isFixed ? '' : 'checked') . '><label class="form-check-label">Percent</label></div>';
        echo '<div class="form-check"><input class="form-check-input" type="radio" name="infra_mode" value="fixed" ' . ($isFixed ? 'checked' : '') . '><label class="form-check-label">Fixed</label></div>';
        echo '</div>';
        echo '</div>';
        echo '<div class="col-12 col-md-6"><label class="form-label">Infrastructure %</label><input class="form-control" type="number" min="0" step="0.01" name="infra_percent" value="' . htmlspecialchars((string)($b['infra_percent'] ?? 0)) . '"></div>';
        echo '<div class="col-12 col-md-6"><label class="form-label">Infrastructure Amount</label><input class="form-control" type="number" min="0" step="0.01" name="infra_amount" value="' . htmlspecialchars((string)($b['infra_amount'] ?? 0)) . '"></div>';
        echo '</div>';
        echo '<div class="d-flex justify-content-between align-items-center mt-4 flex-wrap gap-2">';
        echo '<a class="btn btn-outline-primary" href="allocation-letters.php?view=' . urlencode($viewMode) . '&action=letter_details&allocation_id=' . (int)$allocId . '">Back: Details</a>';
        echo '<div class="d-flex gap-2 flex-wrap">';
        echo '<button class="btn btn-primary" type="submit" name="next_action" value="">Save Terms</button>';
        echo '<button class="btn btn-outline-primary" type="submit" name="next_action" value="guidelines">Save &amp; Next: Guidelines</button>';
        echo '</div>';
        echo '<button class="btn btn-outline-success" type="submit" name="next_action" value="preview">Save &amp; Preview Draft</button>';
        echo '</div>';
        echo '</form>';
        echo '</div></div>';
        if ($page2 !== '') {
            echo '<div class="card shadow-sm border-0 mt-3"><div class="card-body">';
            echo '<h6 class="text-muted mb-2">Current Terms Document (Page 2 asset)</h6>';
            $src = htmlspecialchars($page2);
            if (preg_match('/\.pdf$/i', $page2)) {
                echo '<iframe src="' . $src . '" style="width:100%;height:560px;border:1px solid #e5e7eb;border-radius:10px;"></iframe>';
            } else {
                echo '<img src="' . $src . '" alt="Terms" style="width:100%;border:1px solid #e5e7eb;border-radius:10px;">';
            }
            echo '</div></div>';
        }
    } elseif ($action === 'guidelines') {
        $p = $ctx['property'];
        $g = $ctx['sections']['guidelines'] ?? [];
        $notes = (string)($g['notes'] ?? '');
        $page2 = (string)($ctx['assets']['page2'] ?? '');
        echo '<div class="card shadow-sm border-0"><div class="card-body">';
        echo '<h5 class="mb-3">Building Guidelines</h5>';
        echo '<div class="row g-3 mb-2">';
        echo '<div class="col-12 col-md-6"><div class="p-3 border rounded"><div class="text-muted small">Estate Name</div><div class="fw-bold">' . htmlspecialchars((string)($p['estate_name'] ?? '')) . '</div></div></div>';
        echo '<div class="col-12 col-md-6"><div class="p-3 border rounded"><div class="text-muted small">Plot Number</div><div class="fw-bold">' . htmlspecialchars((string)($p['plot_number'] ?? '')) . '</div></div></div>';
        echo '</div>';
        echo '<form method="post" action="allocation-letters.php?view=' . urlencode($viewMode) . '&action=guidelines&allocation_id=' . (int)$allocId . '">';
        echo '<input type="hidden" name="form_action" value="save_guidelines"><input type="hidden" name="allocation_id" value="' . (int)$allocId . '">';
        echo '<label class="form-label">Notes (optional)</label><textarea class="form-control" name="guidelines_notes" rows="4">' . htmlspecialchars($notes) . '</textarea>';
        echo '<div class="d-flex justify-content-between align-items-center mt-4 flex-wrap gap-2">';
        echo '<a class="btn btn-outline-primary" href="allocation-letters.php?view=' . urlencode($viewMode) . '&action=terms&allocation_id=' . (int)$allocId . '">Back: Terms</a>';
        echo '<div class="d-flex gap-2 flex-wrap">';
        echo '<button class="btn btn-primary" type="submit" name="next_action" value="">Save Guidelines</button>';
        echo '<button class="btn btn-outline-success" type="submit" name="next_action" value="preview">Save &amp; Preview Draft</button>';
        echo '</div>';
        echo '</div>';
        echo '</form>';
        echo '</div></div>';
        if ($page2 !== '') {
            echo '<div class="card shadow-sm border-0 mt-3"><div class="card-body">';
            echo '<h6 class="text-muted mb-2">Guidelines Document (Page 2 asset)</h6>';
            $src = htmlspecialchars($page2);
            if (preg_match('/\.pdf$/i', $page2)) {
                echo '<iframe src="' . $src . '" style="width:100%;height:560px;border:1px solid #e5e7eb;border-radius:10px;"></iframe>';
            } else {
                echo '<img src="' . $src . '" alt="Guidelines" style="width:100%;border:1px solid #e5e7eb;border-radius:10px;">';
            }
            echo '</div></div>';
        }
    } elseif ($action === 'final_preview') {
        $previewHref = 'allocation-letters.php?view=' . urlencode($viewMode) . '&action=preview&allocation_id=' . (int)$allocId . '&force=1';
        echo '<div class="alert alert-info">Opening letter preview in a new tab...</div>';
        echo '<a class="btn btn-primary" target="_blank" rel="noopener" href="' . htmlspecialchars($previewHref, ENT_QUOTES) . '">Open Preview</a>';
        echo '<script>(function(){try{window.open(' . json_encode($previewHref) . ', "_blank", "noopener");}catch(e){}})();</script>';
        echo '</div>';
        require_once __DIR__ . '/includes/footer.php';
        exit;
    }
    echo '</div>';
    echo '<div class="col-12 col-lg-4">';
    echo '<div class="card shadow-sm border-0"><div class="card-body">';
    echo '<div class="fw-bold mb-2">Quick Summary</div>';
    echo '<div class="small text-muted mb-1">Client</div><div class="fw-bold mb-2">' . htmlspecialchars((string)($ctx['client']['full_name'] ?? '')) . '</div>';
    echo '<div class="small text-muted mb-1">Estate</div><div class="fw-bold mb-2">' . htmlspecialchars((string)($ctx['property']['estate_name'] ?? '')) . '</div>';
    echo '<div class="small text-muted mb-1">Plot</div><div class="fw-bold mb-2">' . htmlspecialchars((string)($ctx['property']['plot_number'] ?? '')) . '</div>';
    echo '<div class="small text-muted mb-1">Grand Total</div><div class="fw-bold text-success">' . allocationLetterMoney((float)($ctx['computed']['grand_total'] ?? 0)) . '</div>';
    echo '</div></div>';
    echo '</div>';
    echo '</div>';
    echo '</div>';
    echo '<script>(function(){';
    echo 'const openPreview=' . ($openPreviewInNewTab ? 'true' : 'false') . ';';
    echo 'if(openPreview){';
    echo '  try{window.open(' . json_encode('allocation-letters.php?view=' . $viewMode . '&action=preview&allocation_id=' . (int)$allocId . '&force=1') . ',"_blank","noopener");}catch(e){}';
    echo '  try{const u=new URL(window.location.href);u.searchParams.delete("open_preview");window.history.replaceState(null,"",u.toString());}catch(e){}';
    echo '}';
    echo 'const initBilling=' . json_encode($ctx['billing'] ?? []) . ';';
    echo 'const initComputed=' . json_encode($ctx['computed'] ?? []) . ';';
    echo 'function moneyNumber(v){const n=Number(v);return Number.isFinite(n)?n:0;}';
    echo 'function syncInfraInputs(){const fixed=document.getElementById("infraModeFixed");const percent=document.getElementById("billInfraPercent");const amount=document.getElementById("billInfraAmount");if(!fixed||!percent||!amount)return;const isFixed=fixed.checked;percent.disabled=isFixed;amount.disabled=!isFixed;}';
    echo 'function populateBilling(){';
    echo '  const lc=document.getElementById("billLandCost"); if(lc) lc.value=moneyNumber(initBilling.land_cost ?? initComputed.land_cost ?? 0);';
    echo '  const vat=document.getElementById("billVatPercent"); if(vat) vat.value=moneyNumber(initBilling.vat_percent ?? initComputed.vat_percent ?? 7.5);';
    echo '  const mode=String(initBilling.infra_mode || "percent").toLowerCase();';
    echo '  const fixed=document.getElementById("infraModeFixed"); const pct=document.getElementById("infraModePercent");';
    echo '  if(fixed&&pct){fixed.checked=(mode==="fixed");pct.checked=(mode!=="fixed");}';
    echo '  const ip=document.getElementById("billInfraPercent"); if(ip) ip.value=moneyNumber(initBilling.infra_percent ?? 20);';
    echo '  const ia=document.getElementById("billInfraAmount"); if(ia) ia.value=moneyNumber(initBilling.infra_amount ?? 0);';
    echo '  const ex=document.getElementById("billExcavationFee"); if(ex) ex.value=moneyNumber(initBilling.excavation_fee ?? 0);';
    echo '  const sup=document.getElementById("billSupervision"); if(sup) sup.value=moneyNumber(initBilling.construction_supervision ?? 0);';
    echo '  const ap=document.getElementById("billApprovalFee"); if(ap) ap.value=moneyNumber(initBilling.approval_fee ?? 0);';
    echo '  const af=document.getElementById("billApplicationFee"); if(af) af.value=moneyNumber(initBilling.application_form_fee ?? 0);';
    echo '  const f=document.getElementById("includeFence"); if(f) f.checked=Number(initBilling.include_fence ?? 0)===1;';
    echo '  const c=document.getElementById("includeCarcass"); if(c) c.checked=Number(initBilling.include_carcass ?? 0)===1;';
    echo '  const e=document.getElementById("includeExterior"); if(e) e.checked=Number(initBilling.include_exterior_finishing ?? initBilling.include_exterior_finishing_cost ?? 0)===1;';
    echo '  const d=document.getElementById("includeDpc"); if(d) d.checked=Number(initBilling.include_dpc ?? 0)===1;';
    echo '  const fc=document.getElementById("billFenceCost"); if(fc) fc.value=moneyNumber(initBilling.fence_gate_cost ?? 0);';
    echo '  const cc=document.getElementById("billCarcassCost"); if(cc) cc.value=moneyNumber(initBilling.carcass_cost ?? 0);';
    echo '  const ec=document.getElementById("billExteriorCost"); if(ec) ec.value=moneyNumber(initBilling.exterior_finishing_cost ?? 0);';
    echo '  const dc=document.getElementById("billDpcCost"); if(dc) dc.value=moneyNumber(initBilling.dpc_cost ?? 0);';
    echo '  const pm=document.getElementById("paymentPlanMonths"); if(pm) pm.value=String(Math.max(1, Number(initBilling.payment_plan_months ?? 3) || 3));';
    echo '  syncInfraInputs();';
    echo '}';
    echo 'function refreshBillingSummary(){';
    echo '  fetch("ajax_get_allocation_details.php?id=" + encodeURIComponent(' . json_encode((string)$allocId) . '), {credentials:"same-origin"})';
    echo '    .then(r=>r.json()).then(function(data){';
    echo '      if(!data||!data.success) return;';
    echo '      const cb=data.cost_breakdown||{};';
    echo '      const fmt=function(v){const n=Number(v)||0; return "₦"+n.toLocaleString(undefined,{minimumFractionDigits:0,maximumFractionDigits:2});};';
    echo '      const st=document.getElementById("billingSubTotalText"); if(st&&cb.sub_total!=null) st.textContent=fmt(cb.sub_total);';
    echo '      const vt=document.getElementById("billingVatText"); if(vt&&cb.vat!=null) vt.textContent=fmt(cb.vat);';
    echo '      const gt=document.getElementById("billingGrandTotalText"); if(gt&&cb.grand_total!=null) gt.textContent=fmt(cb.grand_total);';
    echo '    }).catch(function(){});';
    echo '}';
    echo 'document.addEventListener("DOMContentLoaded", function(){';
    echo '  populateBilling();';
    echo '  document.querySelectorAll("input[name=infra_mode]").forEach(function(r){r.addEventListener("change", syncInfraInputs);});';
    echo '  const form=document.getElementById("billingForm");';
    echo '  if(form){form.addEventListener("submit", async function(ev){';
    echo '    ev.preventDefault();';
    echo '    const err=document.getElementById("billingError"); const ok=document.getElementById("billingSuccess");';
    echo '    if(err){err.classList.add("d-none"); err.textContent="";}';
    echo '    if(ok){ok.classList.add("d-none"); ok.textContent="";}';
    echo '    const btn=document.getElementById("billingSaveBtn"); const original=btn?btn.innerHTML:"";';
    echo '    if(btn){btn.disabled=true; btn.innerHTML="Saving...";}';
    echo '    try{';
    echo '      const fd=new FormData(form);';
    echo '      const res=await fetch("allocation-letters.php", {method:"POST", body:fd, credentials:"same-origin"});';
    echo '      const json=await res.json().catch(function(){return null;});';
    echo '      if(!json||!json.success){const msg=(json&&json.error)?json.error:"Failed to save."; if(err){err.textContent=msg; err.classList.remove("d-none");} return;}';
    echo '      if(ok){ok.textContent="Billing saved."; ok.classList.remove("d-none");}';
    echo '      refreshBillingSummary();';
    echo '      try{const url=' . json_encode('allocation-letters.php?view=' . $viewMode . '&action=preview&allocation_id=' . (int)$allocId . '&force=1') . '; window.open(url,"_blank","noopener");}catch(e){}';
    echo '      try{const modalEl=document.getElementById("billingModal"); if(modalEl && window.bootstrap && window.bootstrap.Modal){ const bs=window.bootstrap.Modal.getOrCreateInstance(modalEl); bs.hide(); }}catch(e){}';
    echo '    }catch(ex){ if(err){err.textContent="Failed to save."; err.classList.remove("d-none");} }finally{ if(btn){btn.disabled=false; btn.innerHTML=original;} }';
    echo '  });}';
    echo '});';
    echo '})();</script>';
    require_once __DIR__ . '/includes/footer.php';
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'send_for_approval') {
    $isAjax = (string)($_POST['xhr'] ?? '') === '1';
    if (!$canSendToExecutive) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'message' => '', 'error' => 'Permission denied.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Permission denied.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $sendAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($sendAllocId <= 0) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'message' => '', 'error' => 'Invalid allocation selected for approval.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Invalid allocation selected for approval.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $okMsg = '';
    $errMsg = '';
    try {
        $startedTransaction = method_exists($pdo, 'inTransaction') ? !$pdo->inTransaction() : true;
        if ($startedTransaction) {
            $pdo->beginTransaction();
        }
        $generatedPattern = __DIR__ . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'documents' . DIRECTORY_SEPARATOR . 'generated' . DIRECTORY_SEPARATOR . 'Allocation_Letter_' . $sendAllocId . '_*';
        $hasDocuments = false;
        try { $hasDocuments = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0; } catch (Throwable $e) {}
        $generatedFiles = glob($generatedPattern) ?: [];
        $hasLetterDraftFile = !empty($generatedFiles);
        $billingUpdatedAt = '';
        try {
            if (function_exists('tableHasColumn') && tableHasColumn('allocation_billing', 'updated_at')) {
                $stBu = $pdo->prepare("SELECT updated_at FROM allocation_billing WHERE allocation_id = ? LIMIT 1");
                $stBu->execute([$sendAllocId]);
                $billingUpdatedAt = (string)($stBu->fetchColumn() ?: '');
            }
        } catch (Throwable $e) {}
        if ($billingUpdatedAt !== '' && $hasLetterDraftFile) {
            $latestDraftTime = 0;
            foreach ($generatedFiles as $fp) {
                $t = @filemtime($fp);
                if ($t && $t > $latestDraftTime) { $latestDraftTime = $t; }
            }
            $billTime = strtotime($billingUpdatedAt);
            if ($billTime && $latestDraftTime > 0 && $billTime > $latestDraftTime) {
                $hasLetterDraftFile = false;
            }
        }
        $hasLetterDraftRecord = false;
        if ($hasDocuments) {
            $docSql = "SELECT COUNT(*) FROM documents WHERE 1=1";
            $docParams = [];
            if (colx('documents','type')) { $docSql .= " AND type = ?"; $docParams[] = 'allocation_letter'; }
            if (colx('documents','allocation_id')) {
                $docSql .= " AND allocation_id = ?";
                $docParams[] = $sendAllocId;
            } else {
                $docSql .= " AND file_path LIKE ?";
                $docParams[] = '%Allocation_Letter_' . $sendAllocId . '_%';
            }
            if (!$isSuperAdmin && $companyId && colx('documents','company_id')) { $docSql .= " AND (company_id = ? OR company_id IS NULL)"; $docParams[] = $companyId; }
            $stDoc = $pdo->prepare($docSql);
            $stDoc->execute($docParams);
            $hasLetterDraftRecord = ((int)$stDoc->fetchColumn()) > 0;
        }
        $hasLetterDraft = $hasLetterDraftFile || $hasLetterDraftRecord;
        if (!$hasLetterDraft) {
            $generatedId = 0;
            try {
                $generator = new DocGenerator($pdo);
                $generatedId = (int)$generator->generateAllocationLetter($sendAllocId, $uid);
            } catch (Throwable $generatorError) {
                $generatedId = 0;
            }
            $generatedFiles = glob($generatedPattern) ?: [];
            $hasLetterDraftFile = !empty($generatedFiles);
            if ($hasDocuments && !$hasLetterDraftRecord) {
                $docSql = "SELECT COUNT(*) FROM documents WHERE 1=1";
                $docParams = [];
                if (colx('documents','type')) { $docSql .= " AND type = ?"; $docParams[] = 'allocation_letter'; }
                if (colx('documents','allocation_id')) {
                    $docSql .= " AND allocation_id = ?";
                    $docParams[] = $sendAllocId;
                } else {
                    $docSql .= " AND file_path LIKE ?";
                    $docParams[] = '%Allocation_Letter_' . $sendAllocId . '_%';
                }
                if (!$isSuperAdmin && $companyId && colx('documents','company_id')) { $docSql .= " AND (company_id = ? OR company_id IS NULL)"; $docParams[] = $companyId; }
                $stDoc = $pdo->prepare($docSql);
                $stDoc->execute($docParams);
                $hasLetterDraftRecord = ((int)$stDoc->fetchColumn()) > 0;
            }
            $hasLetterDraft = $hasLetterDraftFile || $hasLetterDraftRecord || $generatedId > 0;
            if (!$hasLetterDraft) {
                throw new RuntimeException('Unable to generate the allocation letter draft.');
            }
        }
        if ($hasDocuments && $hasLetterDraftRecord && colx('documents','status')) {
            $documentQueueStatus = pickColumnToken($pdo, 'documents', 'status', ['pending_executive_approval', 'review', 'pending', 'approved', 'draft']);
            $set = ["status = ?"];
            $params = [$documentQueueStatus];
            if (colx('documents','updated_at')) {
                $set[] = "updated_at = ?";
                $params[] = date('Y-m-d H:i:s');
            }
            $sqlDocUpdate = "UPDATE documents SET " . implode(', ', $set) . " WHERE 1=1";
            if (colx('documents','type')) { $sqlDocUpdate .= " AND type = ?"; $params[] = 'allocation_letter'; }
            if (colx('documents','allocation_id')) {
                $sqlDocUpdate .= " AND allocation_id = ?";
                $params[] = $sendAllocId;
            } else {
                $sqlDocUpdate .= " AND file_path LIKE ?";
                $params[] = '%Allocation_Letter_' . $sendAllocId . '_%';
            }
            if (!$isSuperAdmin && $companyId && colx('documents','company_id')) {
                $sqlDocUpdate .= " AND (company_id = ? OR company_id IS NULL)";
                $params[] = $companyId;
            }
            $updDoc = $pdo->prepare($sqlDocUpdate);
            $updDoc->execute($params);
        }
        if (colx('allocations','status')) {
            $allocationQueueStatus = pickColumnToken($pdo, 'allocations', 'status', ['pending_executive_approval', 'pending']);
            $setAlloc = ["status = ?"];
            $paramsAlloc = [$allocationQueueStatus];
            if (colx('allocations','updated_at')) {
                $setAlloc[] = "updated_at = ?";
                $paramsAlloc[] = date('Y-m-d H:i:s');
            }
            $sqlAlloc = "UPDATE allocations SET " . implode(', ', $setAlloc) . " WHERE id = ?";
            $paramsAlloc[] = $sendAllocId;
            if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) {
                $sqlAlloc .= " AND (company_id = ? OR company_id IS NULL)";
                $paramsAlloc[] = $companyId;
            }
            $updAlloc = $pdo->prepare($sqlAlloc);
            $updAlloc->execute($paramsAlloc);
        }
        try {
            if (function_exists('allocationLetterDataUpsert')) {
                allocationLetterDataUpsert($pdo, (int)$sendAllocId, [
                    'status' => 'pending_chairman_approval',
                    'sent_to_chairman_at' => date('Y-m-d H:i:s'),
                    'chairman_decision' => null,
                    'chairman_comment' => null,
                    'chairman_decided_by' => null,
                    'chairman_decided_at' => null,
                ]);
            }
        } catch (Throwable $e) {}
        if ($startedTransaction && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) {
            $pdo->commit();
        }
        if (function_exists('logActivity') && $uid > 0) {
            try {
                logActivity($uid, 'ALLOCATION_SENT_FOR_EXECUTIVE_APPROVAL', json_encode(['allocation_id' => $sendAllocId, 'company_id' => $companyId]));
            } catch (Throwable $e) {}
        }
        try {
            if (function_exists('ap_user_ids_by_roles') && function_exists('sendNotification')) {
                $ids = ap_user_ids_by_roles($pdo, ['chairman_ceo','executive'], $companyId ? (int)$companyId : null);
                foreach ($ids as $eid) {
                    sendNotification((int)$eid, 'allocation_letter_review', 'Allocation #' . (int)$sendAllocId . ' is pending executive approval.', $pdo);
                }
            }
        } catch (Throwable $e) {}
        $okMsg = 'Allocation letter sent to the executive dashboard for approval.';
    } catch (Throwable $e) {
        try {
            if (!empty($startedTransaction) && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
        } catch (Throwable $rollbackError) {}
        error_log('Allocation letter send_for_approval failed for allocation #' . $sendAllocId . ': ' . $e->getMessage());
        $errMsg = 'Failed to send allocation letter for approval.';
    }
    if ($isAjax) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['success' => $errMsg === '', 'message' => $okMsg, 'error' => $errMsg]);
        exit;
    }
    if ($errMsg !== '') {
        $_SESSION['error_msg'] = $errMsg;
    } else {
        $_SESSION['success_msg'] = $okMsg;
    }
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'submit_draft') {
    $isAjax = (string)($_POST['xhr'] ?? '') === '1';
    if (!$isAdminOfficer && !$isSuperAdmin) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'message' => '', 'error' => 'Permission denied.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Permission denied.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'error' => 'Invalid allocation selected.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $ok = false;
    $err = '';
    try {
        $startedTransaction = method_exists($pdo, 'inTransaction') ? !$pdo->inTransaction() : true;
        if ($startedTransaction) { $pdo->beginTransaction(); }
        $st = $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?");
        $st->execute(['pending_head_admin_review', $targetAllocId]);
        try {
            if (function_exists('allocationLetterDataUpsert')) {
                allocationLetterDataUpsert($pdo, (int)$targetAllocId, [
                    'status' => 'pending_head_admin_review',
                    'submitted_to_head_admin_at' => date('Y-m-d H:i:s'),
                ]);
            }
        } catch (Throwable $e) {}
        if ($startedTransaction && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) { $pdo->commit(); }
        $ok = true;
        try {
            if (function_exists('ap_user_ids_by_roles') && function_exists('sendNotification')) {
                $ids = ap_user_ids_by_roles($pdo, ['head_admin','admin'], $companyId ? (int)$companyId : null);
                foreach ($ids as $hid) {
                    sendNotification((int)$hid, 'allocation_letter_draft_submitted', 'Allocation #' . (int)$targetAllocId . ' draft is ready for review.', $pdo);
                }
            }
        } catch (Throwable $e) {}
    } catch (Throwable $e) {
        $err = 'Failed to submit draft.';
        try { if (!empty($startedTransaction) && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) { $pdo->rollBack(); } } catch (Throwable $x) {}
    }
    if ($isAjax) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['success' => $ok, 'error' => $ok ? '' : $err]);
        exit;
    }
    $_SESSION[$ok ? 'success_msg' : 'error_msg'] = $ok ? 'Draft submitted to Head Admin.' : $err;
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'return_for_correction') {
    $isAjax = (string)($_POST['xhr'] ?? '') === '1';
    if (!$isHeadAdmin && !$isSuperAdmin) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'message' => '', 'error' => 'Permission denied.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Permission denied.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'error' => 'Invalid allocation selected.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $note = trim((string)($_POST['note'] ?? ''));
    $ok = false;
    $err = '';
    try {
        $startedTransaction = method_exists($pdo, 'inTransaction') ? !$pdo->inTransaction() : true;
        if ($startedTransaction) { $pdo->beginTransaction(); }
        $st = $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?");
        $st->execute(['returned_for_correction', $targetAllocId]);
        try {
            if (function_exists('allocationLetterDataUpsert')) {
                allocationLetterDataUpsert($pdo, (int)$targetAllocId, [
                    'status' => 'returned_for_correction',
                    'head_admin_comment' => $note,
                    'returned_for_correction_at' => date('Y-m-d H:i:s'),
                ]);
            }
        } catch (Throwable $e) {}
        if ($startedTransaction && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) { $pdo->commit(); }
        $ok = true;
        try {
            if (function_exists('ap_user_ids_by_roles') && function_exists('sendNotification')) {
                $ids = ap_user_ids_by_roles($pdo, ['admin_officer'], $companyId ? (int)$companyId : null);
                foreach ($ids as $oid) {
                    sendNotification((int)$oid, 'allocation_letter_returned', 'Allocation #' . (int)$targetAllocId . ' was returned for correction.' . ($note !== '' ? (' Note: ' . $note) : ''), $pdo);
                }
            }
        } catch (Throwable $e) {}
    } catch (Throwable $e) {
        $err = 'Failed to return for correction.';
        try { if (!empty($startedTransaction) && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) { $pdo->rollBack(); } } catch (Throwable $x) {}
    }
    if ($isAjax) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['success' => $ok, 'error' => $ok ? '' : $err]);
        exit;
    }
    $_SESSION[$ok ? 'success_msg' : 'error_msg'] = $ok ? 'Returned for correction.' : $err;
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'save_billing' && $canManageApproval) {
    $isAjax = (string)($_POST['xhr'] ?? '') === '1';
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'error' => 'Invalid allocation selected.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $roleNormSave = strtolower((string)$role);
    $ok = false;
    $err = '';
    try {
        if (function_exists('ensureAllocationBillingTable')) { ensureAllocationBillingTable($pdo); }
        $payload = [
            'land_cost' => parseMoneyValue($_POST['land_cost'] ?? 0),
            'infra_mode' => strtolower(trim((string)($_POST['infra_mode'] ?? 'percent'))),
            'infra_percent' => parseMoneyValue($_POST['infra_percent'] ?? 0),
            'infra_amount' => parseMoneyValue($_POST['infra_amount'] ?? 0),
            'excavation_fee' => parseMoneyValue($_POST['excavation_fee'] ?? 0),
            'construction_supervision' => parseMoneyValue($_POST['construction_supervision'] ?? 0),
            'approval_fee' => parseMoneyValue($_POST['approval_fee'] ?? 0),
            'application_form_fee' => parseMoneyValue($_POST['application_form_fee'] ?? 0),
            'fence_gate_cost' => parseMoneyValue($_POST['fence_gate_cost'] ?? 0),
            'carcass_cost' => parseMoneyValue($_POST['carcass_cost'] ?? 0),
            'exterior_finishing_cost' => parseMoneyValue($_POST['exterior_finishing_cost'] ?? 0),
            'dpc_cost' => parseMoneyValue($_POST['dpc_cost'] ?? 0),
            'include_fence' => isset($_POST['include_fence']) ? 1 : 0,
            'include_carcass' => isset($_POST['include_carcass']) ? 1 : 0,
            'include_exterior_finishing' => isset($_POST['include_exterior_finishing']) ? 1 : 0,
            'include_dpc' => isset($_POST['include_dpc']) ? 1 : 0,
            'vat_percent' => parseMoneyValue($_POST['vat_percent'] ?? 0),
            'payment_plan_months' => max(1, (int)($_POST['payment_plan_months'] ?? 3)),
        ];
        if (!in_array($payload['infra_mode'], ['percent','fixed'], true)) { $payload['infra_mode'] = 'percent'; }

        $cols = [
            'allocation_id','company_id',
            'land_cost','infra_mode','infra_percent','infra_amount',
            'excavation_fee','construction_supervision','approval_fee','application_form_fee',
            'fence_gate_cost','carcass_cost','exterior_finishing_cost','dpc_cost',
            'include_fence','include_carcass','include_exterior_finishing','include_dpc',
            'vat_percent','payment_plan_months','updated_by','updated_at'
        ];
        $vals = [
            $targetAllocId, $companyId,
            $payload['land_cost'], $payload['infra_mode'], $payload['infra_percent'], $payload['infra_amount'],
            $payload['excavation_fee'], $payload['construction_supervision'], $payload['approval_fee'], $payload['application_form_fee'],
            $payload['fence_gate_cost'], $payload['carcass_cost'], $payload['exterior_finishing_cost'], $payload['dpc_cost'],
            $payload['include_fence'], $payload['include_carcass'], $payload['include_exterior_finishing'], $payload['include_dpc'],
            $payload['vat_percent'], $payload['payment_plan_months'],
            $uid, date('Y-m-d H:i:s')
        ];
        $placeholders = rtrim(str_repeat('?,', count($cols)), ',');
        $updates = [];
        foreach (array_slice($cols, 2) as $c) { $updates[] = $c . " = VALUES(" . $c . ")"; }
        $sql = "INSERT INTO allocation_billing (" . implode(',', $cols) . ") VALUES ($placeholders) ON DUPLICATE KEY UPDATE " . implode(',', $updates);
        $st = $pdo->prepare($sql);
        $st->execute($vals);
        $ok = true;
        if ($isAdminOfficer && colx('allocations','status')) {
            try {
                $stCur = $pdo->prepare("SELECT status FROM allocations WHERE id = ? LIMIT 1");
                $stCur->execute([$targetAllocId]);
                $cur = strtolower(trim((string)($stCur->fetchColumn() ?: '')));
                if ($cur === '' || in_array($cur, ['pending','draft_prepared','returned_for_correction'], true)) {
                    $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?")->execute(['draft_prepared', $targetAllocId]);
                }
            } catch (Throwable $e) {}
        }
        try {
            if (function_exists('computeAllocationBilling') && function_exists('allocationLetterDataUpsert')) {
                $computed = computeAllocationBilling($payload);
                $statusDraft = 'draft';
                $versionNo = 1;
                try {
                    if (function_exists('allocationLetterDataGet')) {
                        $existing = allocationLetterDataGet($pdo, (int)$targetAllocId);
                        $cur = strtolower((string)($existing['status'] ?? ''));
                        $versionNo = (int)($existing['version_no'] ?? 1);
                        if ($cur !== '' && !in_array($cur, ['draft','rejected','changes_requested'], true)) {
                            $statusDraft = $cur;
                        }
                        if (in_array($cur, ['chairman_approved','sent_to_client','viewed_by_client','accepted_by_client','completed'], true)) {
                            $versionNo = max(1, $versionNo) + 1;
                        }
                    }
                } catch (Throwable $e) {}
                allocationLetterDataUpsert($pdo, $targetAllocId, [
                    'status' => $statusDraft,
                    'version_no' => $versionNo,
                    'land_cost' => (float)($computed['land_cost'] ?? $payload['land_cost'] ?? 0),
                    'infrastructure' => (float)($computed['infrastructure_charge'] ?? 0),
                    'excavation' => (float)($computed['excavation_fee'] ?? 0),
                    'supervision' => (float)($computed['construction_supervision'] ?? 0),
                    'approval_fee' => (float)($computed['approval_fee'] ?? 0),
                    'vat' => (float)($computed['vat'] ?? 0),
                    'total' => (float)($computed['grand_total'] ?? 0),
                ]);
            }
        } catch (Throwable $e) {}

        try {
            $pdo->query("DESCRIBE audit_logs");
            $cols = [];
            $vals = [];
            if (colx('audit_logs','entity_type')) { $cols[] = 'entity_type'; $vals[] = 'allocation_billing'; }
            if (colx('audit_logs','entity_id')) { $cols[] = 'entity_id'; $vals[] = $targetAllocId; }
            if (colx('audit_logs','action')) { $cols[] = 'action'; $vals[] = 'allocation_billing_update'; }
            if (colx('audit_logs','reason')) { $cols[] = 'reason'; $vals[] = json_encode($payload); }
            if (colx('audit_logs','details') && !in_array('reason', $cols, true)) { $cols[] = 'details'; $vals[] = json_encode($payload); }
            if (colx('audit_logs','changed_by')) { $cols[] = 'changed_by'; $vals[] = $uid; }
            if (colx('audit_logs','user_id') && !in_array('changed_by', $cols, true)) { $cols[] = 'user_id'; $vals[] = $uid; }
            if (colx('audit_logs','ip_address')) { $cols[] = 'ip_address'; $vals[] = $_SERVER['REMOTE_ADDR'] ?? ''; }
            if (colx('audit_logs','created_at')) { $cols[] = 'created_at'; $vals[] = date('Y-m-d H:i:s'); }
            if (!empty($cols)) {
                $ph = rtrim(str_repeat('?,', count($cols)), ',');
                $pdo->prepare("INSERT INTO audit_logs (" . implode(',', $cols) . ") VALUES ($ph)")->execute($vals);
            }
        } catch (Throwable $e) {}
    } catch (Throwable $e) {
        $ok = false;
        $err = 'Failed to save billing.';
    }
    if ($isAjax) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['success' => $ok, 'error' => $err]);
        exit;
    }
    if (!$ok) { $_SESSION['error_msg'] = $err; } else { $_SESSION['success_msg'] = 'Billing saved.'; }
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'save_letter_details' && $canManageApproval) {
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $passportUrl = trim((string)($_POST['passport_url'] ?? ''));
    if (isset($_FILES['passport_file']) && (int)($_FILES['passport_file']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) {
        $file = $_FILES['passport_file'];
        $ext = strtolower(pathinfo((string)($file['name'] ?? ''), PATHINFO_EXTENSION));
        if (!in_array($ext, ['jpg','jpeg','png','webp'], true)) {
            $_SESSION['error_msg'] = 'Invalid passport file type. Use JPG, PNG, or WEBP.';
            header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=letter_details&allocation_id=' . (int)$targetAllocId);
            exit;
        }
        $dirRel = 'uploads/letters/passports';
        $dirAbs = __DIR__ . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'letters' . DIRECTORY_SEPARATOR . 'passports';
        if (!is_dir($dirAbs)) { @mkdir($dirAbs, 0777, true); }
        $safe = 'alloc_' . (int)$targetAllocId . '_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
        $destAbs = $dirAbs . DIRECTORY_SEPARATOR . $safe;
        if (@move_uploaded_file((string)$file['tmp_name'], $destAbs)) {
            if ($ext === 'webp' && function_exists('imagecreatefromwebp') && function_exists('imagejpeg')) {
                $im = @imagecreatefromwebp($destAbs);
                if ($im) {
                    $safeJpg = preg_replace('/\.webp$/i', '.jpg', $safe);
                    $destJpgAbs = $dirAbs . DIRECTORY_SEPARATOR . $safeJpg;
                    $okJpg = @imagejpeg($im, $destJpgAbs, 92);
                    if (function_exists('imagedestroy')) { @imagedestroy($im); }
                    if ($okJpg && is_file($destJpgAbs)) {
                        @unlink($destAbs);
                        $passportUrl = $dirRel . '/' . $safeJpg;
                    } else {
                        $passportUrl = $dirRel . '/' . $safe;
                    }
                } else {
                    $passportUrl = $dirRel . '/' . $safe;
                }
            } else {
                $passportUrl = $dirRel . '/' . $safe;
            }
        }
    }
    $data = [
        'full_name' => trim((string)($_POST['full_name'] ?? '')),
        'phone' => trim((string)($_POST['phone'] ?? '')),
        'email' => trim((string)($_POST['email'] ?? '')),
        'address' => (string)($_POST['address'] ?? ''),
        'passport_url' => $passportUrl,
        'estate_name' => trim((string)($_POST['estate_name'] ?? '')),
        'property_name' => trim((string)($_POST['property_name'] ?? '')),
        'plot_number' => trim((string)($_POST['plot_number'] ?? '')),
        'sqm' => trim((string)($_POST['sqm'] ?? '')),
        'property_type' => trim((string)($_POST['property_type'] ?? '')),
        'house_type' => trim((string)($_POST['house_type'] ?? '')),
        'allocation_date' => trim((string)($_POST['allocation_date'] ?? '')),
        'file_number' => trim((string)($_POST['file_number'] ?? '')),
        'reference_number' => trim((string)($_POST['reference_number'] ?? '')),
        'prepared_by' => trim((string)($_POST['prepared_by'] ?? '')),
        'notes' => trim((string)($_POST['notes'] ?? '')),
    ];
    $ok = function_exists('allocationLetterSectionsSave') ? allocationLetterSectionsSave($pdo, $targetAllocId, $companyId, 'details', $data, $uid) : false;
    try {
        if ($ok) {
            $sets = [];
            $vals = [];
            if (colx('allocations','plot_number')) { $sets[] = "plot_number = ?"; $vals[] = ($data['plot_number'] !== '' ? $data['plot_number'] : null); }
            if (colx('allocations','plot_size')) { $sets[] = "plot_size = ?"; $vals[] = ($data['sqm'] !== '' ? $data['sqm'] : null); }
            if (colx('allocations','building_use')) { $sets[] = "building_use = ?"; $vals[] = ($data['property_type'] !== '' ? $data['property_type'] : null); }
            if (colx('allocations','house_type')) { $sets[] = "house_type = ?"; $vals[] = ($data['house_type'] !== '' ? $data['house_type'] : null); }
            if (colx('allocations','allocation_date') && $data['allocation_date'] !== '') { $sets[] = "allocation_date = ?"; $vals[] = $data['allocation_date']; }
            if (colx('allocations','building_number')) { $sets[] = "building_number = CASE WHEN (building_number IS NULL OR TRIM(building_number) = '') THEN ? ELSE building_number END"; $vals[] = ($data['plot_number'] !== '' ? $data['plot_number'] : null); }
            if (colx('allocations','space_size')) { $sets[] = "space_size = CASE WHEN (space_size IS NULL OR TRIM(space_size) = '') THEN ? ELSE space_size END"; $vals[] = ($data['sqm'] !== '' ? $data['sqm'] : null); }
            if (!empty($sets)) {
                $sql = "UPDATE allocations SET " . implode(', ', $sets) . ", updated_at = NOW() WHERE id = ?";
                $vals[] = (int)$targetAllocId;
                $pdo->prepare($sql)->execute($vals);
            }
        }
    } catch (Throwable $e) {}
    try {
        if (function_exists('allocationLetterDataUpsert')) {
            $ctxLite = loadAllocationLetterContext($pdo, $targetAllocId, $companyId, $isSuperAdmin);
            $clientId = (int)($ctxLite['client']['id'] ?? 0);
            $statusDraft = 'draft';
            $versionNo = 1;
            try {
                if (function_exists('allocationLetterDataGet')) {
                    $existing = allocationLetterDataGet($pdo, (int)$targetAllocId);
                    $cur = strtolower((string)($existing['status'] ?? ''));
                    $versionNo = (int)($existing['version_no'] ?? 1);
                    if ($cur !== '' && !in_array($cur, ['draft','rejected','changes_requested'], true)) {
                        $statusDraft = $cur;
                    }
                    if (in_array($cur, ['chairman_approved','sent_to_client','viewed_by_client','accepted_by_client','completed'], true)) {
                        $versionNo = max(1, $versionNo) + 1;
                    }
                }
            } catch (Throwable $e) {}
            allocationLetterDataUpsert($pdo, $targetAllocId, [
                'client_id' => $clientId > 0 ? $clientId : null,
                'status' => $statusDraft,
                'version_no' => $versionNo,
                'phone' => $data['phone'] !== '' ? $data['phone'] : null,
                'email' => $data['email'] !== '' ? $data['email'] : null,
                'address' => $data['address'] !== '' ? $data['address'] : null,
                'passport' => $passportUrl !== '' ? $passportUrl : null,
                'plot_no' => $data['plot_number'] !== '' ? $data['plot_number'] : null,
                'sqm' => $data['sqm'] !== '' ? $data['sqm'] : null,
                'house_type' => $data['house_type'] !== '' ? $data['house_type'] : null,
                'prepared_by' => $data['prepared_by'] !== '' ? $data['prepared_by'] : null,
                'notes' => $data['notes'] !== '' ? $data['notes'] : null,
            ]);
        }
    } catch (Throwable $e) {}
    if ($ok && $isAdminOfficer && colx('allocations','status')) {
        try {
            $stCur = $pdo->prepare("SELECT status FROM allocations WHERE id = ? LIMIT 1");
            $stCur->execute([(int)$targetAllocId]);
            $cur = strtolower(trim((string)($stCur->fetchColumn() ?: '')));
            if ($cur === '' || in_array($cur, ['pending','draft_prepared','returned_for_correction'], true)) {
                $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?")->execute(['draft_prepared', (int)$targetAllocId]);
            }
        } catch (Throwable $e) {}
    }
    if ($ok) { $_SESSION['success_msg'] = 'Letter details saved.'; }
    else { $_SESSION['error_msg'] = 'Failed to save letter details.'; }
    $nextAction = strtolower(trim((string)($_POST['next_action'] ?? '')));
    if ($ok && $nextAction === 'terms') {
        header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=terms&allocation_id=' . (int)$targetAllocId);
        exit;
    }
    if ($ok && $nextAction === 'preview') {
        header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=letter_details&allocation_id=' . (int)$targetAllocId . '&open_preview=1');
        exit;
    }
    header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=letter_details&allocation_id=' . (int)$targetAllocId);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'save_terms' && $canManageApproval) {
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $infraMode = strtolower(trim((string)($_POST['infra_mode'] ?? 'percent')));
    if (!in_array($infraMode, ['percent','fixed'], true)) { $infraMode = 'percent'; }
    $infraPercent = (float)parseMoneyValue($_POST['infra_percent'] ?? 0);
    $infraAmount = (float)parseMoneyValue($_POST['infra_amount'] ?? 0);
    $vatPercent = (float)parseMoneyValue($_POST['vat_percent'] ?? 0);
    $months = max(1, (int)($_POST['payment_plan_months'] ?? 3));
    $refundCharge = (float)parseMoneyValue($_POST['refund_charge_percent'] ?? 0);
    $ctx = loadAllocationLetterContext($pdo, $targetAllocId, $companyId, $isSuperAdmin);
    $landCost = (float)parseMoneyValue($ctx['billing']['land_cost'] ?? 0);
    if ($landCost <= 0) {
        $landCost = (float)parseMoneyValue(($ctx['computed']['land_cost'] ?? 0));
    }
    if ($landCost <= 0) { $landCost = 0.0; }
    try {
        if (function_exists('ensureAllocationBillingTable')) { ensureAllocationBillingTable($pdo); }
        $now = date('Y-m-d H:i:s');
        $sql = "INSERT INTO allocation_billing (allocation_id, company_id, land_cost, infra_mode, infra_percent, infra_amount, vat_percent, payment_plan_months, updated_by, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE
                    company_id = VALUES(company_id),
                    infra_mode = VALUES(infra_mode),
                    infra_percent = VALUES(infra_percent),
                    infra_amount = VALUES(infra_amount),
                    vat_percent = VALUES(vat_percent),
                    payment_plan_months = VALUES(payment_plan_months),
                    updated_by = VALUES(updated_by),
                    updated_at = VALUES(updated_at)";
        $pdo->prepare($sql)->execute([(int)$targetAllocId, $companyId !== null ? (int)$companyId : null, $landCost, $infraMode, $infraPercent, $infraAmount, $vatPercent, $months, $uid, $now]);
    } catch (Throwable $e) {}
    $termsData = ['refund_charge_percent' => $refundCharge];
    $ok = function_exists('allocationLetterSectionsSave') ? allocationLetterSectionsSave($pdo, $targetAllocId, $companyId, 'terms', $termsData, $uid) : false;
    try {
        if (function_exists('allocationLetterDataUpsert')) {
            $statusDraft = 'draft';
            $versionNo = 1;
            try {
                if (function_exists('allocationLetterDataGet')) {
                    $existing = allocationLetterDataGet($pdo, (int)$targetAllocId);
                    $cur = strtolower((string)($existing['status'] ?? ''));
                    $versionNo = (int)($existing['version_no'] ?? 1);
                    if ($cur !== '' && !in_array($cur, ['draft','rejected','changes_requested'], true)) {
                        $statusDraft = $cur;
                    }
                    if (in_array($cur, ['chairman_approved','sent_to_client','viewed_by_client','accepted_by_client','completed'], true)) {
                        $versionNo = max(1, $versionNo) + 1;
                    }
                }
            } catch (Throwable $e) {}
            allocationLetterDataUpsert($pdo, (int)$targetAllocId, ['status' => $statusDraft, 'version_no' => $versionNo]);
        }
    } catch (Throwable $e) {}
    if ($ok && $isAdminOfficer && colx('allocations','status')) {
        try {
            $stCur = $pdo->prepare("SELECT status FROM allocations WHERE id = ? LIMIT 1");
            $stCur->execute([(int)$targetAllocId]);
            $cur = strtolower(trim((string)($stCur->fetchColumn() ?: '')));
            if ($cur === '' || in_array($cur, ['pending','draft_prepared','returned_for_correction'], true)) {
                $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?")->execute(['draft_prepared', (int)$targetAllocId]);
            }
        } catch (Throwable $e) {}
    }
    if ($ok) { $_SESSION['success_msg'] = 'Terms saved.'; }
    else { $_SESSION['error_msg'] = 'Failed to save terms.'; }
    $nextAction = strtolower(trim((string)($_POST['next_action'] ?? '')));
    if ($ok && $nextAction === 'guidelines') {
        header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=guidelines&allocation_id=' . (int)$targetAllocId);
        exit;
    }
    if ($ok && $nextAction === 'preview') {
        header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=terms&allocation_id=' . (int)$targetAllocId . '&open_preview=1');
        exit;
    }
    header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=terms&allocation_id=' . (int)$targetAllocId);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'save_guidelines' && $canManageApproval) {
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $guidelinesData = [
        'notes' => trim((string)($_POST['guidelines_notes'] ?? '')),
    ];
    $ok = function_exists('allocationLetterSectionsSave') ? allocationLetterSectionsSave($pdo, $targetAllocId, $companyId, 'guidelines', $guidelinesData, $uid) : false;
    try {
        if (function_exists('allocationLetterDataUpsert')) {
            $statusDraft = 'draft';
            $versionNo = 1;
            try {
                if (function_exists('allocationLetterDataGet')) {
                    $existing = allocationLetterDataGet($pdo, (int)$targetAllocId);
                    $cur = strtolower((string)($existing['status'] ?? ''));
                    $versionNo = (int)($existing['version_no'] ?? 1);
                    if ($cur !== '' && !in_array($cur, ['draft','rejected','changes_requested'], true)) {
                        $statusDraft = $cur;
                    }
                    if (in_array($cur, ['chairman_approved','sent_to_client','viewed_by_client','accepted_by_client','completed'], true)) {
                        $versionNo = max(1, $versionNo) + 1;
                    }
                }
            } catch (Throwable $e) {}
            allocationLetterDataUpsert($pdo, (int)$targetAllocId, ['status' => $statusDraft, 'version_no' => $versionNo]);
        }
    } catch (Throwable $e) {}
    if ($ok && $isAdminOfficer && colx('allocations','status')) {
        try {
            $stCur = $pdo->prepare("SELECT status FROM allocations WHERE id = ? LIMIT 1");
            $stCur->execute([(int)$targetAllocId]);
            $cur = strtolower(trim((string)($stCur->fetchColumn() ?: '')));
            if ($cur === '' || in_array($cur, ['pending','draft_prepared','returned_for_correction'], true)) {
                $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?")->execute(['draft_prepared', (int)$targetAllocId]);
            }
        } catch (Throwable $e) {}
    }
    if ($ok) { $_SESSION['success_msg'] = 'Building guidelines saved.'; }
    else { $_SESSION['error_msg'] = 'Failed to save building guidelines.'; }
    $nextAction = strtolower(trim((string)($_POST['next_action'] ?? '')));
    if ($ok && $nextAction === 'preview') {
        header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=guidelines&allocation_id=' . (int)$targetAllocId . '&open_preview=1');
        exit;
    }
    header('Location: allocation-letters.php?view=' . urlencode($viewMode) . '&action=guidelines&allocation_id=' . (int)$targetAllocId);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'save_draft' && $canManageApproval) {
    $isAjax = (string)($_POST['xhr'] ?? '') === '1';
    $targetAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($targetAllocId <= 0) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(['success' => false, 'error' => 'Invalid allocation selected.']);
            exit;
        }
        $_SESSION['error_msg'] = 'Invalid allocation selected.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $ok = false;
    try {
        if (function_exists('allocationLetterDataUpsert')) {
            $existing = function_exists('allocationLetterDataGet') ? allocationLetterDataGet($pdo, (int)$targetAllocId) : [];
            $cur = strtolower((string)($existing['status'] ?? ''));
            $status = ($cur !== '' && !in_array($cur, ['draft','rejected','changes_requested'], true)) ? $cur : 'draft';
            $ok = allocationLetterDataUpsert($pdo, (int)$targetAllocId, ['status' => $status]);
        }
    } catch (Throwable $e) { $ok = false; }
    if ($ok && $isAdminOfficer && colx('allocations','status')) {
        try {
            $stCur = $pdo->prepare("SELECT status FROM allocations WHERE id = ? LIMIT 1");
            $stCur->execute([(int)$targetAllocId]);
            $cur2 = strtolower(trim((string)($stCur->fetchColumn() ?: '')));
            if ($cur2 === '' || in_array($cur2, ['pending','draft_prepared','returned_for_correction'], true)) {
                $pdo->prepare("UPDATE allocations SET status = ?, updated_at = NOW() WHERE id = ?")->execute(['draft_prepared', (int)$targetAllocId]);
            }
        } catch (Throwable $e) {}
    }
    if ($isAjax) {
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['success' => $ok]);
        exit;
    }
    $_SESSION[$ok ? 'success_msg' : 'error_msg'] = $ok ? 'Draft saved.' : 'Failed to save draft.';
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'save_acknowledgement' && $canManageApproval) {
    $_SESSION['error_msg'] = 'Client acknowledgement is completed only in the Client Portal.';
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['form_action'] ?? '') === 'release_to_client') {
    if (!$canReleaseToClient) {
        $_SESSION['error_msg'] = 'Permission denied.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    $releaseAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    if ($releaseAllocId <= 0) {
        $_SESSION['error_msg'] = 'Invalid allocation selected for release.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    try {
        $startedTransaction = method_exists($pdo, 'inTransaction') ? !$pdo->inTransaction() : true;
        if ($startedTransaction) {
            $pdo->beginTransaction();
        }
        $st = $pdo->prepare("SELECT status, user_id FROM allocations WHERE id = ?" . ((!$isSuperAdmin && $companyId && colx('allocations','company_id')) ? " AND company_id = ?" : "") . " LIMIT 1");
        $paramsSt = [$releaseAllocId];
        if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) { $paramsSt[] = $companyId; }
        $st->execute($paramsSt);
        $allocRow = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        $curStatus = strtolower((string)($allocRow['status'] ?? ''));
        $allocUserId = (int)($allocRow['user_id'] ?? 0);
        if (!in_array($curStatus, ['executive_approved', 'approved'], true)) {
            throw new RuntimeException('Allocation must be executive approved before releasing to client.');
        }
        try {
            if (class_exists('DocGenerator')) {
                $gen = new DocGenerator($pdo);
                $gen->generateAllocationLetter($releaseAllocId, $uid);
            }
        } catch (Throwable $e) {}
        $hasDocuments = false;
        try { $hasDocuments = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0; } catch (Throwable $e) {}
        if ($hasDocuments && colx('documents','status')) {
            $releasedToken = pickColumnToken($pdo, 'documents', 'status', ['released', 'issued', 'signed', 'approved']);
            $set = ["status = ?"];
            $params = [$releasedToken];
            if (colx('documents','updated_at')) {
                $set[] = "updated_at = ?";
                $params[] = date('Y-m-d H:i:s');
            }
            if (colx('documents','released_at')) {
                $set[] = "released_at = ?";
                $params[] = date('Y-m-d H:i:s');
            }
            $sqlDocUpdate = "UPDATE documents SET " . implode(', ', $set) . " WHERE 1=1";
            if (colx('documents','type')) { $sqlDocUpdate .= " AND type = ?"; $params[] = 'allocation_letter'; }
            if (colx('documents','allocation_id')) {
                $sqlDocUpdate .= " AND allocation_id = ?";
                $params[] = $releaseAllocId;
            } else {
                $sqlDocUpdate .= " AND file_path LIKE ?";
                $params[] = '%Allocation_Letter_' . $releaseAllocId . '_%';
            }
            if ($allocUserId > 0 && colx('documents','user_id')) {
                $sqlDocUpdate .= " AND user_id = ?";
                $params[] = $allocUserId;
            }
            if (!$isSuperAdmin && $companyId && colx('documents','company_id')) {
                $sqlDocUpdate .= " AND company_id = ?";
                $params[] = $companyId;
            }
            $updDoc = $pdo->prepare($sqlDocUpdate);
            $updDoc->execute($params);
        }
        if (colx('allocations','status')) {
            $allocationDoneStatus = 'released_to_client';
            $setAlloc = ["status = ?"];
            $paramsAlloc = [$allocationDoneStatus];
            if (colx('allocations','updated_at')) {
                $setAlloc[] = "updated_at = ?";
                $paramsAlloc[] = date('Y-m-d H:i:s');
            }
            if (colx('allocations','letter_issued_at')) {
                $setAlloc[] = "letter_issued_at = ?";
                $paramsAlloc[] = date('Y-m-d H:i:s');
            }
            $sqlAlloc = "UPDATE allocations SET " . implode(', ', $setAlloc) . " WHERE id = ?";
            $paramsAlloc[] = $releaseAllocId;
            if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) {
                $sqlAlloc .= " AND company_id = ?";
                $paramsAlloc[] = $companyId;
            }
            $updAlloc = $pdo->prepare($sqlAlloc);
            $updAlloc->execute($paramsAlloc);
        }
        try {
            if (function_exists('allocationLetterDataUpsert')) {
                allocationLetterDataUpsert($pdo, (int)$releaseAllocId, [
                    'status' => 'sent_to_client',
                    'sent_to_client_at' => date('Y-m-d H:i:s'),
                ]);
            }
        } catch (Throwable $e) {}
        if ($startedTransaction && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) {
            $pdo->commit();
        }
        if (function_exists('logActivity') && $uid > 0) {
            try {
                logActivity($uid, 'ALLOCATION_LETTER_RELEASED_TO_CLIENT', json_encode(['allocation_id' => $releaseAllocId, 'company_id' => $companyId]));
            } catch (Throwable $e) {}
        }
        try {
            if ($allocUserId > 0) {
                $mailer = __DIR__ . '/includes/mailer.php';
                if (is_file($mailer)) { require_once $mailer; }
                if (function_exists('sendNotification')) {
                    sendNotification($allocUserId, 'allocation_letter_released', 'Your allocation letter has been released to your portal and is now available for download.', $pdo);
                }
            }
        } catch (Throwable $e) {}
        $_SESSION['success_msg'] = 'Allocation letter released to the client dashboard.';
    } catch (Throwable $e) {
        try {
            if (!empty($startedTransaction) && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
        } catch (Throwable $rollbackError) {}
        $_SESSION['error_msg'] = 'Failed to release allocation letter to the client dashboard.';
    }
    header('Location: ' . $allocationLettersBaseHref);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['exec_action']) && $canReviewExecutiveQueue) {
    $decisionAllocId = isset($_POST['allocation_id']) ? (int)$_POST['allocation_id'] : 0;
    $decision = strtolower(trim((string)($_POST['decision'] ?? '')));
    $comment = trim((string)($_POST['comment'] ?? ''));
    if ($decisionAllocId <= 0 || !in_array($decision, ['approve','reject','request_changes'], true)) {
        $_SESSION['error_msg'] = 'Invalid executive decision request.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    if ($comment === '') {
        $_SESSION['error_msg'] = 'Comment is required.';
        header('Location: ' . $allocationLettersBaseHref);
        exit;
    }
    try {
        $startedTransaction = method_exists($pdo, 'inTransaction') ? !$pdo->inTransaction() : true;
        if ($startedTransaction) {
            $pdo->beginTransaction();
        }
        $st = $pdo->prepare("SELECT status, user_id FROM allocations WHERE id = ?" . ((!$isSuperAdmin && $companyId && colx('allocations','company_id')) ? " AND company_id = ?" : "") . " LIMIT 1");
        $paramsSt = [$decisionAllocId];
        if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) { $paramsSt[] = $companyId; }
        $st->execute($paramsSt);
        $allocRow = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        $curStatus = strtolower((string)($allocRow['status'] ?? ''));
        if (!in_array($curStatus, ['pending_executive_approval'], true)) {
            throw new RuntimeException('Allocation is not awaiting executive review.');
        }
        $allocUserId = (int)($allocRow['user_id'] ?? 0);
        if ($decision === 'approve') {
            $newStatus = 'executive_approved';
        } elseif ($decision === 'request_changes') {
            $newStatus = 'returned_for_correction';
        } else {
            $newStatus = pickColumnToken($pdo, 'allocations', 'status', ['rejected', 'declined', 'revoked']);
        }
        $setAlloc = ["status = ?"];
        $paramsAlloc = [$newStatus];
        if (colx('allocations','updated_at')) {
            $setAlloc[] = "updated_at = ?";
            $paramsAlloc[] = date('Y-m-d H:i:s');
        }
        if (colx('allocations','exec_comment')) {
            $setAlloc[] = "exec_comment = ?";
            $paramsAlloc[] = $comment;
        }
        if (colx('allocations','exec_decided_by')) {
            $setAlloc[] = "exec_decided_by = ?";
            $paramsAlloc[] = $uid;
        }
        if (colx('allocations','exec_decided_at')) {
            $setAlloc[] = "exec_decided_at = ?";
            $paramsAlloc[] = date('Y-m-d H:i:s');
        }
        $sqlAlloc = "UPDATE allocations SET " . implode(', ', $setAlloc) . " WHERE id = ?";
        $paramsAlloc[] = $decisionAllocId;
        if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) {
            $sqlAlloc .= " AND company_id = ?";
            $paramsAlloc[] = $companyId;
        }
        $updAlloc = $pdo->prepare($sqlAlloc);
        $updAlloc->execute($paramsAlloc);
        $hasDocuments = false;
        try { $hasDocuments = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0; } catch (Throwable $e) {}
        if ($hasDocuments && colx('documents','status')) {
            $docStatus = $decision === 'approve'
                ? pickColumnToken($pdo, 'documents', 'status', ['approved', 'issued', 'signed', 'pending'])
                : ($decision === 'request_changes'
                    ? pickColumnToken($pdo, 'documents', 'status', ['draft', 'pending'])
                    : pickColumnToken($pdo, 'documents', 'status', ['rejected', 'draft', 'pending']));
            $set = ["status = ?"];
            $params = [$docStatus];
            if (colx('documents','updated_at')) {
                $set[] = "updated_at = ?";
                $params[] = date('Y-m-d H:i:s');
            }
            if (colx('documents','review_comment')) {
                $set[] = "review_comment = ?";
                $params[] = $comment;
            }
            if (colx('documents','reviewed_by')) {
                $set[] = "reviewed_by = ?";
                $params[] = $uid;
            }
            if (colx('documents','reviewed_at')) {
                $set[] = "reviewed_at = ?";
                $params[] = date('Y-m-d H:i:s');
            }
            $sqlDocUpdate = "UPDATE documents SET " . implode(', ', $set) . " WHERE 1=1";
            if (colx('documents','type')) { $sqlDocUpdate .= " AND type = ?"; $params[] = 'allocation_letter'; }
            if (colx('documents','allocation_id')) {
                $sqlDocUpdate .= " AND allocation_id = ?";
                $params[] = $decisionAllocId;
            } else {
                $sqlDocUpdate .= " AND file_path LIKE ?";
                $params[] = '%Allocation_Letter_' . $decisionAllocId . '_%';
            }
            if ($allocUserId > 0 && colx('documents','user_id')) {
                $sqlDocUpdate .= " AND user_id = ?";
                $params[] = $allocUserId;
            }
            if (!$isSuperAdmin && $companyId && colx('documents','company_id')) {
                $sqlDocUpdate .= " AND company_id = ?";
                $params[] = $companyId;
            }
            $updDoc = $pdo->prepare($sqlDocUpdate);
            $updDoc->execute($params);
        }
        try {
            if (function_exists('allocationLetterDataUpsert')) {
                $status = $decision === 'approve' ? 'chairman_approved' : ($decision === 'request_changes' ? 'changes_requested' : 'rejected');
                allocationLetterDataUpsert($pdo, (int)$decisionAllocId, [
                    'status' => $status,
                    'chairman_decision' => $decision,
                    'chairman_comment' => $comment,
                    'chairman_decided_by' => $uid > 0 ? $uid : null,
                    'chairman_decided_at' => date('Y-m-d H:i:s'),
                ]);
            }
        } catch (Throwable $e) {}
        if ($startedTransaction && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) {
            $pdo->commit();
        }
        if (function_exists('logActivity') && $uid > 0) {
            try {
                logActivity($uid, 'EXEC_ALLOCATION_LETTER_DECISION', json_encode(['allocation_id' => $decisionAllocId, 'decision' => $decision, 'company_id' => $companyId]));
            } catch (Throwable $e) {}
        }
        $_SESSION['success_msg'] = $decision === 'approve' ? 'Allocation letter approved.' : ($decision === 'request_changes' ? 'Changes requested for this allocation letter.' : 'Allocation letter rejected.');
        try {
            if (function_exists('ap_user_ids_by_roles') && function_exists('sendNotification')) {
                $ids = ap_user_ids_by_roles($pdo, ['head_admin','admin'], $companyId ? (int)$companyId : null);
                foreach ($ids as $hid) {
                    sendNotification((int)$hid, 'allocation_letter_exec_decision', 'Executive decision for Allocation #' . (int)$decisionAllocId . ': ' . strtoupper($decision) . '.', $pdo);
                }
            }
        } catch (Throwable $e) {}
    } catch (Throwable $e) {
        try {
            if (!empty($startedTransaction) && method_exists($pdo, 'inTransaction') && $pdo->inTransaction()) {
                $pdo->rollBack();
            }
        } catch (Throwable $rollbackError) {}
        $_SESSION['error_msg'] = 'Failed to record executive decision.';
    }
    header('Location: ' . $allocationLettersBaseHref . '&status=' . urlencode($decision === 'approve' ? 'approved' : 'all') . '&allocation_id=' . (int)$decisionAllocId);
    exit;
}
if ($action === 'offer_docx' && $allocId > 0) {
    $user = [];
    $formData = [];
    $allocation = [];
    $property = [];
    $nm = $email = $phone = $addr = '';
    $code = ''; $size = ''; $purpose = ''; $preferredProperty = ''; $offeredAmount = 0.0;
    $orgClient = false;
    try {
        $st = $pdo->prepare("SELECT * FROM allocations WHERE id = ?" . ((!$isSuperAdmin && $companyId && colx('allocations','company_id')) ? " AND company_id = ?" : ""));
        $params = [$allocId];
        if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) { $params[] = $companyId; }
        $st->execute($params);
        $allocation = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        $uid2 = (int)($allocation['user_id'] ?? 0);
        $pid2 = (int)($allocation['property_id'] ?? 0);
        if ($uid2 > 0) {
            $su = $pdo->prepare("SELECT * FROM users WHERE id = ? LIMIT 1");
            $su->execute([$uid2]);
            $user = $su->fetch(PDO::FETCH_ASSOC) ?: [];
            $sf = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY updated_at DESC, created_at DESC LIMIT 1");
            try { $sf->execute([$uid2]); } catch (Throwable $eFD) {}
            $row = $sf->fetch(PDO::FETCH_ASSOC) ?: [];
            if (!empty($row['form_data'])) {
                $tmp = json_decode($row['form_data'], true);
                if (is_array($tmp)) { $formData = $tmp; }
            }
            $nm = $user['name'] ?? ($user['full_name'] ?? (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
            $email = $user['email'] ?? ($formData['email'] ?? '');
            $phone = $user['phone'] ?? ($formData['phone'] ?? '');
            $addr = $user['address'] ?? ($formData['address'] ?? ($formData['residential_address'] ?? ''));
            $orgClient = !empty($formData['company_name'] ?? '');
        }
        if ($pid2 > 0) {
            $sp = $pdo->prepare("SELECT * FROM properties WHERE id = ? LIMIT 1");
            $sp->execute([$pid2]);
            $property = $sp->fetch(PDO::FETCH_ASSOC) ?: [];
            $code = $property['code'] ?? ($property['plot_code'] ?? (string)($property['id'] ?? ''));
            $size = (string)($property['plot_size'] ?? ($property['area_sqm'] ?? ''));
            $purpose = (string)($property['purpose'] ?? ($formData['purpose'] ?? 'RESIDENTIAL'));
            $preferredProperty = (string)($formData['preferred_property'] ?? ($property['type'] ?? ''));
        }
        // Compute offered/total amount from form or deals/payments
        if (!empty($formData['offered_amount'])) {
            $offeredAmount = (float)preg_replace('/[^\d.]/', '', (string)$formData['offered_amount']);
        }
        if ($offeredAmount <= 0) {
            try {
                if (colx('deals','allocation_id')) {
                    $valCol = colx('deals','final_value') ? 'final_value' : (colx('deals','deal_value') ? 'deal_value' : (colx('deals','value') ? 'value' : null));
                    if ($valCol) {
                        $qd = $pdo->prepare("SELECT {$valCol} AS v FROM deals WHERE allocation_id = ? ORDER BY id DESC LIMIT 1");
                        $qd->execute([$allocId]); $offeredAmount = (float)($qd->fetchColumn() ?: 0.0);
                    }
                }
            } catch (Throwable $eD) {}
        }
        if ($offeredAmount <= 0 && colx('payments','allocation_id')) {
            try {
                $cmpClause = ''; $cmpParams = [];
                if ($companyId && colx('payments','company_id')) { $cmpClause = " AND company_id = ?"; $cmpParams[] = $companyId; }
                $qp = $pdo->prepare("SELECT COALESCE(SUM(amount),0) FROM payments WHERE allocation_id = ? AND status IN ('approved','paid')" . $cmpClause);
                $qp->execute(array_merge([$allocId], $cmpParams)); $offeredAmount = (float)($qp->fetchColumn() ?: 0.0);
            } catch (Throwable $eP) {}
        }
    } catch (Throwable $e) {}
    // Determine template: organization vs individual
    $tplSettingKey = $orgClient ? 'offer_letter_org_template_url' : 'offer_letter_ind_template_url';
    $tplUrl = function_exists('getSetting') ? (getSetting($tplSettingKey, '') ?: '') : '';
    // Fallback to user-provided examples if not configured
    if ($tplUrl === '') {
        $tplUrl = $orgClient
            ? 'https://aibenproperties.com/wp-content/uploads/2026/03/DTERRANOVA-CITY-LIMITED-PROVISIONAL-OFFER-LETTER-15-HECTARES-N.docx'
            : 'https://aibenproperties.com/wp-content/uploads/2026/03/AGHOGHO-OKEKE-OFFER-LETTER-250sqm-N.docx';
    }
    $lettersDir = __DIR__ . '/uploads/letters';
    if (!is_dir($lettersDir)) { @mkdir($lettersDir, 0777, true); }
    $tplLocal = $lettersDir . ($orgClient ? '/offer_org_template.docx' : '/offer_ind_template.docx');
    // Ensure local template exists
    if (!file_exists($tplLocal)) {
        if (stripos($tplUrl, 'http') === 0) {
            $bin = @file_get_contents($tplUrl);
            if ($bin !== false) { @file_put_contents($tplLocal, $bin); }
        } else {
            $src = __DIR__ . '/' . ltrim($tplUrl, '/');
            if (file_exists($src)) { @copy($src, $tplLocal); }
        }
        // Inject placeholders as in letter-templates.php
        try {
            $zip = new ZipArchive();
            if (file_exists($tplLocal) && $zip->open($tplLocal) === true) {
                $xml = $zip->getFromName('word/document.xml');
                if ($xml !== false) {
                    if ($orgClient) {
                        $xml = str_replace('DTERRANOVA CITY LIMITED', '{{COMPANY_NAME}}', $xml);
                        $xml = str_replace('DTERRANOVA', '{{COMPANY_NAME}}', $xml);
                        $xml = preg_replace('/(?:₦|NGN)?\s*\d[\d,]*(?:\.\d+)?/u', '{{OFFERED_AMOUNT}}', $xml, 1);
                        $xml = preg_replace('/\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2},\s+\d{4}\b/u', '{{DATE}}', $xml, 1);
                        $xml = preg_replace('/Name:\s*[^<\n]*/i', 'Name: {{CLIENT_NAME}}', $xml);
                        $xml = preg_replace('/Phone:\s*[^<\n]*/i', 'Phone: {{PHONE}}', $xml);
                        $xml = preg_replace('/Email:\s*[^<\n]*/i', 'Email: {{EMAIL}}', $xml);
                        $xml = preg_replace('/Address:\s*[^<\n]*/i', 'Address: {{CLIENT_ADDRESS}}', $xml);
                        $xml = preg_replace('/PLOT NO:\s*[^<\n]*/i', 'PLOT NO: {{PLOT_NO}}', $xml);
                        $xml = preg_replace('/PLOT SIZE:\s*[^<\n]*/i', 'PLOT SIZE: {{PLOT_SIZE}}', $xml);
                        $xml = preg_replace('/PURPOSE:\s*[^<\n]*/i', 'PURPOSE: {{PURPOSE}}', $xml);
                        $xml = preg_replace('/BUILDING TYPE:\s*[^<\n]*/i', 'BUILDING TYPE: {{BUILDING_TYPE}}', $xml);
                        $xml = preg_replace('/AMOUNT:\s*[^<\n]*/i', 'AMOUNT: {{AMOUNT}}', $xml);
                    } else {
                        $xml = str_replace('AGHOGHO OKEKE', '{{CLIENT_NAME}}', $xml);
                        $xml = str_replace('0803 438 0669', '{{PHONE}}', $xml);
                        $xml = str_replace('aghogho.bobokonedo@gmail.com', '{{EMAIL}}', $xml);
                        $xml = str_replace('B20B Chessville Corte Estate, Plot 65 Kafe District, Life Camp, FCT Abuja.', '{{CLIENT_ADDRESS}}', $xml);
                        $xml = str_replace('HUTU Prestige Recreational Resort Estate', '{{ESTATE_NAME}}', $xml);
                        $xml = str_replace('Cadastral Zone E09, Lugbe South District, Abuja-FCT', '{{ESTATE_LOCATION}}', $xml);
                        $xml = str_replace('NOT READY', '{{PLOT_NO}}', $xml);
                        $xml = str_replace('250SQM', '{{PLOT_SIZE}}', $xml);
                        $xml = str_replace('RESIDENTIAL', '{{PURPOSE}}', $xml);
                        $xml = str_replace('4 BEDROOMS TERRACE DUPLEX WITH BQ', '{{BUILDING_TYPE}}', $xml);
                        $xml = preg_replace('/(?:₦|NGN)?\s*\d[\d,]*(?:\.\d+)?/u', '{{OFFERED_AMOUNT}}', $xml, 1);
                        $xml = preg_replace('/\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{1,2},\s+\d{4}\b/u', '{{DATE}}', $xml, 1);
                        $xml = preg_replace('/Name:\s*[^<\n]*/i', 'Name: {{CLIENT_NAME}}', $xml);
                        $xml = preg_replace('/Phone:\s*[^<\n]*/i', 'Phone: {{PHONE}}', $xml);
                        $xml = preg_replace('/Email:\s*[^<\n]*/i', 'Email: {{EMAIL}}', $xml);
                        $xml = preg_replace('/Address:\s*[^<\n]*/i', 'Address: {{CLIENT_ADDRESS}}', $xml);
                        $xml = preg_replace('/PLOT NO:\s*[^<\n]*/i', 'PLOT NO: {{PLOT_NO}}', $xml);
                        $xml = preg_replace('/PLOT SIZE:\s*[^<\n]*/i', 'PLOT SIZE: {{PLOT_SIZE}}', $xml);
                        $xml = preg_replace('/PURPOSE:\s*[^<\n]*/i', 'PURPOSE: {{PURPOSE}}', $xml);
                        $xml = preg_replace('/BUILDING TYPE:\s*[^<\n]*/i', 'BUILDING TYPE: {{BUILDING_TYPE}}', $xml);
                        $xml = preg_replace('/AMOUNT:\s*[^<\n]*/i', 'AMOUNT: {{AMOUNT}}', $xml);
                    }
                    $zip->addFromString('word/document.xml', $xml);
                }
                $zip->close();
            }
        } catch (Throwable $eX) {}
    }
    // Prepare output docx by replacing placeholders with real values
    $companyName = function_exists('getSetting') ? (getSetting('company_name', 'Aiben Properties') ?: 'Aiben Properties') : 'Aiben Properties';
    $estateName = (string)($property['title'] ?? ($property['name'] ?? ''));
    $estateLocation = (string)($property['address'] ?? ($property['location'] ?? ''));
    $repls = [
        '{{COMPANY_NAME}}' => $companyName,
        '{{CLIENT_NAME}}' => trim($nm),
        '{{PHONE}}' => $phone,
        '{{EMAIL}}' => $email,
        '{{CLIENT_ADDRESS}}' => $addr,
        '{{ESTATE_NAME}}' => $estateName,
        '{{ESTATE_LOCATION}}' => $estateLocation,
        '{{PLOT_NO}}' => $code,
        '{{PLOT_SIZE}}' => $size,
        '{{PURPOSE}}' => $purpose,
        '{{BUILDING_TYPE}}' => $preferredProperty,
        '{{AMOUNT}}' => number_format((float)$offeredAmount, 2),
        '{{OFFERED_AMOUNT}}' => number_format((float)$offeredAmount, 2),
        '{{DATE}}' => date('F j, Y'),
    ];
    $outDir = __DIR__ . '/uploads/documents/generated';
    if (!is_dir($outDir)) { @mkdir($outDir, 0777, true); }
    $outPath = $outDir . '/Offer_Letter_' . (int)$allocId . '_' . time() . '.docx';
    @copy($tplLocal, $outPath);
    $ok = false;
    try {
        $zip = new ZipArchive();
        if ($zip->open($outPath) === true) {
            $xml = $zip->getFromName('word/document.xml');
            if ($xml !== false) {
                $xml = strtr($xml, $repls);
                $zip->addFromString('word/document.xml', $xml);
                $ok = true;
            }
            $zip->close();
        }
    } catch (Throwable $eZ) {}
    if ($ok) {
        $rel = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $outPath);
        header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
        header('Content-Disposition: inline; filename="' . basename($outPath) . '"');
        header('Content-Length: ' . filesize($outPath));
        readfile($outPath);
        exit;
    } else {
        header('Content-Type: text/plain; charset=UTF-8');
        http_response_code(500);
        echo "Failed to generate Offer Letter DOCX.";
        exit;
    }
}
if ($action === 'preview' && $allocId > 0) {
    $force = isset($_GET['force']) && (string)$_GET['force'] === '1';
    $download = isset($_GET['download']) && (string)$_GET['download'] === '1';
    $previewMissingNice = [];
    try {
        if ($canManageApproval && function_exists('allocationLetterDataGet')) {
            $ld = allocationLetterDataGet($pdo, (int)$allocId);
            $missing = [];
            if (trim((string)($ld['phone'] ?? '')) === '') { $missing[] = 'phone'; }
            if (trim((string)($ld['email'] ?? '')) === '') { $missing[] = 'email'; }
            if (trim((string)($ld['address'] ?? '')) === '') { $missing[] = 'address'; }
            if (trim((string)($ld['plot_no'] ?? '')) === '') { $missing[] = 'plot_no'; }
            if (trim((string)($ld['sqm'] ?? '')) === '') { $missing[] = 'sqm'; }
            if (trim((string)($ld['house_type'] ?? '')) === '') { $missing[] = 'house_type'; }
            if (trim((string)($ld['prepared_by'] ?? '')) === '') { $missing[] = 'prepared_by'; }
            if (!empty($missing) && function_exists('allocationLetterDataUpsert')) {
                $autoPatched = false;
                $ctxAuto = loadAllocationLetterContext($pdo, (int)$allocId, $companyId, $isSuperAdmin);
                $preparedByFallback = trim((string)($_SESSION['user_name'] ?? $_SESSION['name'] ?? $_SESSION['full_name'] ?? ''));
                if ($preparedByFallback === '' && $uid > 0) {
                    try {
                        $stU = $pdo->prepare("SELECT name, full_name, first_name, last_name FROM users WHERE id = ? LIMIT 1");
                        $stU->execute([$uid]);
                        $urow = $stU->fetch(PDO::FETCH_ASSOC) ?: [];
                        $preparedByFallback = trim((string)($urow['name'] ?? $urow['full_name'] ?? (($urow['first_name'] ?? '') . ' ' . ($urow['last_name'] ?? ''))));
                    } catch (Throwable $e) {}
                }
                $cAuto = is_array($ctxAuto['client'] ?? null) ? $ctxAuto['client'] : [];
                $pAuto = is_array($ctxAuto['property'] ?? null) ? $ctxAuto['property'] : [];
                $mAuto = is_array($ctxAuto['meta'] ?? null) ? $ctxAuto['meta'] : [];
                $candidates = [
                    'phone' => trim((string)($cAuto['phone'] ?? '')),
                    'email' => trim((string)($cAuto['email'] ?? '')),
                    'address' => trim((string)($cAuto['address'] ?? '')),
                    'plot_no' => trim((string)($pAuto['plot_number'] ?? '')),
                    'sqm' => trim((string)($pAuto['sqm'] ?? '')),
                    'house_type' => trim((string)($pAuto['house_type'] ?? '')),
                    'prepared_by' => trim((string)($mAuto['prepared_by'] ?? $preparedByFallback)),
                ];
                $patch = [];
                foreach ($missing as $k) {
                    if (($candidates[$k] ?? '') !== '') {
                        $patch[$k] = $candidates[$k];
                    }
                }
                if (!empty($patch)) {
                    allocationLetterDataUpsert($pdo, (int)$allocId, $patch);
                    $autoPatched = true;
                    $ld = allocationLetterDataGet($pdo, (int)$allocId);
                    $missing = [];
                    if (trim((string)($ld['phone'] ?? '')) === '') { $missing[] = 'phone'; }
                    if (trim((string)($ld['email'] ?? '')) === '') { $missing[] = 'email'; }
                    if (trim((string)($ld['address'] ?? '')) === '') { $missing[] = 'address'; }
                    if (trim((string)($ld['plot_no'] ?? '')) === '') { $missing[] = 'plot_no'; }
                    if (trim((string)($ld['sqm'] ?? '')) === '') { $missing[] = 'sqm'; }
                    if (trim((string)($ld['house_type'] ?? '')) === '') { $missing[] = 'house_type'; }
                    if (trim((string)($ld['prepared_by'] ?? '')) === '') { $missing[] = 'prepared_by'; }
                }
                if ($autoPatched) { $force = true; }
            }
            if (!empty($missing)) {
                $labels = [
                    'phone' => 'Phone',
                    'email' => 'Email',
                    'address' => 'Address',
                    'plot_no' => 'Plot No',
                    'sqm' => 'SQM',
                    'house_type' => 'House Type',
                    'prepared_by' => 'Prepared By',
                ];
                $nice = [];
                foreach ($missing as $k) { $nice[] = $labels[$k] ?? $k; }
                $previewMissingNice = $nice;
            }
        }
    } catch (Throwable $e) {}
    $previewDocPath = '';
    $previewDocId = 0;
    $previewPublicId = '';
    $generatedDir = __DIR__ . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'documents' . DIRECTORY_SEPARATOR . 'generated' . DIRECTORY_SEPARATOR;
    $generatedPattern = $generatedDir . 'Allocation_Letter_' . $allocId . '_*';
    try {
        $hasDocuments = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0;
        if ($hasDocuments) {
            $sqlPreviewDoc = "SELECT id, file_path" . (colx('documents','public_id') ? ", public_id" : "") . " FROM documents WHERE 1=1";
            $paramsPreviewDoc = [];
            if (colx('documents','type')) { $sqlPreviewDoc .= " AND type = ?"; $paramsPreviewDoc[] = 'allocation_letter'; }
            if (colx('documents','allocation_id')) {
                $sqlPreviewDoc .= " AND allocation_id = ?";
                $paramsPreviewDoc[] = $allocId;
            } else {
                $sqlPreviewDoc .= " AND file_path LIKE ?";
                $paramsPreviewDoc[] = '%Allocation_Letter_' . $allocId . '_%';
            }
            if (!$isSuperAdmin && $companyId && colx('documents','company_id')) {
                $sqlPreviewDoc .= " AND company_id = ?";
                $paramsPreviewDoc[] = $companyId;
            }
            $sqlPreviewDoc .= " ORDER BY id DESC LIMIT 1";
            $stPreviewDoc = $pdo->prepare($sqlPreviewDoc);
            $stPreviewDoc->execute($paramsPreviewDoc);
            $rowPreviewDoc = $stPreviewDoc->fetch(PDO::FETCH_ASSOC) ?: [];
            $previewDocId = (int)($rowPreviewDoc['id'] ?? 0);
            $previewDocPath = (string)($rowPreviewDoc['file_path'] ?? '');
            $previewPublicId = (string)($rowPreviewDoc['public_id'] ?? '');
        }
    } catch (Throwable $e) {}
    try {
        $generatedMatches = glob($generatedPattern) ?: [];
        if (!empty($generatedMatches)) {
            usort($generatedMatches, static function ($a, $b) {
                return filemtime($b) <=> filemtime($a);
            });
            foreach ($generatedMatches as $matchPath) {
                if (preg_match('/\.html?$/i', $matchPath)) {
                    $previewDocPath = $matchPath;
                    break;
                }
            }
            if ($previewDocPath === '' && !empty($generatedMatches[0])) {
                $previewDocPath = (string)$generatedMatches[0];
            }
        }
    } catch (Throwable $e) {}
    $billingUpdatedAt = '';
    try {
        if (function_exists('tableHasColumn') && tableHasColumn('allocation_billing', 'updated_at')) {
            $stBu = $pdo->prepare("SELECT updated_at FROM allocation_billing WHERE allocation_id = ? LIMIT 1");
            $stBu->execute([$allocId]);
            $billingUpdatedAt = (string)($stBu->fetchColumn() ?: '');
        }
    } catch (Throwable $e) {}
    if (!$force && $previewDocPath !== '') {
        $previewLocalPath = $previewDocPath;
        if (!preg_match('/^(?:[a-z]+:)?\/\//i', $previewLocalPath) && !preg_match('/^[A-Za-z]:[\\\\\\/]/', $previewLocalPath)) {
            $isAbsUnix = (strlen($previewLocalPath) > 0 && ($previewLocalPath[0] === '/' || $previewLocalPath[0] === '\\'));
            if (!$isAbsUnix) {
                $previewLocalPath = __DIR__ . DIRECTORY_SEPARATOR . ltrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $previewLocalPath), DIRECTORY_SEPARATOR);
            }
        }
        $docTime = is_file($previewLocalPath) ? (int)@filemtime($previewLocalPath) : 0;
        if ($billingUpdatedAt !== '') {
            $billTime = strtotime($billingUpdatedAt);
            if ($billTime && $docTime > 0 && $billTime > $docTime) {
                $force = true;
            }
        }
        if (!$force && $docTime > 0) {
            try {
                $stAu = $pdo->prepare("SELECT updated_at FROM allocations WHERE id = ? LIMIT 1");
                $stAu->execute([(int)$allocId]);
                $allocUpdatedAt = (string)($stAu->fetchColumn() ?: '');
                $allocTime = $allocUpdatedAt !== '' ? strtotime($allocUpdatedAt) : 0;
                if ($allocTime && $allocTime > $docTime) {
                    $force = true;
                }
            } catch (Throwable $e) {}
        }
        if (!$force && $docTime > 0) {
            try {
                $tplFiles = [
                    __DIR__ . '/includes/doc_templates.php',
                    __DIR__ . '/includes/doc_generator.php',
                ];
                $latestTplTime = 0;
                foreach ($tplFiles as $tf) {
                    $t = is_file($tf) ? (int)@filemtime($tf) : 0;
                    if ($t > $latestTplTime) { $latestTplTime = $t; }
                }
                if ($latestTplTime > 0 && $latestTplTime > $docTime) {
                    $force = true;
                }
            } catch (Throwable $e) {}
        }
    }
    if ($force) {
        $previewDocPath = '';
        $previewDocId = 0;
        $previewPublicId = '';
    }
    if ($previewDocPath === '') {
        try {
            $generator = new DocGenerator($pdo);
            $generator->generateAllocationLetter($allocId, $uid);
        } catch (Throwable $e) {}
        try {
            $hasDocuments = $pdo->query("SHOW TABLES LIKE 'documents'")->rowCount() > 0;
            if ($hasDocuments) {
                $sqlPreviewDoc = "SELECT id, file_path" . (colx('documents','public_id') ? ", public_id" : "") . " FROM documents WHERE 1=1";
                $paramsPreviewDoc = [];
                if (colx('documents','type')) { $sqlPreviewDoc .= " AND type = ?"; $paramsPreviewDoc[] = 'allocation_letter'; }
                if (colx('documents','allocation_id')) {
                    $sqlPreviewDoc .= " AND allocation_id = ?";
                    $paramsPreviewDoc[] = $allocId;
                } else {
                    $sqlPreviewDoc .= " AND file_path LIKE ?";
                    $paramsPreviewDoc[] = '%Allocation_Letter_' . $allocId . '_%';
                }
                if (!$isSuperAdmin && $companyId && colx('documents','company_id')) {
                    $sqlPreviewDoc .= " AND company_id = ?";
                    $paramsPreviewDoc[] = $companyId;
                }
                $sqlPreviewDoc .= " ORDER BY id DESC LIMIT 1";
                $stPreviewDoc = $pdo->prepare($sqlPreviewDoc);
                $stPreviewDoc->execute($paramsPreviewDoc);
                $rowPreviewDoc = $stPreviewDoc->fetch(PDO::FETCH_ASSOC) ?: [];
                $previewDocId = (int)($rowPreviewDoc['id'] ?? 0);
                $previewDocPath = (string)($rowPreviewDoc['file_path'] ?? '');
                $previewPublicId = (string)($rowPreviewDoc['public_id'] ?? '');
            }
        } catch (Throwable $e) {}
        try {
            $generatedMatches = glob($generatedPattern) ?: [];
            if (!empty($generatedMatches)) {
                usort($generatedMatches, static function ($a, $b) {
                    return filemtime($b) <=> filemtime($a);
                });
                foreach ($generatedMatches as $matchPath) {
                    if (preg_match('/\.html?$/i', $matchPath)) {
                        $previewDocPath = $matchPath;
                        break;
                    }
                }
                if ($previewDocPath === '' && !empty($generatedMatches[0])) {
                    $previewDocPath = (string)$generatedMatches[0];
                }
            }
        } catch (Throwable $e) {}
    }
    if ($previewDocPath !== '') {
        $makeVerifyUrl = static function (string $publicId): string {
            $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
            $host = (string)($_SERVER['HTTP_HOST'] ?? '');
            $base = $host !== '' ? ($scheme . '://' . $host) : '';
            $base = rtrim($base, '/');
            return ($base !== '' ? $base : '') . '/verify.php?doc=' . rawurlencode($publicId);
        };
        $ensurePublicId = function () use ($pdo, $previewDocId, $previewPublicId, $allocId): string {
            $pid = trim((string)$previewPublicId);
            if ($pid !== '') { return $pid; }
            if (!colx('documents','public_id') || $previewDocId <= 0) { return ''; }
            $prefix = 'AIBEN-AL-';
            for ($i = 0; $i < 25; $i++) {
                $candidate = $prefix . str_pad((string)random_int(0, 999999), 6, '0', STR_PAD_LEFT);
                $st = $pdo->prepare("SELECT COUNT(*) FROM documents WHERE public_id = ? LIMIT 1");
                $st->execute([$candidate]);
                if ((int)$st->fetchColumn() === 0) {
                    $pdo->prepare("UPDATE documents SET public_id = ? WHERE id = ?")->execute([$candidate, $previewDocId]);
                    return $candidate;
                }
            }
            $candidate = $prefix . str_pad((string)$allocId, 6, '0', STR_PAD_LEFT);
            try { $pdo->prepare("UPDATE documents SET public_id = ? WHERE id = ?")->execute([$candidate, $previewDocId]); } catch (Throwable $e) {}
            return $candidate;
        };
        $ensureQrData = static function (string $publicId, string $verifyUrl): array {
            $relDir = 'uploads/qr';
            $absDir = __DIR__ . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'qr';
            if (!is_dir($absDir)) { @mkdir($absDir, 0755, true); }
            $absPath = $absDir . DIRECTORY_SEPARATOR . $publicId . '.png';
            $relPath = $relDir . '/' . $publicId . '.png';
            if (!file_exists($absPath) || filesize($absPath) < 10) {
                $lib = __DIR__ . '/includes/phpqrcode/qrlib.php';
                if (file_exists($lib)) { require_once $lib; }
                if (class_exists('QRcode')) {
                    try { QRcode::png($verifyUrl, $absPath, 'M', 5, 2); } catch (Throwable $e) {}
                }
            }
            $dataUri = '';
            if (file_exists($absPath)) {
                $bin = @file_get_contents($absPath);
                if ($bin !== false) { $dataUri = 'data:image/png;base64,' . base64_encode($bin); }
            }
            return ['qr_rel_path' => $relPath, 'qr_abs_path' => $absPath, 'qr_data_uri' => $dataUri];
        };

        $publicId = $ensurePublicId();
        $verifyUrl = $publicId !== '' ? $makeVerifyUrl($publicId) : '';
        $qr = ($publicId !== '' && $verifyUrl !== '') ? $ensureQrData($publicId, $verifyUrl) : ['qr_data_uri' => ''];

        $previewLocalPath = $previewDocPath;
        if (!preg_match('/^(?:[a-z]+:)?\/\//i', $previewLocalPath) && !preg_match('/^[A-Za-z]:[\\\\\\/]/', $previewLocalPath)) {
            $previewLocalPath = __DIR__ . DIRECTORY_SEPARATOR . ltrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $previewLocalPath), DIRECTORY_SEPARATOR);
        }
        if ($download && is_file($previewLocalPath) && preg_match('/\.pdf$/i', $previewLocalPath)) {
            header('Content-Type: application/pdf');
            header('Content-Disposition: attachment; filename="' . basename($previewLocalPath) . '"');
            header('Content-Length: ' . filesize($previewLocalPath));
            readfile($previewLocalPath);
            exit;
        }
        if (is_file($previewLocalPath) && preg_match('/\.html?$/i', $previewLocalPath)) {
            $html = (string)@file_get_contents($previewLocalPath);
            if ($html !== '') {
            try {
                $base = function_exists('buildAbsBaseUrl') ? buildAbsBaseUrl() : '/';
                $root = realpath(__DIR__);
                if ($root !== false && $root !== '') {
                    $rootNorm = str_replace('\\', '/', (string)$root);
                    $rootNormEnc = str_replace(' ', '%20', $rootNorm);
                    $baseNorm = rtrim((string)$base, '/') . '/';
                    $html = str_replace('file:///' . rtrim($rootNormEnc, '/') . '/', $baseNorm, $html);
                    $html = str_replace('file:///' . rtrim($rootNorm, '/') . '/', $baseNorm, $html);
                    $html = str_replace(rtrim($rootNormEnc, '/') . '/', $baseNorm, $html);
                    $html = str_replace(rtrim($rootNorm, '/') . '/', $baseNorm, $html);

                        $inlineProtected = static function (string $src) use ($baseNorm, $root): string {
                            $s = trim($src);
                            if ($s === '' || stripos($s, 'data:image/') === 0) { return $src; }
                            $rel = '';
                            $abs = '';
                            if (stripos($s, 'file:///') === 0) {
                                $p = substr($s, 8);
                                $p = str_replace('%20', ' ', $p);
                                $abs = $p;
                            } elseif (preg_match('/^[A-Za-z]:[\\\\\\/]/', $s)) {
                                $abs = $s;
                            } elseif (preg_match('/^https?:\\/\\//i', $s)) {
                                if (stripos($s, $baseNorm) === 0) {
                                    $rel = ltrim(substr($s, strlen($baseNorm)), '/');
                                } else {
                                    return $src;
                                }
                            } else {
                                $rel = ltrim($s, '/');
                            }
                            if ($abs === '' && $rel !== '') {
                                $abs = rtrim((string)$root, "\\/") . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $rel);
                            }
                            if ($abs === '' || !is_file($abs) || filesize($abs) < 10) { return $src; }
                            $bin = @file_get_contents($abs);
                            if (!is_string($bin) || strlen($bin) < 10) { return $src; }
                            $mime = 'image/png';
                            if (function_exists('mime_content_type')) {
                                $mt = @mime_content_type($abs);
                                if (is_string($mt) && $mt !== '') { $mime = $mt; }
                            } elseif (function_exists('finfo_open')) {
                                try {
                                    $f = @finfo_open(FILEINFO_MIME_TYPE);
                                    if ($f) {
                                        $mt = @finfo_file($f, $abs);
                                        @finfo_close($f);
                                        if (is_string($mt) && $mt !== '') { $mime = $mt; }
                                    }
                                } catch (Throwable $e) {}
                            }
                            return 'data:' . $mime . ';base64,' . base64_encode($bin);
                        };
                        $html = preg_replace_callback('~src=(["\'])([^"\']*uploads/signatures/[^"\']+)\\1~i', static function ($m) use ($inlineProtected) {
                            $new = $inlineProtected((string)$m[2]);
                            return 'src=' . $m[1] . htmlspecialchars($new, ENT_QUOTES) . $m[1];
                        }, $html) ?: $html;
                }
            } catch (Throwable $e) {}
                try {
                    $sealOverride = '<style data-seal-fix="1">.seal-img{left:-8mm !important;z-index:1 !important;}.sig-img{left:-8mm !important;top:8mm !important;z-index:10 !important;}.sig-lead{position:relative !important;top:14mm !important;font-size:17px !important;font-weight:700 !important;}.sig-name{font-size:17px !important;font-weight:700 !important;}</style>';
                    if (stripos($html, 'data-seal-fix="1"') === false) {
                        $html = preg_replace('~</head>~i', $sealOverride . '</head>', $html, 1) ?: ($sealOverride . $html);
                    }
                } catch (Throwable $e) {}
                header('Content-Type: text/html; charset=UTF-8');
                header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
                header('Pragma: no-cache');
                header('Expires: 0');
                echo $html;
                exit;
            }
        }
        $frameSrc = $previewDocPath;
        if (preg_match('/^[A-Za-z]:[\\\\\\/]/', $frameSrc)) {
            $frameSrc = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $frameSrc);
            $frameSrc = str_replace(DIRECTORY_SEPARATOR, '/', $frameSrc);
        } elseif (strlen($frameSrc) > 0 && $frameSrc[0] === '/') {
            $baseDir = rtrim(str_replace('\\', '/', (string)__DIR__), '/') . '/';
            $fsNorm = str_replace('\\', '/', (string)$frameSrc);
            if (strpos($fsNorm, $baseDir) === 0) {
                $frameSrc = ltrim(substr($fsNorm, strlen($baseDir)), '/');
            }
        }
        $frameSrcRaw = trim((string)$frameSrc);
        if ($frameSrcRaw !== '') {
            $isHttp = preg_match('/^https?:\\/\\//i', $frameSrcRaw) === 1;
            $isServerAbs = (strlen($frameSrcRaw) > 0 && $frameSrcRaw[0] === '/' && preg_match('~^/(home|var|tmp|usr)/~i', $frameSrcRaw));
            $ok = false;
            if ($isHttp) {
                $curHost = strtolower((string)($_SERVER['HTTP_HOST'] ?? ''));
                $linkHost = '';
                if (preg_match('~^https?://([^/]+)~i', $frameSrcRaw, $m)) { $linkHost = strtolower((string)$m[1]); }
                $ok = ($curHost !== '' && $linkHost === $curHost);
            } elseif (!$isServerAbs) {
                $ok = true;
            }
            if ($ok) {
                $loc = $frameSrcRaw;
                $v = 0;
                if (isset($previewLocalPath) && is_string($previewLocalPath) && $previewLocalPath !== '' && is_file($previewLocalPath)) {
                    $v = (int)@filemtime($previewLocalPath);
                }
                if ($v <= 0 && isset($publicId) && is_string($publicId) && $publicId !== '') {
                    $v = (int)(crc32($publicId) & 0x7fffffff);
                }
                if ($v > 0) {
                    $loc .= (strpos($loc, '?') === false ? '?' : '&') . 'v=' . $v;
                }
                header('Location: ' . $loc);
                exit;
            }
        }
        $frameSrc = htmlspecialchars($frameSrc);
        header('Content-Type: text/html; charset=UTF-8');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        header('Pragma: no-cache');
        header('Expires: 0');
        echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Allocation Letter Preview</title><meta name="viewport" content="width=device-width, initial-scale=1"></head><body style="margin:0;background:#f3f4f6;">'
            . '<iframe src="' . $frameSrc . '" style="width:100%;height:100vh;border:0;background:#fff;"></iframe>'
            . '</body></html>';
        exit;
    }
    $clientName = '';
    $clientAddr = '';
    $clientPhone = '';
    $clientEmail = '';
    $passportUrl = '';
    $propertyTitle = '';
    $propertyCode = '';
    $plotSize = '';
    $estateLocation = '';
    $buildingUse = 'Residential';
    $houseType = '';
    $totalPrice = 0.0;
    $currency = '₦';
    $signatureUrl = '';
    $signatoryName = '';
    try {
        $officialChairmanSignatureUrl = 'https://aibenproperties.com/wp-content/uploads/2026/03/chairmans-signature.png';
        $signatoryName = function_exists('getSetting') ? (getSetting('allocation_signatory_name','') ?: '') : '';
        if ($signatoryName === '' && function_exists('getSetting')) {
            $signatoryName = getSetting('chairman_name','') ?: '';
        }
        if ($signatoryName === '' && isset($companyId) && $companyId) {
            try {
                $sn = $pdo->prepare("SELECT COALESCE(chairman_name,'') FROM companies WHERE id = ? LIMIT 1");
                $sn->execute([(int)$companyId]);
                $signatoryName = (string)($sn->fetchColumn() ?: '');
            } catch (Throwable $eCN) {}
        }
        if ($signatoryName === '') { $signatoryName = 'Authorized Signatory'; }
        $signatureUrl = $officialChairmanSignatureUrl;
    } catch (Throwable $e) {}
    try {
        $ctxPreview = loadAllocationLetterContext($pdo, (int)$allocId, $companyId, $isSuperAdmin);
        $cPrev = is_array($ctxPreview['client'] ?? null) ? $ctxPreview['client'] : [];
        $pPrev = is_array($ctxPreview['property'] ?? null) ? $ctxPreview['property'] : [];
        $bPrev = is_array($ctxPreview['computed'] ?? null) ? $ctxPreview['computed'] : [];
        $billPrev = is_array($ctxPreview['billing'] ?? null) ? $ctxPreview['billing'] : [];
        $clientName = (string)($cPrev['full_name'] ?? '');
        $clientAddr = (string)($cPrev['address'] ?? '');
        $clientPhone = (string)($cPrev['phone'] ?? '');
        $clientEmail = (string)($cPrev['email'] ?? '');
        $passportUrl = (string)($cPrev['passport_url'] ?? '');
        $propertyTitle = (string)($pPrev['estate_name'] ?? ($pPrev['property_name'] ?? ''));
        $propertyCode = (string)($pPrev['plot_number'] ?? '');
        $plotSize = (string)($pPrev['sqm'] ?? '');
        $buildingUse = (string)($pPrev['property_type'] ?? $buildingUse);
        $houseType = (string)($pPrev['house_type'] ?? '');
        $totalPrice = (float)($bPrev['grand_total'] ?? ($billPrev['land_cost'] ?? 0));
    } catch (Throwable $e) {}
    $previewData = [
        'client_name' => $clientName !== '' ? $clientName : '—',
        'client_address' => $clientAddr ?: '',
        'client_phone' => $clientPhone ?: '',
        'client_email' => $clientEmail ?: '',
        'property_title' => $propertyTitle !== '' ? $propertyTitle : ('Allocation #' . (int)$allocId),
        'property_code' => $propertyCode !== '' ? $propertyCode : ('ALC-'.$allocId),
        'plot_size' => $plotSize !== '' ? $plotSize : '—',
        'space_size' => ($plotSize !== '' ? $plotSize . ' SQM' : '—'),
        'building_use' => $buildingUse !== '' ? $buildingUse : '—',
        'house_type' => $houseType,
        'estate_location' => $estateLocation,
        'allocation_id' => $allocId,
        'reference' => 'AL-' . str_pad($allocId, 6, '0', STR_PAD_LEFT),
        'currency' => $currency,
        'total_price' => number_format($totalPrice, 2),
        'passport_url' => $passportUrl,
        'signature_url' => $signatureUrl,
        'signatory_name' => $signatoryName,
        'is_preview' => true,
        'render_target' => 'browser'
    ];
    $html = getAllocationLetterTemplateAiben($previewData);
    if ($canManageDraft && $viewMode === 'admin') {
        $pdfPreviewHref = 'allocation-letter-pdf.php?action=preview&allocation_id=' . (int)$allocId . '&regen=1';
        $pdfDownloadHref = 'allocation-letter-pdf.php?action=download&allocation_id=' . (int)$allocId . '&regen=1';
        $toolbar = '<div class="preview-toolbar" data-preview-ui="1" style="position:sticky;top:0;z-index:9999;background:#ffffff;border-bottom:1px solid #e5e7eb;padding:14px 18px;display:flex;gap:10px;align-items:center;justify-content:space-between;">'
            . '<div style="font-family:Arial,sans-serif;font-size:14px;font-weight:700;color:#111827;">Draft preview ready for admin review</div>'
            . '<div style="display:flex;gap:10px;align-items:center;">'
            . '<a target="_blank" rel="noopener" href="' . htmlspecialchars($pdfPreviewHref, ENT_QUOTES) . '" style="text-decoration:none;padding:10px 14px;border:1px solid #93c5fd;border-radius:10px;color:#1d4ed8;font:600 13px Arial,sans-serif;background:#eff6ff;">Preview PDF</a>'
            . '<a target="_blank" rel="noopener" href="' . htmlspecialchars($pdfDownloadHref, ENT_QUOTES) . '" style="text-decoration:none;padding:10px 14px;border:1px solid #16a34a;border-radius:10px;color:#ffffff;font:600 13px Arial,sans-serif;background:#16a34a;">Download PDF</a>'
            . '<a href="' . htmlspecialchars($allocationLettersBaseHref, ENT_QUOTES) . '" style="text-decoration:none;padding:10px 14px;border:1px solid #d1d5db;border-radius:10px;color:#111827;font:600 13px Arial,sans-serif;">Back</a>';
        if ($canSendToExecutive) {
            $toolbar .= '<form method="post" action="allocation-letters.php" onsubmit="return confirm(\'Send this allocation letter to the executive dashboard for approval?\');" style="margin:0;">'
                . '<input type="hidden" name="form_action" value="send_for_approval">'
                . '<input type="hidden" name="view" value="' . htmlspecialchars($viewMode, ENT_QUOTES) . '">'
                . '<input type="hidden" name="allocation_id" value="' . (int)$allocId . '">'
                . '<button type="submit" style="border:0;background:#111827;color:#fff;padding:10px 14px;border-radius:10px;font:600 13px Arial,sans-serif;cursor:pointer;">Send for Approval</button>'
                . '</form>';
        }
        $toolbar .= ''
            . '</div>'
            . '</div>';
        $html = preg_replace('/<body([^>]*)>/i', '<body$1>' . $toolbar, $html, 1) ?: $toolbar . $html;
    }
    header('Content-Type: text/html; charset=UTF-8');
    echo $html;
    exit;
}
if ($action === 'details' && $allocId > 0) {
    $user = [];
    $formData = [];
    $allocation = [];
    $property = [];
    $avatar = '';
    try {
        $st = $pdo->prepare("SELECT * FROM allocations WHERE id = ?" . ((!$isSuperAdmin && $companyId && colx('allocations','company_id')) ? " AND company_id = ?" : ""));
        $params = [$allocId];
        if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) { $params[] = $companyId; }
        $st->execute($params);
        $allocation = $st->fetch(PDO::FETCH_ASSOC) ?: [];
        $uid2 = (int)($allocation['user_id'] ?? 0);
        $pid2 = (int)($allocation['property_id'] ?? 0);
        if ($uid2 > 0) {
            $su = $pdo->prepare("SELECT * FROM users WHERE id = ? LIMIT 1");
            $su->execute([$uid2]);
            $user = $su->fetch(PDO::FETCH_ASSOC) ?: [];
            if (function_exists('getClientAvatarUrl')) { $avatar = getClientAvatarUrl($pdo, $uid2); }
            $sf = $pdo->prepare("SELECT form_data FROM client_forms WHERE client_id = ? ORDER BY updated_at DESC, created_at DESC LIMIT 1");
            $sf->execute([$uid2]);
            $row = $sf->fetch(PDO::FETCH_ASSOC);
            if ($row && !empty($row['form_data'])) {
                $tmp = json_decode($row['form_data'], true);
                if (is_array($tmp)) { $formData = $tmp; }
            }
        }
        if ($pid2 > 0) {
            $sp = $pdo->prepare("SELECT * FROM properties WHERE id = ? LIMIT 1");
            $sp->execute([$pid2]);
            $property = $sp->fetch(PDO::FETCH_ASSOC) ?: [];
        }
    } catch (Throwable $e) {}
    header('Content-Type: text/html; charset=UTF-8');
    $nm = $user['name'] ?? ($user['full_name'] ?? (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
    $email = $user['email'] ?? ($formData['email'] ?? '');
    $phone = $user['phone'] ?? ($formData['phone'] ?? '');
    $addr = $user['address'] ?? ($formData['address'] ?? ($formData['residential_address'] ?? ''));
    $company = $formData['company_name'] ?? '';
    $idDoc = $formData['id_document_path'] ?? '';
    $passport = $formData['passport_photo_path'] ?? $avatar;
    $title = $property['title'] ?? ($property['name'] ?? ('Property #'.($property['id'] ?? '')));
    $code = $property['code'] ?? ($property['plot_code'] ?? '');
    $size = $property['plot_size'] ?? ($property['area_sqm'] ?? '');
    echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Client Details</title><meta name="viewport" content="width=device-width, initial-scale=1">';
    echo '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">';
    echo '<style>body{font-family:Inter,Poppins,Arial,sans-serif;background:#f5f7fb;margin:0;padding:24px} .wrap{max-width:980px;margin:0 auto} .card{background:#fff;border:0;border-radius:16px;box-shadow:0 8px 24px rgba(0,0,0,.06);padding:22px;margin-bottom:18px} .hdr{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px} .title{font-size:22px;font-weight:800;margin:0} .grid{display:grid;grid-template-columns:1fr 1fr;gap:16px} .avatar{width:140px;height:140px;border-radius:12px;object-fit:cover;background:#e6e8ef} .row{display:flex;gap:12px;align-items:center;margin:6px 0} .label{color:#6b7280;min-width:140px;font-weight:600} .val{color:#111827;font-weight:700} .badge{display:inline-block;padding:6px 10px;border-radius:9999px;font-size:12px;border:1px solid #e5e7eb;background:#fff;color:#111827} .muted{color:#6b7280} .btn{display:inline-flex;align-items:center;gap:8px;padding:8px 12px;border:1px solid #e5e7eb;border-radius:10px;background:#fff;text-decoration:none;color:#111827} .btn:hover{background:#f8fafc} .media{display:flex;gap:16px;align-items:flex-start} .section{font-weight:700;margin:6px 0 8px 0} .two{display:grid;grid-template-columns:1fr 1fr;gap:8px} .kv{display:flex;justify-content:space-between;border:1px solid #e5e7eb;border-radius:10px;padding:10px} .kv .k{color:#6b7280} .kv .v{font-weight:700} .actions{display:flex;gap:8px}</style></head><body>';
    echo '<div class="wrap">';
    echo '<div class="hdr"><h1 class="title">Client Application Details</h1><div class="actions"><a class="btn" href="#" onclick="window.print();return false;"><i class=\"fa-solid fa-print\"></i>Print</a></div></div>';
    echo '<div class="card"><div class="media">';
    if (!empty($passport)) { echo '<img class="avatar" src="'.htmlspecialchars($passport).'" alt="Passport">'; }
    echo '<div>';
    echo '<div class="row"><div class="label">Name</div><div class="val">'.htmlspecialchars(trim($nm)).'</div></div>';
    echo '<div class="row"><div class="label">Email</div><div class="val">'.htmlspecialchars($email).'</div></div>';
    echo '<div class="row"><div class="label">Phone</div><div class="val">'.htmlspecialchars($phone).'</div></div>';
    echo '<div class="row"><div class="label">Address</div><div class="val">'.htmlspecialchars($addr).'</div></div>';
    if ($company) { echo '<div class="row"><div class="label">Company</div><div class="val">'.htmlspecialchars($company).'</div></div>'; }
    if (!empty($idDoc)) { echo '<div class="row"><div class="label">ID Document</div><div class="val"><a class="btn" href="'.htmlspecialchars($idDoc).'" target="_blank"><i class=\"fa-solid fa-id-card\"></i>View</a></div></div>'; }
    echo '</div></div></div>';
    echo '<div class="card"><div class="section">Allocation & Property</div><div class="two">';
    echo '<div class="kv"><div class="k">Allocation ID</div><div class="v">#'.(int)$allocId.'</div></div>';
    echo '<div class="kv"><div class="k">Allocation Date</div><div class="v">'.htmlspecialchars($allocation['allocation_date'] ?? '').'</div></div>';
    echo '<div class="kv"><div class="k">Property</div><div class="v">'.htmlspecialchars($title).'</div></div>';
    echo '<div class="kv"><div class="k">Plot/Code</div><div class="v">'.htmlspecialchars($code).'</div></div>';
    echo '<div class="kv"><div class="k">Plot Size</div><div class="v">'.htmlspecialchars((string)$size).'</div></div>';
    echo '<div class="kv"><div class="k">Status</div><div class="v"><span class="badge">'.htmlspecialchars($allocation['status'] ?? 'N/A').'</span></div></div>';
    echo '</div></div>';
    echo '<div class="card"><div class="section">Form Fields</div>';
    if (!empty($formData)) {
        echo '<div class="grid">';
        $shown = 0;
        foreach ($formData as $k => $v) {
            if (is_array($v)) continue;
            $val = is_scalar($v) ? (string)$v : '';
            if ($val === '' || strlen($k) > 60) continue;
            $label = ucwords(str_replace(['_','-'],' ', $k));
            echo '<div class="kv"><div class="k">'.htmlspecialchars($label).'</div><div class="v">'.htmlspecialchars($val).'</div></div>';
            $shown++;
        }
        if ($shown === 0) { echo '<div class="muted">No structured form fields found.</div>'; }
        echo '</div>';
    } else {
        echo '<div class="muted">No saved client form found.</div>';
    }
    echo '</div>';
    echo '</div></body></html>';
    exit;
}
if ($action === 'preview_pdf' && $allocId > 0) {
    header('Content-Type: application/pdf');
    try {
        $coverRel = function_exists('getSetting') ? (getSetting('allocation_letter_cover_path', '') ?? '') : '';
        if ($coverRel) {
            $root = __DIR__;
            $coverAbs = $root . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $coverRel);
            if (file_exists($coverAbs)) {
                // If FPDI exists, we could overlay minimal fields; otherwise just stream the cover PDF
                if (class_exists('\\setasign\\Fpdi\\Fpdi')) {
                    $dir = __DIR__ . '/uploads/documents/generated';
                    if (!is_dir($dir)) { @mkdir($dir, 0777, true); }
                    $pdfPath = $dir . '/Allocation_Cover_Preview_' . $allocId . '_' . time() . '.pdf';
                    if (!class_exists('\\setasign\\Fpdi\\Fpdi')) {
                        // Fallback: stream the original cover file if FPDI is not available
                        readfile($coverAbs);
                        exit;
                    }
                    if (!class_exists('\setasign\Fpdi\Fpdi')) {
                        // Fallback: stream the original cover file if FPDI is not available
                        readfile($coverAbs);
                        exit;
                    }
                    $fpdi = new \setasign\Fpdi\Fpdi();
                    $fpdi->AddPage();
                    $fpdi->setSourceFile($coverAbs);
                    $tplId = $fpdi->importPage(1);
                    $fpdi->useTemplate($tplId, 0, 0, 210, 297);
                    $fpdi->Output($pdfPath, 'F');
                    readfile($pdfPath);
                    exit;
                } else {
                    readfile($coverAbs);
                    exit;
                }
            }
        }
    } catch (Throwable $e) {}
    http_response_code(404);
    echo '%PDF-1.4% Allocation Cover Not Found';
    exit;
}
include __DIR__ . '/includes/header.php';
$rows = [];
try {
    $hasUsers = $pdo->query("SHOW TABLES LIKE 'users'")->rowCount() > 0;
    $hasProps = $pdo->query("SHOW TABLES LIKE 'properties'")->rowCount() > 0;
    $hasEstates = $pdo->query("SHOW TABLES LIKE 'estates'")->rowCount() > 0;
    $hasDealsSubmit = $pdo->query("SHOW TABLES LIKE 'deals_submit'")->rowCount() > 0;
    $hasPayments = $pdo->query("SHOW TABLES LIKE 'payments'")->rowCount() > 0;
    $nameExpr = $hasUsers && colx('users','name') ? 'u.name' : ($hasUsers && colx('users','full_name') ? 'u.full_name' : "NULL");
    $pTitleExpr = $hasProps && colx('properties','title') ? 'p.title' : ($hasProps && colx('properties','name') ? 'p.name' : "NULL");
    $allocPriceExpr = "0";
    if (colx('allocations','total_price')) { $allocPriceExpr = "a.total_price"; }
    elseif (colx('allocations','total_amount')) { $allocPriceExpr = "a.total_amount"; }
    elseif (colx('allocations','amount')) { $allocPriceExpr = "a.amount"; }
    elseif (colx('allocations','price')) { $allocPriceExpr = "a.price"; }
    elseif (colx('allocations','final_price')) { $allocPriceExpr = "a.final_price"; }
    elseif (colx('allocations','sale_price')) { $allocPriceExpr = "a.sale_price"; }
    $plotCol = "NULL";
    if (colx('allocations','plot_number')) { $plotCol = "a.plot_number"; }
    elseif (colx('allocations','unit_number')) { $plotCol = "a.unit_number"; }
    elseif (colx('allocations','plot_no')) { $plotCol = "a.plot_no"; }
    elseif (colx('allocations','property_code')) { $plotCol = "a.property_code"; }
    $plotSizeCol = "NULL";
    if (colx('allocations','plot_size')) { $plotSizeCol = "a.plot_size"; }
    elseif ($hasProps && colx('properties','plot_size')) { $plotSizeCol = "p.plot_size"; }
    elseif ($hasProps && colx('properties','area_sqm')) { $plotSizeCol = "p.area_sqm"; }
    $estateJoin = " LEFT JOIN (SELECT NULL AS id, NULL AS name) e ON 1=0";
    if ($hasEstates) {
        if (colx('allocations','estate_id')) {
            $estateJoin = " LEFT JOIN estates e ON a.estate_id = e.id";
        } elseif ($hasProps && colx('properties','estate_id')) {
            $estateJoin = " LEFT JOIN estates e ON p.estate_id = e.id";
        }
    }
    $dealJoin = " LEFT JOIN (SELECT NULL AS id, NULL AS amount_offered) d ON 1=0";
    if ($hasDealsSubmit && colx('allocations','deal_id') && colx('deals_submit','amount_offered')) {
        $dealJoin = " LEFT JOIN deals_submit d ON a.deal_id = d.id";
    }
    $paidExpr = "0";
    if ($hasPayments) {
        $paidMatchParts = [];
        if (colx('payments','allocation_id')) { $paidMatchParts[] = "allocation_id = a.id"; }
        if (colx('payments','deal_id') && colx('allocations','deal_id')) { $paidMatchParts[] = "(a.deal_id IS NOT NULL AND deal_id = a.deal_id)"; }
        $paidExpr = $paidMatchParts
            ? "(SELECT COALESCE(SUM(amount), 0) FROM payments WHERE status IN ('approved','paid') AND (" . implode(' OR ', $paidMatchParts) . "))"
            : "0";
    }

    $sql = "SELECT a.id AS allocation_id, a.user_id, a.property_id, a.status, a.allocation_date,
                   e.name AS estate_name,
                   {$plotCol} AS plot_number,
                   {$plotSizeCol} AS plot_size,
                   COALESCE(NULLIF({$allocPriceExpr},0), " . ($hasProps && colx('properties','price') ? "p.price" : "0") . ", d.amount_offered, 0) AS total_price,
                   {$paidExpr} AS total_paid";
    if ($nameExpr !== "NULL") { $sql .= ", {$nameExpr} AS client_name"; }
    if ($pTitleExpr !== "NULL") { $sql .= ", {$pTitleExpr} AS property_title"; }
    $sql .= " FROM allocations a";
    if ($nameExpr !== "NULL" && $hasUsers) { $sql .= " LEFT JOIN users u ON a.user_id = u.id"; }
    if ($pTitleExpr !== "NULL" && $hasProps) { $sql .= " LEFT JOIN properties p ON a.property_id = p.id"; }
    $sql .= $estateJoin;
    $sql .= $dealJoin;
    $sql .= " WHERE 1=1";
    $params = [];
    if (colx('allocations','status')) {
        if ($viewMode === 'executive') {
            $execAll = ['pending_executive_approval','executive_approved','chairman_approved','released_to_client','sent_to_client','completed','rejected'];
            if ($statusPreset === 'pending') {
                $sql .= " AND a.status IN ('pending_executive_approval')";
            } elseif ($statusPreset === 'approved') {
                $sql .= " AND a.status IN ('executive_approved','chairman_approved')";
            } elseif ($statusPreset === 'completed') {
                $sql .= " AND a.status IN ('released_to_client','sent_to_client','completed')";
            } elseif ($statusPreset === 'rejected') {
                $sql .= " AND a.status IN ('rejected')";
            } else {
                $sql .= " AND a.status IN ('" . implode("','", $execAll) . "')";
            }
        } else {
            $sql .= " AND (a.status IN ('pending','pending_executive_approval','pending_chairman_approval','pending_head_admin_review','draft_prepared','returned_for_correction','changes_requested','approved','admin_approved','executive_approved','chairman_approved','released','released_to_client','sent_to_client','completed','rejected') OR a.status IS NULL)";
        }
    }
    if (!$isSuperAdmin && $companyId && colx('allocations','company_id')) { $sql .= " AND (a.company_id = ? OR a.company_id IS NULL OR a.company_id = 0)"; $params[] = $companyId; }
    $sql .= " ORDER BY a.id DESC LIMIT 50";
    $st = $pdo->prepare($sql); $st->execute($params);
    $rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) {}
$isExecutiveViewer = $viewMode === 'executive';
$canFinalizeApproval = in_array($roleNorm, ['chairman_ceo','super_admin'], true);
$pageTitle = $isExecutiveViewer ? 'Allocation Letters — Executive Review' : 'Allocation Letters — Admin Workflow';
$pageSubtitle = $isExecutiveViewer
    ? 'Review, approve, and finalize property allocation documents.'
    : 'Generate, preview, and send allocation letters to the executive dashboard.';
$primaryNavHref = $isExecutiveViewer ? 'executive-dashboard.php' : 'management-dashboard.php';
$primaryNavLabel = $isExecutiveViewer ? 'Executive Dashboard' : 'Management Dashboard';
$secondaryNavHref = $isExecutiveViewer ? 'allocation-letters.php?view=executive&status=pending' : $allocationLettersBaseHref;
$secondaryNavLabel = $isExecutiveViewer ? 'Pending Approvals' : 'Workflow Overview';
$tableTitle = $isExecutiveViewer ? 'Executive Allocation Letter Queue' : 'Admin Allocation Letter Workflow';
$statusUiMap = [
    'pending_executive_approval' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'pending_chairman_approval' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'pending_head_admin_review' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'draft_prepared' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'returned_for_correction' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'changes_requested' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'admin_approved' => ['key' => 'approved', 'label' => 'Approved', 'tone' => 'approved'],
    'chairman_approved' => ['key' => 'approved', 'label' => 'Approved', 'tone' => 'approved'],
    'released' => ['key' => 'completed', 'label' => 'Completed', 'tone' => 'completed'],
    'released_to_client' => ['key' => 'completed', 'label' => 'Completed', 'tone' => 'completed'],
    'sent_to_client' => ['key' => 'completed', 'label' => 'Completed', 'tone' => 'completed'],
    'pending' => ['key' => 'pending', 'label' => 'Pending Review', 'tone' => 'pending'],
    'approved' => ['key' => 'approved', 'label' => 'Approved', 'tone' => 'approved'],
    'executive_approved' => ['key' => 'approved', 'label' => 'Approved', 'tone' => 'approved'],
    'completed' => ['key' => 'completed', 'label' => 'Completed', 'tone' => 'completed'],
    'rejected' => ['key' => 'rejected', 'label' => 'Rejected', 'tone' => 'rejected'],
];
$dashboardCounts = ['pending' => 0, 'approved' => 0, 'completed' => 0, 'total' => count($rows)];
foreach ($rows as &$row) {
    $rowStatus = strtolower((string)($row['status'] ?? ''));
    $statusMeta = $statusUiMap[$rowStatus] ?? ['key' => 'other', 'label' => ucwords(str_replace('_', ' ', $rowStatus ?: 'unknown')), 'tone' => 'neutral'];
    $row['status_key'] = $statusMeta['key'];
    $row['status_label'] = $statusMeta['label'];
    $row['status_tone'] = $statusMeta['tone'];
    $row['is_pending_review'] = $statusMeta['key'] === 'pending';
    if (isset($dashboardCounts[$statusMeta['key']])) {
        $dashboardCounts[$statusMeta['key']]++;
    }
}
unset($row);
?>
<style>
.executive-letters-page{
    --exec-bg:#f4f7fb;
    --exec-surface:#ffffff;
    --exec-border:#e6edf5;
    --exec-border-strong:#d7e3f2;
    --exec-text:#112136;
    --exec-muted:#6b7a90;
    --exec-blue:#2563eb;
    --exec-green:#16a34a;
    --exec-red:#dc2626;
    --exec-yellow:#eab308;
    --exec-yellow-soft:#fff8db;
    --exec-shadow:0 20px 45px rgba(15,23,42,.08);
    color:var(--exec-text);
}
.executive-letters-page .exec-breadcrumb{
    display:flex;
    align-items:center;
    gap:10px;
    margin-bottom:18px;
    color:var(--exec-muted);
    font-size:.92rem;
    font-weight:600;
}
.executive-letters-page .exec-breadcrumb a{
    color:var(--exec-muted);
    text-decoration:none;
}
.executive-letters-page .exec-breadcrumb a:hover{
    color:var(--exec-blue);
}
.executive-letters-page .exec-shell{
    display:grid;
    gap:24px;
}
.executive-letters-page .exec-hero{
    background:linear-gradient(135deg,#0f172a 0%, #1e3a5f 58%, #285d8f 100%);
    color:#fff;
    border:0;
    border-radius:20px;
    box-shadow:var(--exec-shadow);
    overflow:hidden;
}
.executive-letters-page .exec-hero .card-body{
    padding:28px;
}
.executive-letters-page .exec-hero-title{
    font-size:2rem;
    font-weight:800;
    margin:0 0 8px;
    color:#fff;
}
.executive-letters-page .exec-hero-subtitle{
    margin:0;
    color:rgba(255,255,255,.78);
    max-width:720px;
}
.executive-letters-page .exec-nav-pills{
    display:flex;
    flex-wrap:wrap;
    gap:10px;
    justify-content:flex-end;
}
.executive-letters-page .exec-nav-pill{
    display:inline-flex;
    align-items:center;
    gap:8px;
    padding:10px 14px;
    border-radius:999px;
    background:rgba(255,255,255,.08);
    border:1px solid rgba(255,255,255,.16);
    color:#fff;
    text-decoration:none;
    font-weight:600;
    transition:all .2s ease;
}
.executive-letters-page .exec-nav-pill:hover,
.executive-letters-page .exec-nav-pill.active{
    background:rgba(255,255,255,.16);
    color:#fff;
    transform:translateY(-1px);
}
.executive-letters-page .exec-stat-grid{
    display:grid;
    grid-template-columns:repeat(4, minmax(0,1fr));
    gap:18px;
}
.executive-letters-page .exec-stat-card{
    background:var(--exec-surface);
    border:1px solid var(--exec-border);
    border-radius:16px;
    box-shadow:0 14px 28px rgba(15,23,42,.05);
    padding:20px;
}
.executive-letters-page a.exec-stat-card{
    color:inherit;
    text-decoration:none;
    display:block;
}
.executive-letters-page a.exec-stat-card:hover{
    border-color:var(--exec-border-strong);
    box-shadow:0 18px 34px rgba(15,23,42,.08);
    transform:translateY(-1px);
}
.executive-letters-page .exec-stat-label{
    display:flex;
    align-items:center;
    gap:10px;
    margin-bottom:14px;
    color:var(--exec-muted);
    font-weight:700;
    font-size:.86rem;
    text-transform:uppercase;
    letter-spacing:.06em;
}
.executive-letters-page .exec-stat-icon{
    width:38px;
    height:38px;
    border-radius:12px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    font-size:1rem;
}
.executive-letters-page .exec-stat-card.pending .exec-stat-icon{ background:rgba(234,179,8,.16); color:#a16207; }
.executive-letters-page .exec-stat-card.approved .exec-stat-icon{ background:rgba(37,99,235,.14); color:#1d4ed8; }
.executive-letters-page .exec-stat-card.completed .exec-stat-icon{ background:rgba(22,163,74,.14); color:#15803d; }
.executive-letters-page .exec-stat-card.total .exec-stat-icon{ background:rgba(15,23,42,.08); color:#0f172a; }
.executive-letters-page .exec-stat-value{
    font-size:2rem;
    font-weight:800;
    line-height:1;
    margin-bottom:6px;
}
.executive-letters-page .exec-stat-meta{
    color:var(--exec-muted);
    font-size:.92rem;
}
.executive-letters-page .exec-board{
    background:var(--exec-surface);
    border:1px solid var(--exec-border);
    border-radius:18px;
    box-shadow:var(--exec-shadow);
    overflow:hidden;
}
.executive-letters-page .exec-board *{
    max-width:100%;
}
.side-drawer-backdrop{
    position:fixed;
    inset:0;
    background:rgba(15,23,42,.45);
    opacity:0;
    pointer-events:none;
    transition:opacity .2s ease;
    z-index:1049;
}
.side-drawer-backdrop.show{
    opacity:1;
    pointer-events:auto;
}
.side-drawer{
    position:fixed;
    top:0;
    right:0;
    width:560px;
    max-width:100%;
    height:100vh;
    background:#ffffff;
    box-shadow:0 30px 70px rgba(15,23,42,.25);
    transform:translateX(104%);
    transition:transform .22s ease;
    z-index:1050;
    display:flex;
    flex-direction:column;
    border-left:1px solid rgba(226,232,240,.85);
}
.side-drawer.open{
    transform:translateX(0);
}
.side-drawer #drawerContent{
    overflow:auto;
    -webkit-overflow-scrolling:touch;
}
.side-drawer .sticky-bottom{
    position:sticky;
    bottom:0;
}
.executive-letters-page .exec-board-head{
    padding:22px 24px 16px;
    border-bottom:1px solid var(--exec-border);
}
.executive-letters-page .exec-board-title{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:16px;
    margin-bottom:16px;
}
.executive-letters-page .exec-board-title h2{
    margin:0;
    font-size:1.15rem;
    font-weight:800;
}
.executive-letters-page .exec-board-title p{
    margin:6px 0 0;
    color:var(--exec-muted);
}
.executive-letters-page .exec-live-count{
    display:inline-flex;
    align-items:center;
    gap:8px;
    padding:10px 14px;
    border-radius:999px;
    background:#eef4ff;
    color:#1d4ed8;
    font-weight:700;
    font-size:.9rem;
}
.executive-letters-page .exec-toolbar{
    display:grid;
    grid-template-columns:minmax(220px,1.7fr) minmax(180px,.9fr) minmax(170px,.8fr);
    gap:12px;
}
.executive-letters-page .exec-control{
    position:relative;
}
.executive-letters-page .exec-control i{
    position:absolute;
    top:50%;
    left:14px;
    transform:translateY(-50%);
    color:#94a3b8;
}
.executive-letters-page .exec-input,
.executive-letters-page .exec-select{
    width:100%;
    min-height:40px;
    border:1px solid var(--exec-border-strong);
    border-radius:12px;
    padding:10px 14px;
    background:#fff;
    color:var(--exec-text);
    font-weight:600;
    transition:border-color .2s ease, box-shadow .2s ease;
}
.executive-letters-page .exec-control .exec-input{
    padding-left:40px;
}
.executive-letters-page .exec-input:focus,
.executive-letters-page .exec-select:focus{
    border-color:#9dbaf5;
    box-shadow:0 0 0 4px rgba(37,99,235,.12);
    outline:0;
}
.executive-letters-page .exec-table-wrap{
    padding:8px 10px 14px;
}
.executive-letters-page .exec-table{
    margin:0;
    border-collapse:separate;
    border-spacing:0 10px;
}
.executive-letters-page .exec-table thead th{
    border:0;
    color:var(--exec-muted);
    font-size:.78rem;
    text-transform:uppercase;
    letter-spacing:.08em;
    font-weight:800;
    padding:0 16px 8px;
}
.executive-letters-page .exec-table tbody tr{
    background:#fff;
    box-shadow:0 10px 20px rgba(15,23,42,.04);
    transition:transform .18s ease, box-shadow .18s ease, background-color .18s ease;
    cursor:pointer;
}
.executive-letters-page .exec-table tbody tr.is-focused{
    background:rgba(37,99,235,.08);
    box-shadow:inset 4px 0 0 var(--exec-blue), 0 12px 22px rgba(37,99,235,.12);
}
.executive-letters-page .exec-table tbody tr:hover{
    transform:translateY(-1px);
    box-shadow:0 16px 28px rgba(15,23,42,.07);
}
.executive-letters-page .exec-table tbody tr.pending-review{
    background:var(--exec-yellow-soft);
    box-shadow:inset 4px 0 0 #f0b90b, 0 10px 20px rgba(234,179,8,.12);
}
.executive-letters-page .exec-table tbody td{
    border:0;
    padding:14px 16px;
    vertical-align:middle;
}
.executive-letters-page .exec-table tbody tr td:first-child{
    border-radius:14px 0 0 14px;
}
.executive-letters-page .exec-table tbody tr td:last-child{
    border-radius:0 14px 14px 0;
}
.executive-letters-page .exec-entity-title{
    font-weight:800;
    color:var(--exec-text);
}
.executive-letters-page .exec-entity-meta{
    color:var(--exec-muted);
    font-size:.88rem;
    margin-top:4px;
}
.executive-letters-page .exec-badge{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    padding:7px 12px;
    border-radius:999px;
    font-size:.78rem;
    font-weight:800;
    letter-spacing:.02em;
}
.executive-letters-page .exec-badge.pending{ background:rgba(234,179,8,.18); color:#9a6700; }
.executive-letters-page .exec-badge.approved{ background:rgba(37,99,235,.12); color:#1d4ed8; }
.executive-letters-page .exec-badge.completed{ background:rgba(22,163,74,.14); color:#15803d; }
.executive-letters-page .exec-badge.rejected{ background:rgba(220,38,38,.12); color:#b91c1c; }
.executive-letters-page .exec-badge.neutral{ background:#eef2f7; color:#475569; }
.executive-letters-page .exec-actions{
    display:flex;
    flex-wrap:wrap;
    gap:8px;
    justify-content:flex-start;
}
.executive-letters-page .exec-btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    gap:8px;
    min-height:38px;
    padding:9px 12px;
    border-radius:12px;
    border:1px solid transparent;
    font-size:.86rem;
    font-weight:700;
    text-decoration:none;
    transition:all .2s ease;
}
.executive-letters-page .exec-btn:hover{
    transform:translateY(-1px);
}
.executive-letters-page .exec-btn-secondary{
    background:#fff;
    border-color:var(--exec-border-strong);
    color:#1f2937;
}
.executive-letters-page .exec-btn-secondary:hover{
    background:#f8fbff;
    color:#0f172a;
}
.executive-letters-page .exec-btn-approve{
    background:rgba(22,163,74,.12);
    color:#15803d;
    border-color:rgba(22,163,74,.2);
}
.executive-letters-page .exec-btn-approve:hover{
    background:rgba(22,163,74,.18);
    color:#166534;
}
.executive-letters-page .exec-btn-reject{
    background:rgba(220,38,38,.1);
    color:#b91c1c;
    border-color:rgba(220,38,38,.16);
}
.executive-letters-page .exec-btn-reject:hover{
    background:rgba(220,38,38,.16);
    color:#991b1b;
}
.executive-letters-page .exec-btn-primary-dark{
    background:#0f172a;
    color:#fff;
}
.executive-letters-page .exec-btn-primary-dark:hover{
    background:#111f38;
    color:#fff;
}
.executive-letters-page .exec-btn[disabled]{
    opacity:.45;
    pointer-events:none;
    transform:none;
}
.executive-letters-page .exec-empty{
    display:grid;
    place-items:center;
    gap:12px;
    padding:60px 20px 70px;
    text-align:center;
    color:var(--exec-muted);
}
.executive-letters-page .exec-empty i{
    width:64px;
    height:64px;
    border-radius:20px;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    background:#eef4ff;
    color:#2563eb;
    font-size:1.4rem;
}
.executive-letters-page .exec-empty strong{
    display:block;
    color:var(--exec-text);
    font-size:1.02rem;
}
.executive-letters-page .exec-alert{
    margin:0;
    border:0;
    border-radius:14px;
    box-shadow:0 10px 20px rgba(15,23,42,.05);
}
.executive-letters-page .exec-modal .modal-content{
    border:0;
    border-radius:18px;
    box-shadow:0 24px 55px rgba(15,23,42,.18);
}
.executive-letters-page .exec-modal .modal-header,
.executive-letters-page .exec-modal .modal-footer{
    border-color:#edf2f7;
    padding:18px 22px;
}
.executive-letters-page .exec-modal .modal-body{
    padding:20px 22px;
}
.executive-letters-page .exec-modal .form-control{
    border-radius:14px;
    border:1px solid var(--exec-border-strong);
    min-height:46px;
}
.executive-letters-page .exec-modal .form-control:focus{
    border-color:#9dbaf5;
    box-shadow:0 0 0 4px rgba(37,99,235,.12);
}
@media (max-width: 1199.98px){
    .executive-letters-page .exec-stat-grid{
        grid-template-columns:repeat(2, minmax(0,1fr));
    }
}
@media (max-width: 991.98px){
    .executive-letters-page .exec-toolbar{
        grid-template-columns:1fr;
        gap:10px;
    }
    .executive-letters-page .exec-nav-pills{
        justify-content:flex-start;
    }
    .executive-letters-page .exec-input,
    .executive-letters-page .exec-select{
        min-height:38px;
        padding:9px 12px;
        border-radius:12px;
    }
}
@media (max-width: 1024px){
    .executive-letters-page .exec-hero .card-body{
        padding:22px;
    }
    .executive-letters-page .exec-hero-title{
        font-size:1.55rem;
    }
    .executive-letters-page .exec-stat-grid{
        grid-template-columns:1fr;
    }
    .executive-letters-page .exec-table thead{
        display:none;
    }
    .executive-letters-page .exec-table,
    .executive-letters-page .exec-table tbody,
    .executive-letters-page .exec-table tr,
    .executive-letters-page .exec-table td{
        display:block;
        width:100%;
    }
    .executive-letters-page .exec-table tbody tr{
        padding:8px 0;
    }
    .executive-letters-page .exec-table tbody td{
        padding:10px 16px;
    }
    .executive-letters-page .exec-table tbody tr td:first-child,
    .executive-letters-page .exec-table tbody tr td:last-child{
        border-radius:0;
    }
    .executive-letters-page .exec-actions{
        display:grid;
        grid-template-columns:1fr;
    }
    .executive-letters-page .exec-btn{
        width:100%;
        justify-content:center;
    }
    .side-drawer{
        width:100%;
        max-width:100%;
    }
    .side-drawer .p-4{
        padding:16px !important;
    }
}
</style>
<div class="container-fluid px-4 py-4">
    <div class="executive-letters-page">
        <div class="exec-breadcrumb">
            <a href="<?= htmlspecialchars($primaryNavHref) ?>">Dashboard</a>
            <i class="fa-solid fa-chevron-right"></i>
            <span>Allocation Letters</span>
        </div>
        <div class="exec-shell">
            <div class="card exec-hero">
                <div class="card-body">
                    <div class="row g-4 align-items-center">
                        <div class="col-xl-8">
                            <h1 class="exec-hero-title"><?= htmlspecialchars($pageTitle) ?></h1>
                            <p class="exec-hero-subtitle"><?= htmlspecialchars($pageSubtitle) ?></p>
                        </div>
                        <div class="col-xl-4">
                            <div class="exec-nav-pills">
                                <a href="<?= htmlspecialchars($primaryNavHref) ?>" class="exec-nav-pill">
                                    <i class="fa-solid fa-gauge-high"></i>
                                    <span><?= htmlspecialchars($primaryNavLabel) ?></span>
                                </a>
                                <a href="<?= htmlspecialchars($secondaryNavHref) ?>" class="exec-nav-pill">
                                    <i class="fa-solid fa-list-check"></i>
                                    <span><?= htmlspecialchars($secondaryNavLabel) ?></span>
                                </a>
                                <a href="<?= htmlspecialchars($allocationLettersBaseHref) ?>" class="exec-nav-pill active">
                                    <i class="fa-solid fa-file-signature"></i>
                                    <span>Allocation Letters</span>
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="exec-stat-grid">
                <a class="exec-stat-card pending" href="allocation-letters.php?view=executive&amp;status=pending">
                    <div class="exec-stat-label"><span class="exec-stat-icon"><i class="fa-solid fa-hourglass-half"></i></span>Pending Review</div>
                    <div class="exec-stat-value"><?= number_format((int)$dashboardCounts['pending']) ?></div>
                    <div class="exec-stat-meta">Priority items needing chairman attention</div>
                </a>
                <a class="exec-stat-card approved" href="allocation-letters.php?view=executive&amp;status=approved">
                    <div class="exec-stat-label"><span class="exec-stat-icon"><i class="fa-solid fa-circle-check"></i></span>Approved</div>
                    <div class="exec-stat-value"><?= number_format((int)$dashboardCounts['approved']) ?></div>
                    <div class="exec-stat-meta">Allocation letters already cleared for workflow progression</div>
                </a>
                <a class="exec-stat-card completed" href="allocation-letters.php?view=executive&amp;status=completed">
                    <div class="exec-stat-label"><span class="exec-stat-icon"><i class="fa-solid fa-flag-checkered"></i></span>Completed</div>
                    <div class="exec-stat-value"><?= number_format((int)$dashboardCounts['completed']) ?></div>
                    <div class="exec-stat-meta">Documents fully closed and issued</div>
                </a>
                <a class="exec-stat-card total" href="allocation-letters.php?view=executive&amp;status=all">
                    <div class="exec-stat-label"><span class="exec-stat-icon"><i class="fa-solid fa-layer-group"></i></span>Total Queue</div>
                    <div class="exec-stat-value"><?= number_format((int)$dashboardCounts['total']) ?></div>
                    <div class="exec-stat-meta">Executive review records in the current dashboard view</div>
                </a>
            </div>

            <?php if (!empty($_SESSION['error_msg'])): ?>
                <div class="alert alert-warning exec-alert"><?= htmlspecialchars($_SESSION['error_msg']); unset($_SESSION['error_msg']); ?></div>
            <?php endif; ?>
            <?php if (!empty($_SESSION['success_msg'])): ?>
                <div class="alert alert-success exec-alert"><?= htmlspecialchars($_SESSION['success_msg']); unset($_SESSION['success_msg']); ?></div>
            <?php endif; ?>

            <div class="exec-board">
                <div class="exec-board-head">
                    <div class="exec-board-title">
                        <div>
                            <h2><?= htmlspecialchars($tableTitle) ?></h2>
                            <p>Search the queue, filter decisions, and move quickly on high-priority allocation letters.</p>
                        </div>
                        <div class="exec-live-count">
                            <i class="fa-solid fa-list-ul"></i>
                            <span><span id="visibleAllocationCount"><?= number_format((int)$dashboardCounts['total']) ?></span> items visible</span>
                        </div>
                    </div>
                    <div class="exec-toolbar">
                        <div class="exec-control">
                            <i class="fa-solid fa-magnifying-glass"></i>
                            <input type="search" id="allocationSearchInput" class="exec-input" placeholder="Search by client name or allocation ID">
                        </div>
                        <div>
                            <select id="allocationStatusFilter" class="exec-select">
                                <option value="all">All Statuses</option>
                                <option value="pending">Pending Review</option>
                                <option value="approved">Approved</option>
                                <option value="completed">Completed</option>
                                <option value="rejected">Rejected</option>
                            </select>
                        </div>
                        <div>
                            <input type="date" id="allocationDateFilter" class="exec-input">
                        </div>
                    </div>
                </div>

                <?php if (!empty($rows)): ?>
                    <div class="exec-table-wrap">
                        <div class="table-responsive">
                            <table class="table exec-table align-middle" id="executiveAllocationTable">
                                <thead>
                                    <tr>
                                        <th>Allocation ID</th>
                                        <th>Estate + Plot</th>
                                        <th>Client</th>
                                        <th>Status</th>
                                        <th>Payment</th>
                                        <th>Progress</th>
                                        <th>Action</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <?php foreach ($rows as $r): ?>
                                        <?php
                                            $rawStatus = strtolower((string)($r['status'] ?? ''));
                                            $canSendForApproval = $canSendToExecutive && !in_array($rawStatus, ['pending_executive_approval','executive_approved','completed','released','released_to_client'], true);
                                            $canDecide = ($r['status_key'] ?? '') === 'pending';
                                            $clientLabel = $r['client_name'] ?? ('Client #'.(int)($r['user_id'] ?? 0));
                                            $propertyLabel = $r['property_title'] ?? ('Property #'.(int)($r['property_id'] ?? 0));
                                            $estateLabel = trim((string)($r['estate_name'] ?? '')) !== '' ? (string)$r['estate_name'] : $propertyLabel;
                                            $plotBits = [];
                                            if (!empty($r['plot_number'])) { $plotBits[] = 'Plot ' . (string)$r['plot_number']; }
                                            if (!empty($r['plot_size'])) { $plotBits[] = (string)$r['plot_size']; }
                                            $plotLine = $plotBits ? implode(' • ', $plotBits) : '';
                                            $price = (float)($r['total_price'] ?? 0);
                                            $paid = (float)($r['total_paid'] ?? 0);
                                            $balance = max(0.0, $price - $paid);
                                            $percent = $price > 0 ? min(100, (int)round(($paid / $price) * 100)) : 0;
                                            $barTone = $percent >= 100 ? 'success' : (($percent > 0) ? 'warning' : 'danger');
                                            $fmtPaid = function_exists('formatCurrency') ? formatCurrency($paid) : ('₦' . number_format($paid, 2));
                                            $fmtPrice = function_exists('formatCurrency') ? formatCurrency($price) : ('₦' . number_format($price, 2));
                                            $fmtBal = function_exists('formatCurrency') ? formatCurrency($balance) : ('₦' . number_format($balance, 2));
                                            $searchBlob = strtolower(trim((string)$clientLabel . ' ' . (string)$propertyLabel . ' ' . (string)$estateLabel . ' ' . (string)$plotLine . ' #' . (int)$r['allocation_id'] . ' ' . (int)$r['allocation_id']));
                                            $rowDate = '';
                                            if (!empty($r['allocation_date'])) {
                                                try {
                                                    $rowDate = date('Y-m-d', strtotime((string)$r['allocation_date']));
                                                } catch (Throwable $e) {
                                                    $rowDate = '';
                                                }
                                            }
                                        ?>
                                        <tr class="<?= !empty($r['is_pending_review']) ? 'pending-review' : '' ?><?= $focusedAllocationId === (int)$r['allocation_id'] ? ' is-focused' : '' ?>" data-allocation-id="<?= (int)$r['allocation_id'] ?>" data-search="<?= htmlspecialchars($searchBlob) ?>" data-status="<?= htmlspecialchars((string)($r['status_key'] ?? 'other')) ?>" data-date="<?= htmlspecialchars($rowDate) ?>" data-detail-url="allocation-letters.php?view=<?= urlencode($viewMode) ?>&action=details&allocation_id=<?= (int)$r['allocation_id'] ?>">
                                            <td>
                                                <div class="exec-entity-title">#<?= (int)$r['allocation_id'] ?></div>
                                                <div class="exec-entity-meta"><?= !empty($r['allocation_date']) ? date('M j, Y', strtotime((string)$r['allocation_date'])) : 'Date not available' ?></div>
                                            </td>
                                            <td>
                                                <div class="exec-entity-title"><?= htmlspecialchars($estateLabel) ?></div>
                                                <div class="exec-entity-meta"><?= htmlspecialchars($plotLine !== '' ? $plotLine : $propertyLabel) ?></div>
                                                <?php if ($plotLine !== '' && $propertyLabel !== $estateLabel): ?>
                                                    <div class="exec-entity-meta"><?= htmlspecialchars($propertyLabel) ?></div>
                                                <?php endif; ?>
                                            </td>
                                            <td>
                                                <div class="exec-entity-title"><?= htmlspecialchars($clientLabel) ?></div>
                                                <div class="exec-entity-meta">Client allocation profile</div>
                                            </td>
                                            <td>
                                                <span class="exec-badge <?= htmlspecialchars((string)($r['status_tone'] ?? 'neutral')) ?>"><?= htmlspecialchars((string)($r['status_label'] ?? '-')) ?></span>
                                            </td>
                                            <td>
                                                <div class="exec-entity-title"><?= htmlspecialchars($fmtPaid) ?></div>
                                                <div class="exec-entity-meta">of <?= htmlspecialchars($fmtPrice) ?> • Bal <?= htmlspecialchars($fmtBal) ?></div>
                                            </td>
                                            <td style="min-width:140px;">
                                                <div class="d-flex justify-content-between small text-muted mb-1">
                                                    <span><?= (int)$percent ?>%</span>
                                                </div>
                                                <div class="progress" style="height:6px; background:#eef2f7;">
                                                    <div class="progress-bar bg-<?= htmlspecialchars($barTone) ?>" role="progressbar" style="width: <?= (int)$percent ?>%"></div>
                                                </div>
                                            </td>
                                            <td>
                                                <div class="exec-actions">
                                                    <a class="exec-btn exec-btn-secondary" target="_blank" href="allocation-letters.php?view=<?= urlencode($viewMode) ?>&action=preview&allocation_id=<?= (int)$r['allocation_id'] ?>&force=1">
                                                        <i class="fa-regular fa-eye"></i>
                                                        <span><?= $isExecutiveViewer ? 'Preview' : 'Preview Draft' ?></span>
                                                    </a>
                                                    <?php if (!$isExecutiveViewer): ?>
                                                        <button type="button" class="exec-btn exec-btn-secondary" onclick="openAllocationDetailsDrawer(<?= (int)$r['allocation_id'] ?>); event.stopPropagation();">
                                                            <i class="fa-solid fa-circle-info"></i>
                                                            <span>Allocation Details</span>
                                                        </button>
                                                        <a class="exec-btn exec-btn-secondary" target="_blank" href="allocation-letters.php?view=<?= urlencode($viewMode) ?>&action=details&allocation_id=<?= (int)$r['allocation_id'] ?>">
                                                            <i class="fa-solid fa-file-lines"></i>
                                                            <span>Letter Details</span>
                                                        </a>
                                                    <?php endif; ?>
                                                    <?php if ($isExecutiveViewer): ?>
                                                        <?php if ($canDecide): ?>
                                                            <button type="button" class="exec-btn exec-btn-approve" data-bs-toggle="modal" data-bs-target="#execDecisionModal" data-id="<?= (int)$r['allocation_id'] ?>" data-decision="approve" data-client="<?= htmlspecialchars((string)$clientLabel) ?>">
                                                                <i class="fa-solid fa-circle-check"></i>
                                                                <span>Approve</span>
                                                            </button>
                                                            <button type="button" class="exec-btn exec-btn-secondary" data-bs-toggle="modal" data-bs-target="#execDecisionModal" data-id="<?= (int)$r['allocation_id'] ?>" data-decision="request_changes" data-client="<?= htmlspecialchars((string)$clientLabel) ?>">
                                                                <i class="fa-solid fa-rotate-left"></i>
                                                                <span>Request Changes</span>
                                                            </button>
                                                            <button type="button" class="exec-btn exec-btn-reject" data-bs-toggle="modal" data-bs-target="#execDecisionModal" data-id="<?= (int)$r['allocation_id'] ?>" data-decision="reject" data-client="<?= htmlspecialchars((string)$clientLabel) ?>">
                                                                <i class="fa-solid fa-ban"></i>
                                                                <span>Reject</span>
                                                            </button>
                                                        <?php endif; ?>
                                                    <?php else: ?>
                                                        <?php if ($canManageApproval): ?>
                                                            <form action="generate_document.php" method="POST" target="_blank" class="d-inline">
                                                                <input type="hidden" name="doc_type" value="allocation_letter">
                                                                <input type="hidden" name="target_id" value="<?= (int)$r['allocation_id'] ?>">
                                                                <button type="submit" class="exec-btn exec-btn-secondary">
                                                                    <i class="fa-solid fa-wand-magic-sparkles"></i>
                                                                    <span>Generate Draft</span>
                                                                </button>
                                                            </form>
                                                        <?php endif; ?>
                                                        <?php if ($canSendForApproval): ?>
                                                            <form method="post" class="d-inline" onsubmit="return confirm('Send this allocation letter to the executive dashboard for approval?');">
                                                                <input type="hidden" name="form_action" value="send_for_approval">
                                                                <input type="hidden" name="view" value="<?= htmlspecialchars($viewMode) ?>">
                                                                <input type="hidden" name="allocation_id" value="<?= (int)$r['allocation_id'] ?>">
                                                                <button type="submit" class="exec-btn exec-btn-primary-dark">
                                                                    <i class="fa-solid fa-paper-plane"></i>
                                                                    <span>Send for Approval</span>
                                                                </button>
                                                            </form>
                                                        <?php elseif ($rawStatus === 'pending_executive_approval'): ?>
                                                            <button type="button" class="exec-btn exec-btn-secondary" disabled>
                                                                <i class="fa-solid fa-hourglass-half"></i>
                                                                <span>Pending Executive Review</span>
                                                            </button>
                                                        <?php elseif ($rawStatus === 'executive_approved'): ?>
                                                            <form method="post" class="d-inline" onsubmit="return confirm('Send this approved allocation letter to the client dashboard?');">
                                                                <input type="hidden" name="form_action" value="release_to_client">
                                                                <input type="hidden" name="view" value="<?= htmlspecialchars($viewMode) ?>">
                                                                <input type="hidden" name="allocation_id" value="<?= (int)$r['allocation_id'] ?>">
                                                                <button type="submit" class="exec-btn exec-btn-approve">
                                                                    <i class="fa-solid fa-paper-plane"></i>
                                                                    <span>Send to Client Dashboard</span>
                                                                </button>
                                                            </form>
                                                        <?php elseif (in_array($rawStatus, ['completed','released_to_client'], true)): ?>
                                                            <button type="button" class="exec-btn exec-btn-secondary" disabled>
                                                                <i class="fa-solid fa-check-double"></i>
                                                                <span>Released to Client</span>
                                                            </button>
                                                        <?php endif; ?>
                                                    <?php endif; ?>
                                                    <?php if (colx('allocations','letter_issued_at')): ?>
                                                        <form method="post" action="ajax_update_document_status.php" class="d-inline">
                                                            <input type="hidden" name="entity" value="allocation_letter">
                                                            <input type="hidden" name="allocation_id" value="<?= (int)$r['allocation_id'] ?>">
                                                            <input type="hidden" name="action" value="mark_issued">
                                                            <button class="exec-btn exec-btn-secondary" type="submit">
                                                                <i class="fa-solid fa-check-double"></i>
                                                                <span>Mark Issued</span>
                                                            </button>
                                                        </form>
                                                    <?php endif; ?>
                                                </div>
                                            </td>
                                        </tr>
                                    <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>
                <?php endif; ?>

                <div class="exec-empty" id="allocationEmptyState" <?= !empty($rows) ? 'hidden' : '' ?>>
                    <i class="fa-solid fa-file-circle-check"></i>
                    <div>
                        <strong>No allocation letters pending review</strong>
                        <div>No new executive review items match the current queue or filter selection.</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="side-drawer-backdrop" id="drawerBackdrop" onclick="closeAllocationDetailsDrawer()"></div>
<div class="side-drawer" id="sideDrawer">
    <div class="p-4 border-bottom d-flex justify-content-between align-items-center bg-white sticky-top">
        <div>
            <h5 class="mb-1 fw-bold text-contrast">Allocation Details</h5>
            <span class="badge bg-light text-dark border me-2" id="detailId">#Loading...</span>
            <span id="detailStatus"></span>
        </div>
        <button type="button" class="btn-close" onclick="closeAllocationDetailsDrawer()"></button>
    </div>
    <div class="p-4 flex-grow-1" id="drawerContent">
        <div class="text-center py-5"><div class="spinner-border text-navy" role="status"></div></div>
    </div>
    <div class="p-4 border-top bg-light mt-auto sticky-bottom">
        <div class="d-grid gap-2 d-md-flex justify-content-md-end" id="drawerActions">
            <button class="btn btn-outline-secondary" onclick="closeAllocationDetailsDrawer()">Close</button>
        </div>
    </div>
</div>
<div class="modal fade" id="billingModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog modal-lg modal-dialog-scrollable">
        <form class="modal-content" id="billingForm">
            <div class="modal-header">
                <div>
                    <h5 class="modal-title mb-0">Edit Billing</h5>
                    <div class="text-muted small" id="billingModalSub">Allocation #—</div>
                </div>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <input type="hidden" name="form_action" value="save_billing">
                <input type="hidden" name="xhr" value="1">
                <input type="hidden" name="allocation_id" id="billingAllocationId" value="">
                <div class="row g-3">
                    <div class="col-12 col-md-6">
                        <label class="form-label" id="billLandCostLabel">Land Cost</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="land_cost" id="billLandCost">
                    </div>
                    <div class="col-12 col-md-6">
                        <label class="form-label">VAT %</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="vat_percent" id="billVatPercent">
                    </div>

                    <div class="col-12">
                        <label class="form-label">Infrastructure Charge Mode</label>
                        <div class="d-flex gap-3 flex-wrap">
                            <div class="form-check">
                                <input class="form-check-input" type="radio" name="infra_mode" id="infraModePercent" value="percent" checked>
                                <label class="form-check-label" for="infraModePercent">Percentage of base cost</label>
                            </div>
                            <div class="form-check">
                                <input class="form-check-input" type="radio" name="infra_mode" id="infraModeFixed" value="fixed">
                                <label class="form-check-label" for="infraModeFixed">Fixed amount</label>
                            </div>
                        </div>
                    </div>
                    <div class="col-12 col-md-6">
                        <label class="form-label">Infrastructure %</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="infra_percent" id="billInfraPercent">
                    </div>
                    <div class="col-12 col-md-6">
                        <label class="form-label">Infrastructure Amount</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="infra_amount" id="billInfraAmount">
                    </div>

                    <div class="col-12 col-md-6">
                        <label class="form-label">Excavation Fee</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="excavation_fee" id="billExcavationFee">
                    </div>
                    <div class="col-12 col-md-6">
                        <label class="form-label">Construction Supervision</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="construction_supervision" id="billSupervision">
                    </div>
                    <div class="col-12 col-md-6">
                        <label class="form-label">Approval Fee</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="approval_fee" id="billApprovalFee">
                    </div>
                    <div class="col-12 col-md-6">
                        <label class="form-label">Application Form Fee</label>
                        <input type="number" step="0.01" min="0" class="form-control" name="application_form_fee" id="billApplicationFee">
                    </div>

                    <div class="col-12">
                        <div class="fw-bold small text-uppercase text-muted">Optional Items (show in letter only if enabled)</div>
                    </div>
                    <div class="col-12 col-md-6">
                        <div class="form-check mb-2">
                            <input class="form-check-input" type="checkbox" name="include_fence" id="includeFence">
                            <label class="form-check-label" for="includeFence">Fence / Gate</label>
                        </div>
                        <input type="number" step="0.01" min="0" class="form-control" name="fence_gate_cost" id="billFenceCost" placeholder="Fence / Gate cost">
                    </div>
                    <div class="col-12 col-md-6">
                        <div class="form-check mb-2">
                            <input class="form-check-input" type="checkbox" name="include_carcass" id="includeCarcass">
                            <label class="form-check-label" for="includeCarcass">Carcass</label>
                        </div>
                        <input type="number" step="0.01" min="0" class="form-control" name="carcass_cost" id="billCarcassCost" placeholder="Carcass cost">
                    </div>
                    <div class="col-12 col-md-6">
                        <div class="form-check mb-2">
                            <input class="form-check-input" type="checkbox" name="include_exterior_finishing" id="includeExterior">
                            <label class="form-check-label" for="includeExterior">Exterior Finishing</label>
                        </div>
                        <input type="number" step="0.01" min="0" class="form-control" name="exterior_finishing_cost" id="billExteriorCost" placeholder="Exterior finishing cost">
                    </div>
                    <div class="col-12 col-md-6">
                        <div class="form-check mb-2">
                            <input class="form-check-input" type="checkbox" name="include_dpc" id="includeDpc">
                            <label class="form-check-label" for="includeDpc">DPC</label>
                        </div>
                        <input type="number" step="0.01" min="0" class="form-control" name="dpc_cost" id="billDpcCost" placeholder="DPC cost">
                    </div>

                    <div class="col-12 col-md-6">
                        <label class="form-label">Payment Plan</label>
                        <select class="form-select" id="paymentPlanPreset">
                            <option value="3">3 months</option>
                            <option value="6">6 months</option>
                            <option value="custom">Custom</option>
                        </select>
                        <input type="number" step="1" min="1" class="form-control mt-2" name="payment_plan_months" id="paymentPlanMonths" value="3">
                    </div>

                    <div class="col-12 col-md-6">
                        <label class="form-label">Grand Total (auto)</label>
                        <input type="text" class="form-control" id="billGrandTotal" value="₦0.00" readonly>
                        <div class="text-muted small mt-1">Subtotal and VAT are calculated automatically.</div>
                    </div>
                </div>
                <div class="alert alert-danger mt-3 d-none" id="billingError"></div>
                <div class="alert alert-success mt-3 d-none" id="billingSuccess">Saved.</div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
                <button type="submit" class="btn btn-primary" id="billingSaveBtn">Save Billing</button>
            </div>
        </form>
    </div>
</div>
<?php if ($isExecutiveViewer): ?>
<div class="modal fade executive-letters-page exec-modal" id="execDecisionModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog">
        <form method="post" action="<?= htmlspecialchars($allocationLettersBaseHref . '&status=' . urlencode($statusPreset)) ?>" class="modal-content">
            <div class="modal-header">
                <div>
                    <h5 class="modal-title mb-1" id="execDecisionTitle">Confirm Decision</h5>
                    <div class="text-muted small" id="execDecisionSubtitle">Review the selected allocation action before continuing.</div>
                </div>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <input type="hidden" name="exec_action" value="1">
                <input type="hidden" name="allocation_id" id="execDecisionAllocationId">
                <input type="hidden" name="decision" id="execDecisionValue">
                <div class="alert alert-light border mb-3" id="execDecisionContext">You are about to confirm an executive decision for this allocation letter.</div>
                <div class="mb-3">
                    <label class="form-label fw-semibold">Comment</label>
                    <textarea name="comment" class="form-control" rows="4" required placeholder="Provide a clear approval or rejection reason"></textarea>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="exec-btn exec-btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button type="submit" class="exec-btn exec-btn-primary-dark" id="execDecisionSubmit">
                    <i class="fa-solid fa-circle-check"></i>
                    <span>Confirm Action</span>
                </button>
            </div>
        </form>
    </div>
</div>
<?php endif; ?>
<script>
(function(){
    const CAN_EDIT_BILLING = <?= $canManageApproval && $viewMode === 'admin' ? 'true' : 'false' ?>;
    function getStatusBadgeHTML(status) {
        const map = {
            pending: { icon: 'fa-clock', cls: 'status-badge-pending', label: 'Pending' },
            pending_executive_approval: { icon: 'fa-clock', cls: 'status-badge-pending', label: 'Pending' },
            approved: { icon: 'fa-check-circle', cls: 'status-badge-approved', label: 'Approved' },
            executive_approved: { icon: 'fa-check-circle', cls: 'status-badge-approved', label: 'Approved' },
            allocated: { icon: 'fa-check-double', cls: 'status-badge-paid', label: 'Allocated' },
            finalized: { icon: 'fa-lock', cls: 'status-badge-approved', label: 'Finalized' },
            completed: { icon: 'fa-lock', cls: 'status-badge-approved', label: 'Completed' },
            revoked: { icon: 'fa-ban', cls: 'status-badge-rejected', label: 'Revoked' },
            rejected: { icon: 'fa-circle-xmark', cls: 'status-badge-rejected', label: 'Rejected' }
        };
        const key = String(status || '').toLowerCase().replace(/\s+/g, '_');
        const cfg = map[key];
        if (!cfg) return `<span class="badge bg-secondary text-white">${String(status || 'Unknown')}</span>`;
        return `<span class="status-badge ${cfg.cls}"><i class="fa-solid ${cfg.icon}"></i> ${cfg.label}</span>`;
    }

    function fmtMoney(v) {
        const n = Number(v) || 0;
        return '₦' + n.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 });
    }

    window.openAllocationDetailsDrawer = function(id, openBilling) {
        const allocId = Number(id) || 0;
        if (!allocId) return;
        const shouldOpenBilling = Boolean(openBilling);
        const backdrop = document.getElementById('drawerBackdrop');
        const drawer = document.getElementById('sideDrawer');
        const content = document.getElementById('drawerContent');
        const detailId = document.getElementById('detailId');
        const detailStatus = document.getElementById('detailStatus');
        if (detailId) detailId.textContent = '#Loading...';
        if (detailStatus) detailStatus.innerHTML = '';
        if (content) content.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-navy" role="status"></div></div>';
        if (backdrop) backdrop.classList.add('show');
        if (drawer) drawer.classList.add('open');
        document.body.style.overflow = 'hidden';
        try { if (drawer) drawer.setAttribute('aria-hidden', 'false'); } catch(e) {}

        fetch('ajax_get_allocation_details.php?id=' + encodeURIComponent(String(allocId)), { credentials: 'same-origin' })
            .then(function(res){ return res.json(); })
            .then(function(data){
                if (!data || !data.success) {
                    const err = (data && data.error) ? data.error : 'Failed to load details.';
                    if (content) content.innerHTML = '<div class="alert alert-danger">' + String(err) + '</div>';
                    return;
                }
                renderAllocationDetails(data, { openBilling: shouldOpenBilling });
            })
            .catch(function(){
                if (content) content.innerHTML = '<div class="alert alert-danger">Failed to load details.</div>';
            });
    };

    window.closeAllocationDetailsDrawer = function() {
        const backdrop = document.getElementById('drawerBackdrop');
        const drawer = document.getElementById('sideDrawer');
        if (backdrop) backdrop.classList.remove('show');
        if (drawer) drawer.classList.remove('open');
        document.body.style.overflow = '';
        try { if (drawer) drawer.setAttribute('aria-hidden', 'true'); } catch(e) {}
        setTimeout(function(){
            const content = document.getElementById('drawerContent');
            const detailId = document.getElementById('detailId');
            const detailStatus = document.getElementById('detailStatus');
            if (content) content.innerHTML = '<div class="text-center py-5"><div class="spinner-border text-navy" role="status"></div></div>';
            if (detailId) detailId.textContent = '#...';
            if (detailStatus) detailStatus.innerHTML = '';
        }, 300);
    };

    document.addEventListener('keydown', function(e){
        if (e && e.key === 'Escape') {
            const drawer = document.getElementById('sideDrawer');
            if (drawer && drawer.classList.contains('open')) {
                window.closeAllocationDetailsDrawer();
            }
        }
    });

    function renderAllocationDetails(data, opts) {
        const openBilling = Boolean(opts && opts.openBilling);
        const alloc = data.allocation || {};
        const payments = Array.isArray(data.payments) ? data.payments : [];
        const docs = Array.isArray(data.documents) ? data.documents : [];
        const history = Array.isArray(data.edit_history) ? data.edit_history : [];
        const timeline = Array.isArray(data.timeline) ? data.timeline : [];
        const summary = data.payment_summary || {};
        const cost = data.cost_breakdown || {};
        const letter = (data && typeof data.letter_data === 'object' && data.letter_data) ? data.letter_data : {};

        const statusKey = String(alloc.status || '').toLowerCase().replace(/\s+/g, '_');
        const total = Number(summary.total_price ?? alloc.property_price ?? 0) || 0;
        const paid = Number(summary.total_paid ?? 0) || 0;
        const outstanding = Number(summary.outstanding ?? (total - paid)) || 0;
        const percent = total > 0 ? Math.min(100, Math.round((paid / total) * 100)) : 0;
        const barTone = percent >= 100 ? 'success' : (percent > 0 ? 'warning' : 'danger');

        const detailId = document.getElementById('detailId');
        const detailStatus = document.getElementById('detailStatus');
        if (detailId) detailId.textContent = '#' + String(alloc.id || '').padStart(6, '0');
        if (detailStatus) detailStatus.innerHTML = getStatusBadgeHTML(statusKey);

        const avatar = alloc.client_photo_b64 || alloc.client_photo || '';
        const clientInitial = String(alloc.client_name || 'C').charAt(0).toUpperCase();
        const clientPhotoHtml = avatar
            ? `<img src="${avatar}" style="width:40px;height:40px;border-radius:50%;object-fit:cover;">`
            : `<div class="avatar-ring" style="width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;">${clientInitial}</div>`;

        const propTitle = String(alloc.property_title || '—');
        const propAddr = String(alloc.property_address || alloc.location || '—');
        const unit = alloc.unit_number ? String(alloc.unit_number) : '';
        const plot = alloc.plot_number ? String(alloc.plot_number) : '';
        const buildingNumber = alloc.building_number ? String(alloc.building_number) : '';
        const houseType = alloc.house_type ? String(alloc.house_type) : '';
        const buildingUse = alloc.building_use ? String(alloc.building_use) : '';
        const areaSqm = alloc.area_sqm ? String(alloc.area_sqm) : (alloc.space_size ? String(alloc.space_size) : '');

        const decisionKeyRaw = String(letter.chairman_decision || alloc.exec_decision || '').toLowerCase();
        const decisionKey = (decisionKeyRaw === 'approve' || decisionKeyRaw === 'approved') ? 'approved'
            : (decisionKeyRaw === 'request_changes' || decisionKeyRaw === 'changes_requested' || decisionKeyRaw === 'returned_for_correction') ? 'changes_requested'
            : (decisionKeyRaw === 'reject' || decisionKeyRaw === 'rejected' || decisionKeyRaw === 'declined' || decisionKeyRaw === 'revoked') ? 'rejected'
            : '';
        const decisionLabel = decisionKey === 'approved' ? 'Approved'
            : (decisionKey === 'changes_requested' ? 'Changes Requested' : (decisionKey === 'rejected' ? 'Rejected' : ''));
        const decisionAtRaw = letter.chairman_decided_at || alloc.exec_decided_at || '';
        const decisionAt = decisionAtRaw ? new Date(decisionAtRaw).toLocaleString() : '';
        const decisionComment = String(letter.chairman_comment || alloc.exec_comment || alloc.review_comment || '').trim();
        const feedbackBadge = decisionKey ? getStatusBadgeHTML(decisionKey) : '';
        const feedbackHtml = (decisionComment || decisionLabel)
            ? `
                <div class="mb-4">
                    <h6 class="text-uppercase text-muted small fw-bold mb-3">Executive Feedback</h6>
                    <div class="card border p-3 rounded-3">
                        <div class="d-flex justify-content-between align-items-start">
                            <div>
                                ${decisionLabel ? `<div class="fw-bold">${decisionLabel}</div>` : `<div class="fw-bold">Feedback</div>`}
                                ${decisionAt ? `<div class="text-muted small">${decisionAt}</div>` : ``}
                            </div>
                            <div>${feedbackBadge}</div>
                        </div>
                        ${decisionComment ? `<div class="mt-2 small" style="white-space:pre-wrap;">${decisionComment.replace(/</g,'&lt;').replace(/>/g,'&gt;')}</div>` : `<div class="mt-2 small text-muted fst-italic">No note provided.</div>`}
                    </div>
                </div>
            `
            : '';

        const paymentsRows = payments.length
            ? payments.slice(0, 10).map(function(p){
                const dt = p.date ? new Date(p.date).toLocaleDateString() : '';
                const method = p.method || p.payment_method || 'Transfer';
                const amt = fmtMoney(p.amount);
                const st = getStatusBadgeHTML(p.status || '');
                return `<tr><td class="text-muted">${dt}</td><td>${method}</td><td class="text-end fw-bold">${amt}</td><td class="text-end">${st}</td></tr>`;
            }).join('')
            : '<tr><td colspan="4" class="text-muted fst-italic">No payments recorded</td></tr>';

        const docsRows = docs.length
            ? docs.slice(0, 10).map(function(d){
                const t = d.type || 'document';
                const title = d.title || '';
                const st = getStatusBadgeHTML(d.status || '');
                const open = d.file_path ? `<a href="${d.file_path}" target="_blank" class="btn btn-sm btn-light border">Open</a>` : '';
                return `<tr><td class="text-muted">${t}</td><td class="fw-bold">${title}</td><td class="text-end">${st}</td><td class="text-end">${open}</td></tr>`;
            }).join('')
            : '<tr><td colspan="4" class="text-muted fst-italic">No documents linked</td></tr>';

        const histRows = history.length
            ? history.slice(0, 10).map(function(h){
                const who = h.edited_by_name || (h.edited_by ? ('Admin #' + h.edited_by) : '—');
                const summaryTxt = h.summary || h.edit_note || '';
                const when = h.edited_at ? new Date(h.edited_at).toLocaleString() : '';
                return `<tr><td class="text-muted">${who}</td><td>${summaryTxt}</td><td class="text-end">${when}</td></tr>`;
            }).join('')
            : '<tr><td colspan="3" class="text-muted fst-italic">No edits recorded</td></tr>';

        const timelineRows = timeline.length
            ? timeline.map(function(step){
                const dt = step.date ? new Date(step.date).toLocaleDateString() : '--';
                const role = step.role || '';
                const label = step.step || '';
                const cls = step.status || '';
                return `<div class="timeline-item ${cls}"><div class="timeline-dot"></div><div class="d-flex justify-content-between"><span class="fw-bold small">${label}</span><span class="text-muted small" style="font-size:0.7rem;">${dt}</span></div><div class="small text-muted">${role}</div></div>`;
            }).join('')
            : '<div class="text-muted small fst-italic">No timeline available</div>';

        const costHtml = `
            <div class="row g-2 text-center">
                <div class="col-4">
                    <div class="p-2 border rounded">
                        <div class="small text-muted">Land</div>
                        <div class="fw-bold small">${fmtMoney(cost.land_cost)}</div>
                    </div>
                </div>
                <div class="col-4">
                    <div class="p-2 border rounded">
                        <div class="small text-muted">Infrastructure</div>
                        <div class="fw-bold small">${fmtMoney(cost.infrastructure_charge ?? cost.infra_lease)}</div>
                    </div>
                </div>
                <div class="col-4">
                    <div class="p-2 border rounded">
                        <div class="small text-muted">VAT</div>
                        <div class="fw-bold small">${fmtMoney(cost.vat)}</div>
                    </div>
                </div>
                <div class="col-12 mt-2">
                    <div class="p-2 border rounded bg-success bg-opacity-10 border-success">
                        <div class="small text-success">Grand Total</div>
                        <div class="fw-bold small text-success">${fmtMoney(cost.grand_total)}</div>
                    </div>
                </div>
            </div>
        `;

        const html = `
            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Client</h6>
                <div class="card border p-3 rounded-3">
                    <div class="d-flex align-items-center">
                        <div class="me-3">${clientPhotoHtml}</div>
                        <div>
                            <div class="fw-bold">${String(alloc.client_name || '—')}</div>
                            <div class="text-muted small">${String(alloc.client_email || '')}</div>
                            <div class="text-muted small">${String(alloc.client_phone || '')}</div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Property</h6>
                <div class="card border p-3 rounded-3">
                    <div class="d-flex justify-content-between mb-2">
                        <div class="fw-bold">${propTitle}</div>
                        <div class="fw-bold">${fmtMoney(alloc.property_price || total)}</div>
                    </div>
                    <div class="text-muted small">${propAddr}</div>
                    <div class="row g-2 mt-2 small">
                        ${buildingNumber ? `<div class="col-6"><span class="text-muted">Unit/Plot:</span> <span class="fw-semibold">${buildingNumber}</span></div>` : ''}
                        ${houseType ? `<div class="col-6"><span class="text-muted">Type:</span> <span class="fw-semibold">${houseType}</span></div>` : ''}
                        ${buildingUse ? `<div class="col-6"><span class="text-muted">Use:</span> <span class="fw-semibold">${buildingUse}</span></div>` : ''}
                        ${areaSqm ? `<div class="col-6"><span class="text-muted">Size:</span> <span class="fw-semibold">${areaSqm}</span></div>` : ''}
                        ${(unit || plot) && !buildingNumber ? `<div class="col-12"><span class="text-muted">Unit:</span> <span class="fw-semibold">${unit || plot}</span></div>` : ''}
                    </div>
                </div>
            </div>

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Allocation</h6>
                <div class="card border p-3 rounded-3">
                    <div class="row g-2 small">
                        <div class="col-6"><span class="text-muted">Agent:</span> <span class="fw-semibold">${String(alloc.agent_name || '—')}</span></div>
                        <div class="col-6"><span class="text-muted">Status:</span> <span class="fw-semibold">${String(alloc.status || '—')}</span></div>
                        <div class="col-6"><span class="text-muted">Created:</span> <span class="fw-semibold">${alloc.created_at ? new Date(alloc.created_at).toLocaleString() : '—'}</span></div>
                        <div class="col-6"><span class="text-muted">Updated:</span> <span class="fw-semibold">${alloc.updated_at ? new Date(alloc.updated_at).toLocaleString() : '—'}</span></div>
                    </div>
                </div>
            </div>

            ${feedbackHtml}

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Cost Breakdown</h6>
                <div class="card border p-3 rounded-3">${costHtml}</div>
            </div>

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Payment</h6>
                <div class="card border p-3 rounded-3">
                    <div class="row g-2 text-center">
                        <div class="col-4">
                            <div class="p-2 border rounded">
                                <div class="small text-muted">Total</div>
                                <div class="fw-bold small">${fmtMoney(total)}</div>
                            </div>
                        </div>
                        <div class="col-4">
                            <div class="p-2 border rounded bg-success bg-opacity-10 border-success">
                                <div class="small text-success">Paid</div>
                                <div class="fw-bold small text-success">${fmtMoney(paid)}</div>
                            </div>
                        </div>
                        <div class="col-4">
                            <div class="p-2 border rounded bg-danger bg-opacity-10 border-danger">
                                <div class="small text-danger">Balance</div>
                                <div class="fw-bold small text-danger">${fmtMoney(outstanding)}</div>
                            </div>
                        </div>
                    </div>
                    <div class="mt-2">
                        <div class="d-flex justify-content-between align-items-center">
                            <small class="text-muted">Payment Progress</small>
                            <small class="fw-bold">${percent}%</small>
                        </div>
                        <div class="progress-premium">
                            <div class="progress-bar-premium progress-bar-${barTone}" role="progressbar" style="width:${percent}%"></div>
                        </div>
                    </div>
                    <div class="mt-3">
                        <small class="fw-bold">Recent Transactions</small>
                        <table class="table table-sm table-borderless small mt-1">
                            <tbody>${paymentsRows}</tbody>
                        </table>
                    </div>
                </div>
            </div>

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Documents</h6>
                <div class="card border p-2 rounded-3">
                    <table class="table table-sm table-borderless small mb-0">
                        <tbody>${docsRows}</tbody>
                    </table>
                </div>
            </div>

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Edit History</h6>
                <div class="card border p-2 rounded-3">
                    <table class="table table-sm table-borderless small mb-0">
                        <thead><tr><th>Admin</th><th>Change</th><th class="text-end">Time</th></tr></thead>
                        <tbody>${histRows}</tbody>
                    </table>
                </div>
            </div>

            <div class="mb-4">
                <h6 class="text-uppercase text-muted small fw-bold mb-3">Workflow Timeline</h6>
                <div class="ps-2">${timelineRows}</div>
            </div>
        `;

        const content = document.getElementById('drawerContent');
        if (content) content.innerHTML = html;

        const actions = document.getElementById('drawerActions');
        if (actions) {
            const base =
                `<a class="btn btn-outline-dark" href="allocation-details.php?id=${encodeURIComponent(String(alloc.id || ''))}" target="_blank" rel="noopener"><i class="fa-solid fa-up-right-from-square me-2"></i>Open allocation details</a>`
                + `<button class="btn btn-outline-secondary" onclick="closeAllocationDetailsDrawer()">Close</button>`;
            if (CAN_EDIT_BILLING) {
                const st = String((alloc && alloc.status) ? alloc.status : '').toLowerCase();
                const canSendForApproval = !['pending_executive_approval','pending','review','executive_approved','completed','released','finalized'].includes(st);
                const canReleaseToClient = ['executive_approved','approved'].includes(st);
                actions.innerHTML =
                    `<form method="post" action="allocation-letters.php" class="d-inline" style="margin:0;">
                        <input type="hidden" name="form_action" value="save_draft">
                        <input type="hidden" name="view" value="admin">
                        <input type="hidden" name="allocation_id" value="${encodeURIComponent(String(alloc.id || ''))}">
                        <button type="submit" class="btn btn-outline-dark"><i class="fa-solid fa-floppy-disk me-2"></i>Save Draft</button>
                    </form>`
                    + `<a class="btn btn-outline-dark" href="allocation-letters.php?view=admin&action=letter_details&allocation_id=${encodeURIComponent(String(alloc.id || ''))}"><i class="fa-solid fa-file-pen me-2"></i>Letter Details</a>`
                    + `<button class="btn btn-outline-primary" id="openBillingBtn"><i class="fa-solid fa-pen-to-square me-2"></i>Edit Billing</button>`
                    + (canSendForApproval ? `<form method="post" action="allocation-letters.php" class="d-inline" style="margin:0;" onsubmit="return confirm('Send this allocation letter to the Chairman/Executive for approval?');">
                        <input type="hidden" name="form_action" value="send_for_approval">
                        <input type="hidden" name="view" value="admin">
                        <input type="hidden" name="allocation_id" value="${encodeURIComponent(String(alloc.id || ''))}">
                        <button type="submit" class="btn btn-primary"><i class="fa-solid fa-paper-plane me-2"></i>Send to Chairman Approval</button>
                    </form>` : ``)
                    + (canReleaseToClient ? `<form method="post" action="allocation-letters.php" class="d-inline" style="margin:0;" onsubmit="return confirm('Send this approved allocation letter to the Client Dashboard?');">
                        <input type="hidden" name="form_action" value="release_to_client">
                        <input type="hidden" name="view" value="admin">
                        <input type="hidden" name="allocation_id" value="${encodeURIComponent(String(alloc.id || ''))}">
                        <button type="submit" class="btn btn-success"><i class="fa-solid fa-paper-plane me-2"></i>Send to Client Dashboard</button>
                    </form>` : ``)
                    + `<a class="btn btn-outline-success" target="_blank" href="allocation-letters.php?view=admin&action=preview&allocation_id=${encodeURIComponent(String(alloc.id || ''))}&force=1"><i class="fa-solid fa-eye me-2"></i>Preview Draft</a>`
                    + `<a class="btn btn-outline-primary" target="_blank" href="allocation-letter-pdf.php?action=preview&allocation_id=${encodeURIComponent(String(alloc.id || ''))}&regen=1"><i class="fa-solid fa-file-pdf me-2"></i>Preview PDF</a>`
                    + `<a class="btn btn-success" target="_blank" href="allocation-letter-pdf.php?action=download&allocation_id=${encodeURIComponent(String(alloc.id || ''))}"><i class="fa-solid fa-download me-2"></i>Download PDF</a>`
                    + `<a class="btn btn-outline-secondary" href="allocation-letters.php?view=admin"><i class="fa-solid fa-list me-2"></i>Back to List</a>`
                    + base;
            } else {
                actions.innerHTML = base;
            }
        }

        if (CAN_EDIT_BILLING) {
            const btn = document.getElementById('openBillingBtn');
            if (btn) {
                btn.onclick = function() {
                    openBillingModal(alloc, data.billing_settings || {}, data.cost_breakdown || {});
                };
            }
        }
        if (CAN_EDIT_BILLING && openBilling) {
            openBillingModal(alloc, data.billing_settings || {}, data.cost_breakdown || {});
        }
    }

    function moneyNumber(v) {
        const n = Number(v);
        return isFinite(n) ? n : 0;
    }

    function computeBillingFromForm() {
        const land = moneyNumber(document.getElementById('billLandCost').value);
        const vatPct = moneyNumber(document.getElementById('billVatPercent').value);
        const infraMode = document.getElementById('infraModeFixed').checked ? 'fixed' : 'percent';
        const infraPct = moneyNumber(document.getElementById('billInfraPercent').value);
        const infraAmt = moneyNumber(document.getElementById('billInfraAmount').value);
        const excavation = moneyNumber(document.getElementById('billExcavationFee').value);
        const supervision = moneyNumber(document.getElementById('billSupervision').value);
        const approval = moneyNumber(document.getElementById('billApprovalFee').value);
        const appForm = moneyNumber(document.getElementById('billApplicationFee').value);

        const incFence = document.getElementById('includeFence').checked;
        const incCarcass = document.getElementById('includeCarcass').checked;
        const incExterior = document.getElementById('includeExterior').checked;
        const incDpc = document.getElementById('includeDpc').checked;

        const fence = incFence ? moneyNumber(document.getElementById('billFenceCost').value) : 0;
        const carcass = incCarcass ? moneyNumber(document.getElementById('billCarcassCost').value) : 0;
        const exterior = incExterior ? moneyNumber(document.getElementById('billExteriorCost').value) : 0;
        const dpc = incDpc ? moneyNumber(document.getElementById('billDpcCost').value) : 0;

        const infraCharge = infraMode === 'fixed' ? infraAmt : ((infraPct / 100) * land);
        const sub = land + infraCharge + excavation + supervision + approval + appForm + fence + carcass + exterior + dpc;
        const vat = (vatPct / 100) * sub;
        const grand = sub + vat;
        return { grand };
    }

    function syncInfraInputs() {
        const isFixed = document.getElementById('infraModeFixed').checked;
        document.getElementById('billInfraPercent').disabled = isFixed;
        document.getElementById('billInfraAmount').disabled = !isFixed;
    }

    function updateGrandTotalPreview() {
        const r = computeBillingFromForm();
        const el = document.getElementById('billGrandTotal');
        if (el) el.value = fmtMoney(r.grand);
    }

    function openBillingModal(alloc, billing, cost) {
        const modalEl = document.getElementById('billingModal');
        const bsModal = modalEl ? bootstrap.Modal.getOrCreateInstance(modalEl) : null;
        const allocId = Number(alloc.id || 0) || 0;
        document.getElementById('billingAllocationId').value = String(allocId);
        const sub = document.getElementById('billingModalSub');
        if (sub) sub.textContent = 'Allocation #' + String(allocId).padStart(6, '0');

        const bu = String(alloc.property_type || alloc.building_use || alloc.buildingUse || alloc.type || '').toLowerCase();
        const landLabel = (bu.includes('land'))
            ? 'Land Cost'
            : ((bu.includes('commercial') || bu.includes('shop') || bu.includes('suite') || bu.includes('office')) ? 'Unit Cost' : 'Property Cost');
        const lcLabel = document.getElementById('billLandCostLabel');
        if (lcLabel) lcLabel.textContent = landLabel;

        document.getElementById('billLandCost').value = moneyNumber(billing.land_cost ?? cost.land_cost ?? alloc.property_price ?? 0);
        document.getElementById('billVatPercent').value = moneyNumber(billing.vat_percent ?? cost.vat_percent ?? 7.5);

        const mode = String(billing.infra_mode || 'percent').toLowerCase();
        document.getElementById('infraModeFixed').checked = mode === 'fixed';
        document.getElementById('infraModePercent').checked = mode !== 'fixed';
        document.getElementById('billInfraPercent').value = moneyNumber(billing.infra_percent ?? 20);
        document.getElementById('billInfraAmount').value = moneyNumber(billing.infra_amount ?? 0);

        document.getElementById('billExcavationFee').value = moneyNumber(billing.excavation_fee ?? 0);
        document.getElementById('billSupervision').value = moneyNumber(billing.construction_supervision ?? 0);
        document.getElementById('billApprovalFee').value = moneyNumber(billing.approval_fee ?? 0);
        document.getElementById('billApplicationFee').value = moneyNumber(billing.application_form_fee ?? 0);

        document.getElementById('includeFence').checked = Number(billing.include_fence ?? 0) === 1;
        document.getElementById('includeCarcass').checked = Number(billing.include_carcass ?? 0) === 1;
        document.getElementById('includeExterior').checked = Number(billing.include_exterior_finishing ?? 0) === 1;
        document.getElementById('includeDpc').checked = Number(billing.include_dpc ?? 0) === 1;

        document.getElementById('billFenceCost').value = moneyNumber(billing.fence_gate_cost ?? 0);
        document.getElementById('billCarcassCost').value = moneyNumber(billing.carcass_cost ?? 0);
        document.getElementById('billExteriorCost').value = moneyNumber(billing.exterior_finishing_cost ?? 0);
        document.getElementById('billDpcCost').value = moneyNumber(billing.dpc_cost ?? 0);

        const months = Math.max(1, Number(billing.payment_plan_months ?? 3) || 3);
        document.getElementById('paymentPlanMonths').value = String(months);
        const preset = (months === 3 || months === 6) ? String(months) : 'custom';
        document.getElementById('paymentPlanPreset').value = preset;
        document.getElementById('paymentPlanMonths').disabled = preset !== 'custom';

        const err = document.getElementById('billingError');
        const ok = document.getElementById('billingSuccess');
        if (err) { err.classList.add('d-none'); err.textContent = ''; }
        if (ok) { ok.classList.add('d-none'); }

        syncInfraInputs();
        updateGrandTotalPreview();
        if (bsModal) bsModal.show();
    }

    const billingForm = document.getElementById('billingForm');
    if (billingForm) {
        const inputs = billingForm.querySelectorAll('input,select');
        inputs.forEach(function(el){
            el.addEventListener('input', function(){
                if (el.id === 'paymentPlanPreset') return;
                if (el.name === 'infra_mode') { syncInfraInputs(); }
                updateGrandTotalPreview();
            });
            el.addEventListener('change', function(){
                if (el.id === 'paymentPlanPreset') {
                    const v = el.value;
                    const monthsEl = document.getElementById('paymentPlanMonths');
                    if (v === 'custom') { monthsEl.disabled = false; }
                    else { monthsEl.disabled = true; monthsEl.value = v; }
                    updateGrandTotalPreview();
                }
                if (el.name === 'infra_mode') { syncInfraInputs(); updateGrandTotalPreview(); }
                if (el.type === 'checkbox') { updateGrandTotalPreview(); }
            });
        });

        billingForm.addEventListener('submit', async function(e){
            e.preventDefault();
            const err = document.getElementById('billingError');
            const ok = document.getElementById('billingSuccess');
            if (err) { err.classList.add('d-none'); err.textContent = ''; }
            if (ok) { ok.classList.add('d-none'); }
            const btn = document.getElementById('billingSaveBtn');
            const original = btn ? btn.innerHTML : '';
            if (btn) { btn.disabled = true; btn.innerHTML = 'Saving...'; }
            try {
                const fd = new FormData(billingForm);
                const res = await fetch('allocation-letters.php', { method: 'POST', body: fd, credentials: 'same-origin' });
                const json = await res.json().catch(function(){ return null; });
                if (!json || !json.success) {
                    const msg = (json && json.error) ? json.error : 'Failed to save.';
                    if (err) { err.textContent = msg; err.classList.remove('d-none'); }
                    return;
                }
                const allocId = document.getElementById('billingAllocationId').value;
                if (ok) {
                    ok.textContent = 'Saved. Opening letter preview...';
                    ok.classList.remove('d-none');
                }
                if (allocId) {
                    const data = await fetch('ajax_get_allocation_details.php?id=' + encodeURIComponent(String(allocId)), { credentials: 'same-origin' })
                        .then(function(r){ return r.json(); }).catch(function(){ return null; });
                    if (data && data.success) {
                        renderAllocationDetails(data);
                    }
                    try {
                        const modalEl = document.getElementById('billingModal');
                        const bsModal = modalEl ? bootstrap.Modal.getOrCreateInstance(modalEl) : null;
                        if (bsModal) { bsModal.hide(); }
                    } catch (e) {}
                    try {
                        const url = 'allocation-letters.php?view=admin&action=preview&allocation_id=' + encodeURIComponent(String(allocId)) + '&force=1';
                        window.open(url, '_blank');
                    } catch (e) {}
                }
            } catch (ex) {
                if (err) { err.textContent = 'Failed to save.'; err.classList.remove('d-none'); }
            } finally {
                if (btn) { btn.disabled = false; btn.innerHTML = original; }
            }
        });
    }

    const searchInput = document.getElementById('allocationSearchInput');
    const statusFilter = document.getElementById('allocationStatusFilter');
    const dateFilter = document.getElementById('allocationDateFilter');
    const table = document.getElementById('executiveAllocationTable');
    const emptyState = document.getElementById('allocationEmptyState');
    const visibleCounter = document.getElementById('visibleAllocationCount');
    const rows = table ? Array.from(table.querySelectorAll('tbody tr')) : [];
    const statusPreset = <?= json_encode($statusPreset) ?>;
    const focusedAllocationId = <?= json_encode($focusedAllocationId) ?>;
    const autoOpen = <?= json_encode(isset($_GET['auto_open']) && (string)$_GET['auto_open'] !== '0') ?>;
    const autoBilling = <?= json_encode(isset($_GET['auto_billing']) && (string)$_GET['auto_billing'] !== '0') ?>;

    function applyFilters() {
        const term = (searchInput ? searchInput.value : '').trim().toLowerCase();
        const status = statusFilter ? statusFilter.value : 'all';
        const date = dateFilter ? dateFilter.value : '';
        let visible = 0;

        rows.forEach(function(row){
            const rowSearch = (row.getAttribute('data-search') || '').toLowerCase();
            const rowStatus = row.getAttribute('data-status') || '';
            const rowDate = row.getAttribute('data-date') || '';
            const matchesTerm = !term || rowSearch.indexOf(term) !== -1;
            const matchesStatus = status === 'all' || rowStatus === status;
            const matchesDate = !date || rowDate === date;
            const show = matchesTerm && matchesStatus && matchesDate;
            row.hidden = !show;
            if (show) { visible += 1; }
        });

        if (visibleCounter) {
            visibleCounter.textContent = visible.toLocaleString();
        }
        if (emptyState) {
            emptyState.hidden = visible !== 0;
        }
    }

    if (statusFilter && statusPreset) {
        statusFilter.value = statusPreset;
    }
    if (searchInput) { searchInput.addEventListener('input', applyFilters); }
    if (statusFilter) { statusFilter.addEventListener('change', applyFilters); }
    if (dateFilter) { dateFilter.addEventListener('change', applyFilters); }
    applyFilters();

    if (focusedAllocationId) {
        const focusedRow = rows.find(function(row){
            return String(row.getAttribute('data-allocation-id') || '') === String(focusedAllocationId);
        });
        if (focusedRow && !focusedRow.hidden) {
            setTimeout(function(){
                focusedRow.scrollIntoView({ behavior: 'smooth', block: 'center' });
            }, 120);
        }
        if (autoOpen) {
            setTimeout(function(){
                window.openAllocationDetailsDrawer(focusedAllocationId, autoBilling);
            }, 220);
        }
    }

    rows.forEach(function(row){
        row.addEventListener('click', function(event){
            if (event.target.closest('a, button, form, input, textarea, select, label')) {
                return;
            }
            const allocId = row.getAttribute('data-allocation-id');
            if (allocId) {
                window.openAllocationDetailsDrawer(allocId);
            }
        });
    });

    const decisionModal = document.getElementById('execDecisionModal');
    if (decisionModal) {
        const titleEl = document.getElementById('execDecisionTitle');
        const subtitleEl = document.getElementById('execDecisionSubtitle');
        const contextEl = document.getElementById('execDecisionContext');
        const allocationInput = document.getElementById('execDecisionAllocationId');
        const decisionInput = document.getElementById('execDecisionValue');
        const submitBtn = document.getElementById('execDecisionSubmit');
        decisionModal.addEventListener('show.bs.modal', function (event) {
            const trigger = event.relatedTarget;
            if (!trigger) { return; }
            const allocationId = trigger.getAttribute('data-id') || '';
            const decision = trigger.getAttribute('data-decision') || 'approve';
            const client = trigger.getAttribute('data-client') || 'this client';
            const approveMode = decision === 'approve';
            const requestMode = decision === 'request_changes';
            if (allocationInput) allocationInput.value = allocationId;
            if (decisionInput) decisionInput.value = decision;
            if (titleEl) titleEl.textContent = approveMode ? 'Approve Allocation Letter' : (requestMode ? 'Request Changes' : 'Reject Allocation Letter');
            if (subtitleEl) subtitleEl.textContent = approveMode
                ? 'This decision moves the document forward in the executive workflow.'
                : (requestMode ? 'This returns the allocation letter to Admin for corrections.' : 'This decision sends the allocation back for corrective action.');
            if (contextEl) {
                const variant = approveMode ? 'alert-success' : (requestMode ? 'alert-warning' : 'alert-danger');
                contextEl.className = 'alert mb-3 ' + variant;
                contextEl.textContent = (approveMode ? 'Approve' : (requestMode ? 'Request changes for' : 'Reject')) + ' allocation #' + allocationId + ' for ' + client + '.';
            }
            if (submitBtn) {
                submitBtn.className = 'exec-btn ' + (approveMode ? 'exec-btn-approve' : (requestMode ? 'exec-btn-secondary' : 'exec-btn-reject'));
                submitBtn.innerHTML = approveMode
                    ? '<i class="fa-solid fa-circle-check"></i><span>Confirm Approval</span>'
                    : (requestMode ? '<i class="fa-solid fa-rotate-left"></i><span>Request Changes</span>' : '<i class="fa-solid fa-ban"></i><span>Confirm Rejection</span>');
            }
        });

        const modalForm = decisionModal.querySelector('form');
        if (modalForm) {
            let submitting = false;
            modalForm.addEventListener('submit', async function(event){
                if (submitting) { return; }
                event.preventDefault();
                const decision = (decisionInput && decisionInput.value) ? decisionInput.value : 'approve';
                const requestMode = decision === 'request_changes';
                const title = decision === 'approve' ? 'Approve Allocation Letter' : (requestMode ? 'Request Changes' : 'Reject Allocation Letter');
                const message = decision === 'approve'
                    ? 'Are you sure you want to approve this allocation letter?'
                    : (requestMode ? 'Request changes for this allocation letter?' : 'Are you sure you want to reject this allocation letter?');
                const variant = decision === 'approve' ? 'success' : (requestMode ? 'warning' : 'danger');
                const ok = window.showConfirm ? await window.showConfirm({ title, message, confirmText: 'Yes, continue', variant }) : window.confirm(message);
                if (!ok) { return false; }
                submitting = true;
                modalForm.submit();
            });
        }
    }

    document.querySelectorAll('.js-finalize-allocation').forEach(function(button){
        button.addEventListener('click', async function(event){
            event.preventDefault();
            const allocationId = button.getAttribute('data-id') || '';
            const previewUrl = button.getAttribute('data-preview-url') || '';
            if (!allocationId) { return; }
            const ok = window.showConfirm
                ? await window.showConfirm({ title: 'Finalize Allocation Letter', message: 'This will approve, sign, and release the allocation letter to the client portal.', confirmText: 'Finalize now', variant: 'success' })
                : window.confirm('Finalize and release this allocation letter?');
            if (!ok) { return; }
            const originalHtml = button.innerHTML;
            button.disabled = true;
            button.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i><span>Finalizing...</span>';
            try {
                const response = await fetch('start-approval.php', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: 'allocation_id=' + encodeURIComponent(allocationId)
                });
                const result = await response.json().catch(function(){ return null; });
                if (!result || !result.success) {
                    const blockers = result && Array.isArray(result.blockers) && result.blockers.length
                        ? '\n - ' + result.blockers.join('\n - ')
                        : '';
                    window.alert('Final approval was blocked.' + blockers);
                    return;
                }
                if (previewUrl) {
                    window.open(previewUrl, '_blank', 'noopener');
                }
                window.location.reload();
            } catch (error) {
                window.alert('Final approval failed.');
            } finally {
                button.disabled = false;
                button.innerHTML = originalHtml;
            }
        });
    });
})();
</script>
<?php include __DIR__ . '/includes/footer.php'; ?>

Youez - 2016 - github.com/yon3zu
LinuXploit