<?php
declare(strict_types=1);

/**
 * admin_personalize.php
 * Single-file admin panel with AJAX for:
 * - update price
 * - update request status + admin reply
 * - check status from personalization API
 * - retry submission when status is pending
 * - upload PDF
 * - download latest uploaded PDF
 */

session_start();

if (!isset($_SESSION['admin'])) {
    header("Location: admin_login.php");
    exit();
}

require_once __DIR__ . '/../config.php'; // adjust if needed

/*
|--------------------------------------------------------------------------
| Personalization API Config
|--------------------------------------------------------------------------
*/
define('PERSONALIZATION_API_BASE',   'https://www.s8v.ng');
define('PERSONALIZATION_SUBMIT_URL', PERSONALIZATION_API_BASE . '/api/personalization');
define('PERSONALIZATION_STATUS_URL', PERSONALIZATION_API_BASE . '/api/personalization/check');
define('PERSONALIZATION_API_TOKEN',  'rrUviNdggfSGuGlGbaZURpkfAX4ebiFn201n721faTlMILRbV3');
define('PERSONALIZATION_DEFAULT_TYPE', 'info');
define('RETRY_MAX_ATTEMPTS',         5);

/* -----------------------------
   Helpers
----------------------------- */
function e(?string $value): string
{
    return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}

function is_ajax_request(): bool
{
    return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
        strtolower((string)$_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
}

function json_response(bool $success, string $message, array $data = [], int $statusCode = 200): void
{
    http_response_code($statusCode);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode([
        'success' => $success,
        'message' => $message,
        'data'    => $data,
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit();
}

function ensure_csrf_token(): string
{
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function verify_csrf_or_fail(): void
{
    $token = (string)($_POST['csrf_token'] ?? '');
    $sessionToken = (string)($_SESSION['csrf_token'] ?? '');

    if ($sessionToken === '' || $token === '' || !hash_equals($sessionToken, $token)) {
        json_response(false, 'Invalid CSRF token.', [], 419);
    }
}

function format_money(float $value): string
{
    return number_format($value, 2);
}

function normalize_status(string $status): string
{
    $status = strtolower(trim($status));

    return match ($status) {
        'submitted'                                => 'pending',
        'pending'                                  => 'pending',
        'in progress', 'in_progress', 'in-progress', 'ipe',
        'processing'                               => 'processing',
        'successful', 'success',
        'completed', 'complete', 'done'            => 'completed',
        'failed', 'error', 'declined', 'rejected'  => 'failed',
        'invalid', 'not_found', 'not found'        => 'invalid',
        default                                    => $status !== '' ? $status : 'pending',
    };
}

function status_label(string $status): string
{
    return strtoupper(str_replace('_', ' ', normalize_status($status)));
}

function status_class(string $status): string
{
    return match (normalize_status($status)) {
        'pending'    => 'bad-pending',
        'processing' => 'bad-processing',
        'completed'  => 'bad-completed',
        'failed'     => 'bad-failed',
        'invalid'    => 'bad-invalid',
        default      => 'bad-pending',
    };
}

function column_exists(mysqli $conn, string $table, string $column): bool
{
    $stmt = $conn->prepare("
        SELECT COUNT(*) AS c
        FROM information_schema.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = ?
          AND COLUMN_NAME = ?
    ");

    if (!$stmt) {
        return false;
    }

    $stmt->bind_param("ss", $table, $column);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result ? $result->fetch_assoc() : null;
    $stmt->close();

    return (int)($row['c'] ?? 0) > 0;
}

function extract_api_message(array $payload, string $fallback = ''): string
{
    $candidates = [
        $payload['message'] ?? null,
        $payload['reply'] ?? null,
        $payload['error'] ?? null,
        $payload['data']['message'] ?? null,
        $payload['data']['reply'] ?? null,
        $payload['data']['admin_reply'] ?? null,
        $payload['data']['status'] ?? null,
    ];

    foreach ($candidates as $candidate) {
        if (is_string($candidate) && trim($candidate) !== '') {
            return trim($candidate);
        }
    }

    return $fallback;
}

function personalization_api_post(string $url, array $payload, int $timeout = 30): array
{
    $payload['token'] = PERSONALIZATION_API_TOKEN;

    $ch = curl_init($url);

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_TIMEOUT        => $timeout,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'Accept: application/json',
        ],
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 3,
    ]);

    $body      = curl_exec($ch);
    $httpCode  = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);

    curl_close($ch);

    if ($body === false || $curlError) {
        return ['success' => false, 'error' => $curlError ?: 'Empty response'];
    }

    $decoded = json_decode($body, true);

    if (!is_array($decoded)) {
        return ['success' => false, 'error' => 'Invalid JSON response from API'];
    }

    if ($httpCode >= 400) {
        return ['success' => false, 'error' => extract_api_message($decoded, "HTTP {$httpCode}")];
    }

    if (isset($decoded['success']) && $decoded['success'] === false) {
        return ['success' => false, 'error' => extract_api_message($decoded, 'API request failed')];
    }

    return ['success' => true, 'data' => $decoded];
}

function build_submit_payload(array $requestRow, array $storedPayload = []): array
{
    $payload = $storedPayload;

    $trackingId = trim((string)($requestRow['tracking_id'] ?? ''));
    if ($trackingId !== '') {
        $payload['tracking_id'] = $trackingId;
    }

    if (empty($payload['type']) || !is_string($payload['type'])) {
        $payload['type'] = PERSONALIZATION_DEFAULT_TYPE;
    }

    return $payload;
}

function extract_remote_personalization_data(array $apiData): array
{
    return is_array($apiData['data'] ?? null) ? $apiData['data'] : [];
}

function extract_remote_personalization_status(array $apiData): string
{
    $status = (string)($apiData['status'] ?? '');
    if ($status === '' && isset($apiData['data']['status']) && is_string($apiData['data']['status'])) {
        $status = $apiData['data']['status'];
    }
    return normalize_status($status);
}

/* -----------------------------
   DB Connect
----------------------------- */
$conn = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
if ($conn->connect_error) {
    http_response_code(500);
    exit('Database connection failed.');
}
$conn->set_charset('utf8mb4');

/* -----------------------------
   Ensure tables exist
----------------------------- */
$conn->query("
CREATE TABLE IF NOT EXISTS personalization (
    id INT AUTO_INCREMENT PRIMARY KEY,
    `user` VARCHAR(255) NOT NULL,
    tracking_id VARCHAR(120) DEFAULT NULL,
    price DECIMAL(12,2) NOT NULL DEFAULT 0,
    status VARCHAR(50) NOT NULL DEFAULT 'pending',
    admin_reply TEXT DEFAULT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
");

$conn->query("
CREATE TABLE IF NOT EXISTS personalize_files (
    id INT AUTO_INCREMENT PRIMARY KEY,
    personalization_id INT NOT NULL,
    filename VARCHAR(255) NOT NULL,
    original_name VARCHAR(255) DEFAULT NULL,
    uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (personalization_id) REFERENCES personalization(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
");

$conn->query("
CREATE TABLE IF NOT EXISTS personalize_price (
    id INT AUTO_INCREMENT PRIMARY KEY,
    price DECIMAL(12,2) NOT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
");

/* relax old enum restriction if it existed */
$conn->query("ALTER TABLE personalization MODIFY COLUMN status VARCHAR(50) NOT NULL DEFAULT 'pending'");

/* extra columns for admin status checking */
if (!column_exists($conn, 'personalization', 'admin_updated_at')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN admin_updated_at TIMESTAMP NULL DEFAULT NULL AFTER admin_reply");
}
if (!column_exists($conn, 'personalization', 'remote_download_url')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN remote_download_url TEXT NULL AFTER admin_updated_at");
}
if (!column_exists($conn, 'personalization', 'last_status_response')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN last_status_response LONGTEXT NULL AFTER remote_download_url");
}
if (!column_exists($conn, 'personalization', 'status_checked_at')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN status_checked_at TIMESTAMP NULL DEFAULT NULL AFTER last_status_response");
}
if (!column_exists($conn, 'personalization', 'submitted_payload')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN submitted_payload LONGTEXT NULL AFTER status_checked_at");
}
if (!column_exists($conn, 'personalization', 'last_submit_response')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN last_submit_response LONGTEXT NULL AFTER submitted_payload");
}
if (!column_exists($conn, 'personalization', 'retry_count')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN retry_count INT NOT NULL DEFAULT 0 AFTER last_submit_response");
}
if (!column_exists($conn, 'personalization', 'last_retry_at')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN last_retry_at TIMESTAMP NULL DEFAULT NULL AFTER retry_count");
}
if (!column_exists($conn, 'personalization', 'remote_personalization_id')) {
    $conn->query("ALTER TABLE personalization ADD COLUMN remote_personalization_id BIGINT NULL DEFAULT NULL AFTER tracking_id");
}
if (!column_exists($conn, 'personalize_files', 'original_name')) {
    $conn->query("ALTER TABLE personalize_files ADD COLUMN original_name VARCHAR(255) NULL AFTER filename");
}

/* ensure one default price row */
$res = $conn->query("SELECT COUNT(*) AS c FROM personalize_price");
if ($res) {
    $row = $res->fetch_assoc();
    if ((int)($row['c'] ?? 0) === 0) {
        $defaultPrice = 250.00;
        $stmt = $conn->prepare("INSERT INTO personalize_price (price) VALUES (?)");
        $stmt->bind_param("d", $defaultPrice);
        $stmt->execute();
        $stmt->close();
    }
    $res->free();
}

/* normalize old status values */
$conn->query("UPDATE personalization SET status = 'processing' WHERE LOWER(status) IN ('in_progress', 'in progress', 'in-progress', 'ipe', 'processing')");
$conn->query("UPDATE personalization SET status = 'completed' WHERE LOWER(status) IN ('successful', 'success', 'completed', 'complete', 'done')");
$conn->query("UPDATE personalization SET status = 'failed' WHERE LOWER(status) IN ('failed', 'error', 'declined', 'rejected')");
$conn->query("UPDATE personalization SET status = 'pending' WHERE LOWER(status) IN ('submitted', 'pending', '')");

/* -----------------------------
   Download latest uploaded local file
----------------------------- */
if (isset($_GET['download']) && ctype_digit((string)$_GET['download'])) {
    $id = (int)$_GET['download'];

    $stmt = $conn->prepare("
        SELECT filename, original_name
        FROM personalize_files
        WHERE personalization_id = ?
        ORDER BY id DESC
        LIMIT 1
    ");
    $stmt->bind_param("i", $id);
    $stmt->execute();
    $result = $stmt->get_result();
    $file = $result->fetch_assoc();
    $stmt->close();

    if (!$file) {
        http_response_code(404);
        exit('No file found for this request.');
    }

    $path = __DIR__ . '/uploads/personalize/' . $file['filename'];

    if (!is_file($path)) {
        http_response_code(404);
        exit('File missing on server.');
    }

    $downloadName = $file['original_name'] ?: basename($file['filename']);

    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="' . basename($downloadName) . '"');
    header('Content-Length: ' . filesize($path));
    readfile($path);
    exit();
}

/* -----------------------------
   AJAX POST Actions
----------------------------- */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    verify_csrf_or_fail();

    $action = trim((string)$_POST['action']);
    $validStatuses = ['pending', 'processing', 'completed', 'failed', 'invalid'];

    switch ($action) {
        case 'update_price':
            $price = isset($_POST['price']) ? (float)$_POST['price'] : 0;

            if ($price <= 0) {
                json_response(false, 'Invalid price value.', [], 422);
            }

            $stmt = $conn->prepare("INSERT INTO personalize_price (price) VALUES (?)");
            $stmt->bind_param("d", $price);

            if (!$stmt->execute()) {
                $stmt->close();
                json_response(false, 'Failed to update price.', [], 500);
            }

            $stmt->close();

            json_response(true, 'Price updated successfully.', [
                'price'         => number_format($price, 2, '.', ''),
                'price_display' => '₦' . number_format($price, 2),
            ]);
            break;

        case 'update_request':
            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
            $status = normalize_status((string)($_POST['status'] ?? 'pending'));
            $adminReply = trim((string)($_POST['admin_reply'] ?? ''));

            if ($id <= 0) {
                json_response(false, 'Invalid request ID.', [], 422);
            }

            if (!in_array($status, $validStatuses, true)) {
                json_response(false, 'Invalid status value.', [], 422);
            }

            $checkStmt = $conn->prepare("SELECT id FROM personalization WHERE id = ? LIMIT 1");
            $checkStmt->bind_param("i", $id);
            $checkStmt->execute();
            $exists = $checkStmt->get_result()->fetch_assoc();
            $checkStmt->close();

            if (!$exists) {
                json_response(false, 'Request not found.', [], 404);
            }

            $stmt = $conn->prepare("
                UPDATE personalization
                SET status = ?, admin_reply = ?, admin_updated_at = NOW()
                WHERE id = ?
                LIMIT 1
            ");
            $stmt->bind_param("ssi", $status, $adminReply, $id);

            if (!$stmt->execute()) {
                $stmt->close();
                json_response(false, 'Failed to update request.', [], 500);
            }

            $stmt->close();

            json_response(true, "Request #{$id} updated successfully.", [
                'id'           => $id,
                'status'       => $status,
                'status_label' => status_label($status),
                'admin_reply'  => $adminReply,
            ]);
            break;

        case 'check_status':
            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;

            if ($id <= 0) {
                json_response(false, 'Invalid request ID.', [], 422);
            }

            $stmt = $conn->prepare("
                SELECT p.*, pf.filename AS latest_file, pf.original_name AS latest_original_name
                FROM personalization p
                LEFT JOIN (
                    SELECT f1.personalization_id, f1.filename, f1.original_name
                    FROM personalize_files f1
                    INNER JOIN (
                        SELECT personalization_id, MAX(id) AS max_id
                        FROM personalize_files
                        GROUP BY personalization_id
                    ) f2
                        ON f1.personalization_id = f2.personalization_id
                       AND f1.id = f2.max_id
                ) pf ON pf.personalization_id = p.id
                WHERE p.id = ?
                LIMIT 1
            ");
            $stmt->bind_param("i", $id);
            $stmt->execute();
            $result = $stmt->get_result();
            $requestRow = $result->fetch_assoc();
            $stmt->close();

            if (!$requestRow) {
                json_response(false, 'Request not found.', [], 404);
            }

            $trackingId = trim((string)($requestRow['tracking_id'] ?? ''));
            $remoteId = (int)($requestRow['remote_personalization_id'] ?? 0);

            if ($trackingId === '' && $remoteId <= 0) {
                json_response(false, 'No tracking ID or remote personalization ID is available for this request.', [], 422);
            }

            $statusPayload = [];
            if ($trackingId !== '') {
                $statusPayload['tracking_id'] = $trackingId;
            } else {
                $statusPayload['id'] = $remoteId;
            }

            $apiResult = personalization_api_post(
                PERSONALIZATION_STATUS_URL,
                $statusPayload,
                30
            );

            if (!$apiResult['success']) {
                json_response(false, 'Status check failed: ' . $apiResult['error'], [], 502);
            }

            $apiData = $apiResult['data'];
            $remoteData = extract_remote_personalization_data($apiData);
            $newStatus = extract_remote_personalization_status($apiData);

            $responseTrackingId = trim((string)($apiData['tracking_id'] ?? $remoteData['tracking_id'] ?? $trackingId));
            $responseRemoteId = (int)($apiData['id'] ?? $remoteData['id'] ?? $remoteId);

            $existingReply = trim((string)($requestRow['admin_reply'] ?? ''));
            $messageFromApi = trim((string)($apiData['message'] ?? ''));
            $downloadUrl = trim((string)($remoteData['download_url'] ?? ''));

            if (!empty($remoteData)) {
                $lines = [];
                $map = [
                    'firstName'   => 'First Name',
                    'middleName'  => 'Middle Name',
                    'lastName'    => 'Last Name',
                    'dateOfBirth' => 'Date of Birth',
                    'gender'      => 'Gender',
                    'idNumber'    => 'ID Number',
                    'tracking_id' => 'Tracking ID',
                ];

                foreach ($map as $key => $label) {
                    $value = trim((string)($remoteData[$key] ?? ''));
                    if ($value !== '') {
                        $lines[] = $label . ': ' . $value;
                    }
                }

                if (!empty($lines)) {
                    $newReply = implode("\n", $lines);
                    if ($messageFromApi !== '') {
                        $newReply .= "\n\nAPI Message: " . $messageFromApi;
                    }
                } else {
                    $newReply = $messageFromApi !== '' ? $messageFromApi : $existingReply;
                }
            } elseif (in_array($newStatus, ['failed', 'invalid'], true)) {
                $newReply = $messageFromApi !== '' ? $messageFromApi : $existingReply;
            } elseif ($messageFromApi !== '') {
                $newReply = $messageFromApi;
            } else {
                $newReply = $existingReply;
            }

            $rawResponse = json_encode($apiData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            $checkedAt = date('Y-m-d H:i:s');

            $updateStmt = $conn->prepare("
                UPDATE personalization
                SET status = ?,
                    tracking_id = ?,
                    remote_personalization_id = ?,
                    admin_reply = ?,
                    remote_download_url = ?,
                    last_status_response = ?,
                    status_checked_at = NOW(),
                    admin_updated_at = NOW()
                WHERE id = ?
                LIMIT 1
            ");
            $updateStmt->bind_param("ssisssi", $newStatus, $responseTrackingId, $responseRemoteId, $newReply, $downloadUrl, $rawResponse, $id);

            if (!$updateStmt->execute()) {
                $updateStmt->close();
                json_response(false, 'Failed to save checked status.', [], 500);
            }
            $updateStmt->close();

            json_response(true, 'Status checked successfully.', [
                'id'                  => $id,
                'remote_id'           => $responseRemoteId,
                'tracking_id'         => $responseTrackingId,
                'status'              => $newStatus,
                'status_label'        => status_label($newStatus),
                'admin_reply'         => $newReply,
                'remote_download_url' => $downloadUrl,
                'status_checked_at'   => $checkedAt,
                'local_download_url'  => !empty($requestRow['latest_file']) ? ($_SERVER['PHP_SELF'] . '?download=' . $id) : '',
                'local_file_name'     => (string)($requestRow['latest_original_name'] ?: $requestRow['latest_file'] ?: ''),
                'photo'               => (string)($remoteData['photo'] ?? ''),
                'identity_data'       => $remoteData,
            ]);
            break;

        case 'retry_submit':
            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;

            if ($id <= 0) {
                json_response(false, 'Invalid request ID.', [], 422);
            }

            $stmt = $conn->prepare("
                SELECT p.*, pf.filename AS latest_file, pf.original_name AS latest_original_name
                FROM personalization p
                LEFT JOIN (
                    SELECT f1.personalization_id, f1.filename, f1.original_name
                    FROM personalize_files f1
                    INNER JOIN (
                        SELECT personalization_id, MAX(id) AS max_id
                        FROM personalize_files
                        GROUP BY personalization_id
                    ) f2
                        ON f1.personalization_id = f2.personalization_id
                       AND f1.id = f2.max_id
                ) pf ON pf.personalization_id = p.id
                WHERE p.id = ?
                LIMIT 1
            ");
            $stmt->bind_param("i", $id);
            $stmt->execute();
            $result = $stmt->get_result();
            $requestRow = $result->fetch_assoc();
            $stmt->close();

            if (!$requestRow) {
                json_response(false, 'Request not found.', [], 404);
            }

            $currentStatus = normalize_status((string)($requestRow['status'] ?? 'pending'));
            if ($currentStatus !== 'pending') {
                json_response(false, 'Retry is only allowed while status is pending.', [], 422);
            }

            $currentRetryCount = (int)($requestRow['retry_count'] ?? 0);
            if ($currentRetryCount >= RETRY_MAX_ATTEMPTS) {
                json_response(false, 'Retry limit reached for this request (' . RETRY_MAX_ATTEMPTS . ' attempts).', [], 422);
            }

            $storedPayloadJson = trim((string)($requestRow['submitted_payload'] ?? ''));
            $storedPayload = [];
            if ($storedPayloadJson !== '') {
                $decodedStored = json_decode($storedPayloadJson, true);
                if (is_array($decodedStored)) {
                    $storedPayload = $decodedStored;
                }
            }

            $retryPayload = build_submit_payload($requestRow, $storedPayload);
            $retryPayload['source'] = 'admin_panel_retry';
            $retryPayload['retry_attempt'] = $currentRetryCount + 1;
            $retryPayload['admin_user'] = (string)($_SESSION['admin'] ?? '');
            $retryPayload['original_user'] = (string)($requestRow['user'] ?? '');
            $retryPayload['original_price'] = (float)($requestRow['price'] ?? 0);

            $retryPayloadJson = json_encode($retryPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);

            $apiResult = personalization_api_post(
                PERSONALIZATION_SUBMIT_URL,
                $retryPayload,
                30
            );

            if (!$apiResult['success']) {
                $errorText = $apiResult['error'] ?: 'Unknown API error';
                $internalNote = 'Retry #' . ($currentRetryCount + 1) . ' failed: ' . $errorText;
                $existingReply = trim((string)($requestRow['admin_reply'] ?? ''));
                $combinedReply = $existingReply !== ''
                    ? $existingReply . "\n\n" . $internalNote
                    : $internalNote;

                $errUpdateStmt = $conn->prepare("
                    UPDATE personalization
                    SET admin_reply = ?,
                        last_submit_response = ?,
                        retry_count = retry_count + 1,
                        last_retry_at = NOW(),
                        admin_updated_at = NOW()
                    WHERE id = ?
                    LIMIT 1
                ");
                $errResponse = json_encode(['error' => $errorText], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                $errUpdateStmt->bind_param("ssi", $combinedReply, $errResponse, $id);
                $errUpdateStmt->execute();
                $errUpdateStmt->close();

                json_response(false, 'Retry failed: ' . $errorText, [
                    'id'           => $id,
                    'retry_count'  => $currentRetryCount + 1,
                    'admin_reply'  => $combinedReply,
                ], 502);
            }

            $apiData = $apiResult['data'];
            $remoteData = extract_remote_personalization_data($apiData);

            $newTrackingId = trim((string)($apiData['tracking_id'] ?? $remoteData['tracking_id'] ?? $requestRow['tracking_id'] ?? ''));
            $remoteRequestId = (int)($apiData['id'] ?? $remoteData['id'] ?? $requestRow['remote_personalization_id'] ?? 0);
            $newStatus = extract_remote_personalization_status($apiData);
            $downloadUrl = trim((string)($remoteData['download_url'] ?? ''));
            $apiMessage = extract_api_message($apiData, 'Retry submitted.');

            $existingReply = trim((string)($requestRow['admin_reply'] ?? ''));
            $retryNote = 'Retry #' . ($currentRetryCount + 1) . ' (' . date('Y-m-d H:i:s') . '): ' . $apiMessage;

            if ($apiMessage !== '' && $apiMessage !== 'Retry submitted.') {
                $newReply = $existingReply !== '' ? ($existingReply . "\n\n" . $retryNote) : $retryNote;
            } else {
                $newReply = $existingReply !== '' ? $existingReply : $retryNote;
            }

            $rawResponse = json_encode($apiData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

            $updateStmt = $conn->prepare("
                UPDATE personalization
                SET status = ?,
                    tracking_id = ?,
                    remote_personalization_id = ?,
                    admin_reply = ?,
                    remote_download_url = ?,
                    submitted_payload = ?,
                    last_submit_response = ?,
                    retry_count = retry_count + 1,
                    last_retry_at = NOW(),
                    admin_updated_at = NOW()
                WHERE id = ?
                LIMIT 1
            ");
            $updateStmt->bind_param(
                "ssissssi",
                $newStatus,
                $newTrackingId,
                $remoteRequestId,
                $newReply,
                $downloadUrl,
                $retryPayloadJson,
                $rawResponse,
                $id
            );

            if (!$updateStmt->execute()) {
                $updateStmt->close();
                json_response(false, 'Retry succeeded upstream but failed to save state.', [], 500);
            }
            $updateStmt->close();

            $newRetryCount = $currentRetryCount + 1;

            json_response(true, 'Retry #' . $newRetryCount . ' submitted. ' . $apiMessage, [
                'id'                  => $id,
                'remote_id'           => $remoteRequestId,
                'tracking_id'         => $newTrackingId,
                'status'              => $newStatus,
                'status_label'        => status_label($newStatus),
                'admin_reply'         => $newReply,
                'remote_download_url' => $downloadUrl,
                'retry_count'         => $newRetryCount,
                'retry_limit'         => RETRY_MAX_ATTEMPTS,
                'retry_remaining'     => max(0, RETRY_MAX_ATTEMPTS - $newRetryCount),
                'last_retry_at'       => date('Y-m-d H:i:s'),
                'local_download_url'  => !empty($requestRow['latest_file']) ? ($_SERVER['PHP_SELF'] . '?download=' . $id) : '',
                'local_file_name'     => (string)($requestRow['latest_original_name'] ?: $requestRow['latest_file'] ?: ''),
                'can_retry'           => ($newStatus === 'pending') && ($newRetryCount < RETRY_MAX_ATTEMPTS),
            ]);
            break;

        case 'upload_pdf':
            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;

            if ($id <= 0) {
                json_response(false, 'Invalid request ID.', [], 422);
            }

            $checkStmt = $conn->prepare("SELECT id FROM personalization WHERE id = ? LIMIT 1");
            $checkStmt->bind_param("i", $id);
            $checkStmt->execute();
            $exists = $checkStmt->get_result()->fetch_assoc();
            $checkStmt->close();

            if (!$exists) {
                json_response(false, 'Request not found.', [], 404);
            }

            if (!isset($_FILES['pdf_file'])) {
                json_response(false, 'No file uploaded.', [], 422);
            }

            $file = $_FILES['pdf_file'];

            if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
                json_response(false, 'Upload failed.', [], 422);
            }

            if (($file['size'] ?? 0) > 10 * 1024 * 1024) {
                json_response(false, 'PDF must be 10MB or less.', [], 422);
            }

            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mime = finfo_file($finfo, $file['tmp_name']);
            finfo_close($finfo);

            $ext = strtolower(pathinfo((string)$file['name'], PATHINFO_EXTENSION));

            if ($mime !== 'application/pdf' || $ext !== 'pdf') {
                json_response(false, 'Only valid PDF files are allowed.', [], 422);
            }

            $uploadDir = __DIR__ . '/uploads/personalize/';
            if (!is_dir($uploadDir) && !mkdir($uploadDir, 0755, true)) {
                json_response(false, 'Failed to create upload directory.', [], 500);
            }

            try {
                $safeName = sprintf(
                    'personalize_%d_%s.pdf',
                    $id,
                    bin2hex(random_bytes(12))
                );
            } catch (Throwable $e) {
                json_response(false, 'Failed to generate file name.', [], 500);
            }

            $destination = $uploadDir . $safeName;

            if (!move_uploaded_file($file['tmp_name'], $destination)) {
                json_response(false, 'Failed to save uploaded file.', [], 500);
            }

            $originalName = trim((string)$file['name']);

            $stmt = $conn->prepare("
                INSERT INTO personalize_files (personalization_id, filename, original_name)
                VALUES (?, ?, ?)
            ");
            $stmt->bind_param("iss", $id, $safeName, $originalName);

            if (!$stmt->execute()) {
                @unlink($destination);
                $stmt->close();
                json_response(false, 'Failed to record uploaded file.', [], 500);
            }

            $stmt->close();

            $updateStmt = $conn->prepare("
                UPDATE personalization
                SET status = 'completed', admin_updated_at = NOW()
                WHERE id = ?
                LIMIT 1
            ");
            $updateStmt->bind_param("i", $id);
            $updateStmt->execute();
            $updateStmt->close();

            json_response(true, "PDF uploaded successfully for request #{$id}.", [
                'id'            => $id,
                'filename'      => $safeName,
                'original_name' => $originalName,
                'download_url'  => $_SERVER['PHP_SELF'] . '?download=' . $id,
                'status'        => 'completed',
                'status_label'  => status_label('completed'),
            ]);
            break;

        default:
            json_response(false, 'Invalid action.', [], 400);
    }
}

/* -----------------------------
   Page Data
----------------------------- */
$csrfToken = ensure_csrf_token();

$currentPrice = 0.00;
$stmt = $conn->prepare("SELECT price FROM personalize_price ORDER BY id DESC LIMIT 1");
$stmt->execute();
$result = $stmt->get_result();
if ($row = $result->fetch_assoc()) {
    $currentPrice = (float)$row['price'];
}
$stmt->close();

/* stats */
$stats = [
    'total'      => 0,
    'pending'    => 0,
    'processing' => 0,
    'completed'  => 0,
    'failed'     => 0,
    'invalid'    => 0,
];

$statsQuery = $conn->query("
    SELECT
        COUNT(*) AS total,
        SUM(CASE WHEN LOWER(status) = 'pending' THEN 1 ELSE 0 END) AS pending_count,
        SUM(CASE WHEN LOWER(status) = 'processing' THEN 1 ELSE 0 END) AS processing_count,
        SUM(CASE WHEN LOWER(status) = 'completed' THEN 1 ELSE 0 END) AS completed_count,
        SUM(CASE WHEN LOWER(status) = 'failed' THEN 1 ELSE 0 END) AS failed_count,
        SUM(CASE WHEN LOWER(status) = 'invalid' THEN 1 ELSE 0 END) AS invalid_count
    FROM personalization
");

if ($statsQuery) {
    $row = $statsQuery->fetch_assoc();
    $stats['total']      = (int)($row['total'] ?? 0);
    $stats['pending']    = (int)($row['pending_count'] ?? 0);
    $stats['processing'] = (int)($row['processing_count'] ?? 0);
    $stats['completed']  = (int)($row['completed_count'] ?? 0);
    $stats['failed']     = (int)($row['failed_count'] ?? 0);
    $stats['invalid']    = (int)($row['invalid_count'] ?? 0);
    $statsQuery->free();
}

/* search */
$search = trim((string)($_GET['search'] ?? ''));
$requests = [];

$listColumns = "
        p.id,
        p.user,
        p.tracking_id,
        p.remote_personalization_id,
        p.price,
        p.status,
        p.admin_reply,
        p.admin_updated_at,
        p.remote_download_url,
        p.last_status_response,
        p.status_checked_at,
        p.submitted_payload,
        p.last_submit_response,
        p.retry_count,
        p.last_retry_at,
        p.created_at,
        pf.filename AS latest_file,
        pf.original_name AS latest_original_name
";

if ($search !== '') {
    $like = '%' . $search . '%';

    $stmt = $conn->prepare("
        SELECT $listColumns
        FROM personalization p
        LEFT JOIN (
            SELECT f1.personalization_id, f1.filename, f1.original_name
            FROM personalize_files f1
            INNER JOIN (
                SELECT personalization_id, MAX(id) AS max_id
                FROM personalize_files
                GROUP BY personalization_id
            ) f2
                ON f1.personalization_id = f2.personalization_id
               AND f1.id = f2.max_id
        ) pf ON pf.personalization_id = p.id
        WHERE p.user LIKE ?
           OR p.tracking_id LIKE ?
           OR p.admin_reply LIKE ?
           OR p.status LIKE ?
        ORDER BY p.id DESC
        LIMIT 300
    ");

    $stmt->bind_param("ssss", $like, $like, $like, $like);
    $stmt->execute();
    $result = $stmt->get_result();

    while ($row = $result->fetch_assoc()) {
        $requests[] = $row;
    }
    $stmt->close();
} else {
    $sql = "
        SELECT $listColumns
        FROM personalization p
        LEFT JOIN (
            SELECT f1.personalization_id, f1.filename, f1.original_name
            FROM personalize_files f1
            INNER JOIN (
                SELECT personalization_id, MAX(id) AS max_id
                FROM personalize_files
                GROUP BY personalization_id
            ) f2
                ON f1.personalization_id = f2.personalization_id
               AND f1.id = f2.max_id
        ) pf ON pf.personalization_id = p.id
        ORDER BY p.id DESC
        LIMIT 300
    ";

    $q = $conn->query($sql);
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $requests[] = $row;
        }
        $q->free();
    }
}

$conn->close();
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Admin — Personalization Manager</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root{
    --bg1:#071633;
    --bg2:#0d47a1;
    --card:rgba(255,255,255,0.06);
    --card-border:rgba(255,255,255,0.08);
    --accent:#38bdf8;
    --accent2:#0ea5e9;
    --text:#eaf2ff;
    --muted:rgba(234,242,255,0.72);
    --success:#16a34a;
    --danger:#ef4444;
    --warning:#f59e0b;
    --info:#3b82f6;
    --pending:#94a3b8;
    --invalid:#b91c1c;
    --shadow:0 10px 30px rgba(0,0,0,.18);
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
    font-family:Inter,Arial,sans-serif;
    background:
        radial-gradient(circle at top left, rgba(56,189,248,.18), transparent 22%),
        radial-gradient(circle at bottom right, rgba(34,197,94,.14), transparent 22%),
        linear-gradient(135deg,var(--bg1),var(--bg2));
    color:var(--text);
    min-height:100vh;
    padding:16px;
}
.container{ max-width:1480px; margin:0 auto; }
.header{
    display:flex; justify-content:space-between; align-items:flex-start;
    gap:16px; margin-bottom:16px; flex-wrap:wrap;
}
.card{
    background:var(--card); border:1px solid var(--card-border);
    border-radius:18px; padding:18px; backdrop-filter:blur(10px);
    box-shadow:var(--shadow); margin-bottom:16px;
}
.h1{ font-size:26px; font-weight:800; color:var(--accent); margin-bottom:6px; }
.small{ font-size:13px; color:var(--muted); }
.form-row{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.stats-grid{
    display:grid; grid-template-columns:repeat(6,minmax(0,1fr));
    gap:14px; margin-bottom:16px;
}
.stat-card{ text-align:center; padding:16px; }
.stat-title{
    font-size:12px; text-transform:uppercase; letter-spacing:.08em;
    color:var(--muted); margin-bottom:8px; font-weight:800;
}
.stat-value{ font-size:24px; font-weight:800; }
.input, textarea, select{
    width:100%; background:rgba(0,0,0,.18);
    border:1px solid rgba(255,255,255,.1); color:var(--text);
    padding:10px 12px; border-radius:12px; outline:none;
}
.input:focus, textarea:focus, select:focus{
    border-color:rgba(56,189,248,.7); box-shadow:0 0 0 3px rgba(56,189,248,.15);
}
textarea{ min-height:84px; resize:vertical; }
.button{
    border:0; border-radius:12px; padding:10px 14px; cursor:pointer;
    font-weight:800; background:linear-gradient(90deg,var(--accent2),var(--accent));
    color:#06253d; transition:.2s ease; text-decoration:none;
    display:inline-flex; align-items:center; justify-content:center; gap:8px;
}
.button:hover{transform:translateY(-1px)}
.button:disabled{ opacity:.7; cursor:not-allowed; transform:none; }
.button.secondary{ background:rgba(255,255,255,.12); color:var(--text); }
.button.info{ background:linear-gradient(90deg,#60a5fa,#38bdf8); color:#04192c; }
.button.warning{ background:linear-gradient(90deg,#fbbf24,#f59e0b); color:#2b1800; }
.button.search-btn{ min-width:120px; }
.table-wrap{ overflow:auto; }
.table{ width:100%; border-collapse:collapse; min-width:1550px; }
.table th{
    text-align:left; padding:12px; background:rgba(255,255,255,.05);
    position:sticky; top:0; z-index:1;
    font-size:12px; text-transform:uppercase; letter-spacing:.05em;
}
.table td{
    padding:12px; border-top:1px solid rgba(255,255,255,.06); vertical-align:top;
}
.status-badge{
    display:inline-block; padding:7px 10px; border-radius:999px;
    font-size:12px; font-weight:800; letter-spacing:.3px;
}
.bad-pending{background:var(--pending);color:#04111f}
.bad-processing{background:var(--warning);color:#2b1800}
.bad-completed{background:#22c55e;color:#04140c}
.bad-failed{background:var(--danger);color:#fff}
.bad-invalid{background:var(--invalid);color:#fff}
.reply-box{
    white-space:pre-wrap; background:rgba(0,0,0,.16);
    border-radius:12px; padding:10px; min-height:44px; min-width:220px;
}
.row-actions{
    min-width:300px; display:flex; flex-direction:column; gap:12px;
}
.inline-form{ display:flex; flex-direction:column; gap:8px; }
.file-meta{ font-size:12px; color:var(--muted); margin-top:8px; line-height:1.45; }
.toast-wrap{
    position:fixed; top:16px; right:16px; z-index:99999;
    display:flex; flex-direction:column; gap:10px;
}
.toast{
    min-width:280px; max-width:420px; padding:12px 14px;
    border-radius:12px; background:rgba(6,17,36,.96); color:#fff;
    border:1px solid rgba(255,255,255,.08);
    box-shadow:0 10px 30px rgba(0,0,0,.25);
}
.toast.success{border-left:4px solid var(--success)}
.toast.error{border-left:4px solid var(--danger)}
.toast.info{border-left:4px solid var(--info)}
.loader{
    display:inline-block; width:14px; height:14px;
    border:2px solid rgba(255,255,255,.35); border-top-color:#fff;
    border-radius:50%; animation:spin .7s linear infinite;
}
.progress{
    height:8px; border-radius:999px; background:rgba(255,255,255,.08);
    overflow:hidden; display:none;
}
.progress-bar{
    height:100%; width:0%;
    background:linear-gradient(90deg,#38bdf8,#22c55e);
    transition:width .2s ease;
}
.empty{ color:var(--muted); padding:16px 0; }
.search-wrap{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
.search-input{ min-width:300px; }
.file-actions{ display:flex; flex-wrap:wrap; gap:8px; }
.retry-meta{
    display:flex; align-items:center; justify-content:space-between;
    gap:8px; background:rgba(251,191,36,.12);
    border:1px solid rgba(251,191,36,.35); border-radius:10px;
    padding:8px 10px; font-size:12px; color:#fde68a;
}
.retry-meta strong{ color:#fbbf24; font-weight:800; }
.retry-meta .retry-limit{ color:#fef3c7; font-size:11px; opacity:.85; }
.retry-row{ display:flex; flex-direction:column; gap:6px; }
.retry-mini{ font-size:11px; color:#fbbf24; text-align:center; font-weight:700; }
@keyframes spin{to{transform:rotate(360deg)}}
@media (max-width: 1200px){ .stats-grid{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (max-width: 900px){
    .header{flex-direction:column}
    .stats-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
    .search-input{min-width:100%}
}
@media (max-width: 640px){ .stats-grid{grid-template-columns:1fr} }
</style>
</head>
<body>

<div id="toastWrap" class="toast-wrap"></div>

<div class="container">

    <div class="header">
        <div>
            <div class="h1">Admin — Personalization Manager</div>
            <div class="small">Signed in as: <strong><?= e((string)$_SESSION['admin']) ?></strong></div>
        </div>

        <div class="card" style="min-width:340px">
            <form id="priceForm" class="form-row">
                <input type="hidden" name="csrf_token" value="<?= e($csrfToken) ?>">
                <input type="hidden" name="action" value="update_price">

                <label class="small" for="price">Global Price: ₦</label>
                <input
                    id="price"
                    name="price"
                    type="number"
                    step="0.01"
                    min="0.01"
                    class="input"
                    style="width:150px"
                    value="<?= number_format($currentPrice, 2, '.', '') ?>"
                    required
                >
                <button type="submit" class="button">
                    <span class="btn-text">Update Price</span>
                </button>
            </form>
        </div>
    </div>

    <div class="stats-grid">
        <div class="card stat-card"><div class="stat-title">Total</div><div class="stat-value"><?= $stats['total'] ?></div></div>
        <div class="card stat-card"><div class="stat-title">Pending</div><div class="stat-value"><?= $stats['pending'] ?></div></div>
        <div class="card stat-card"><div class="stat-title">Processing</div><div class="stat-value"><?= $stats['processing'] ?></div></div>
        <div class="card stat-card"><div class="stat-title">Completed</div><div class="stat-value"><?= $stats['completed'] ?></div></div>
        <div class="card stat-card"><div class="stat-title">Failed</div><div class="stat-value"><?= $stats['failed'] ?></div></div>
        <div class="card stat-card"><div class="stat-title">Invalid</div><div class="stat-value"><?= $stats['invalid'] ?></div></div>
    </div>

    <div class="card">
        <div class="form-row" style="justify-content:space-between;align-items:flex-start;margin-bottom:14px;">
            <div>
                <h3 style="margin:0 0 6px">Requests</h3>
                <div class="small">Latest 300 personalization requests</div>
            </div>

            <form method="get" class="search-wrap">
                <input
                    type="text"
                    name="search"
                    class="input search-input"
                    placeholder="Search user / tracking id / status / reply"
                    value="<?= e($search) ?>"
                >
                <button type="submit" class="button search-btn">
                    <i class="fa-solid fa-magnifying-glass"></i>
                    Search
                </button>
                <?php if ($search !== ''): ?>
                    <a href="<?= e($_SERVER['PHP_SELF']) ?>" class="button secondary">Reset</a>
                <?php endif; ?>
            </form>
        </div>

        <?php if (empty($requests)): ?>
            <div class="empty">No personalization requests found.</div>
        <?php else: ?>
            <div class="table-wrap">
                <table class="table">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>User</th>
                            <th>Tracking ID</th>
                            <th>Price</th>
                            <th>Status</th>
                            <th>Retries</th>
                            <th>Admin Reply</th>
                            <th>Submitted At</th>
                            <th>Last Checked</th>
                            <th>File</th>
                            <th>Actions</th>
                        </tr>
                    </thead>
                    <tbody>
                    <?php foreach ($requests as $r): ?>
                        <?php
                            $status = normalize_status((string)($r['status'] ?? 'pending'));
                            $latestFile = (string)($r['latest_file'] ?? '');
                            $latestOriginalName = (string)($r['latest_original_name'] ?? '');
                            $remoteDownloadUrl = trim((string)($r['remote_download_url'] ?? ''));
                            $localDownloadUrl = $latestFile !== '' ? ($_SERVER['PHP_SELF'] . '?download=' . (int)$r['id']) : '';
                            $retryCount = (int)($r['retry_count'] ?? 0);
                            $lastRetryAt = (string)($r['last_retry_at'] ?? '');
                            $canRetry = ($status === 'pending') && ($retryCount < RETRY_MAX_ATTEMPTS);
                        ?>
                        <tr
                            id="row-<?= (int)$r['id'] ?>"
                            data-id="<?= (int)$r['id'] ?>"
                            data-local-download-url="<?= e($localDownloadUrl) ?>"
                            data-local-original-name="<?= e($latestOriginalName !== '' ? $latestOriginalName : $latestFile) ?>"
                            data-remote-download-url="<?= e($remoteDownloadUrl) ?>"
                            data-retry-count="<?= $retryCount ?>"
                            data-retry-limit="<?= (int)RETRY_MAX_ATTEMPTS ?>"
                            data-can-retry="<?= $canRetry ? '1' : '0' ?>"
                        >
                            <td class="small"><?= (int)$r['id'] ?></td>
                            <td class="small"><?= e((string)$r['user']) ?></td>
                            <td class="small js-tracking-cell"><?= e((string)$r['tracking_id']) ?></td>
                            <td class="small">₦<?= number_format((float)$r['price'], 2) ?></td>
                            <td>
                                <span class="status-badge <?= e(status_class($status)) ?> js-status-badge">
                                    <?= e(status_label($status)) ?>
                                </span>
                            </td>
                            <td class="small">
                                <?php if ($retryCount > 0): ?>
                                    <div class="retry-meta js-retry-meta">
                                        <span><strong><?= $retryCount ?></strong> / <?= (int)RETRY_MAX_ATTEMPTS ?></span>
                                        <?php if ($lastRetryAt !== ''): ?>
                                            <span class="retry-limit js-last-retry" title="<?= e($lastRetryAt) ?>">
                                                <?= e(date('H:i', strtotime($lastRetryAt))) ?>
                                            </span>
                                        <?php endif; ?>
                                    </div>
                                <?php else: ?>
                                    <span style="color:#94a3b8">—</span>
                                <?php endif; ?>
                            </td>
                            <td style="max-width:260px">
                                <div class="reply-box js-reply-box"><?= e((string)($r['admin_reply'] ?? '')) ?></div>
                            </td>
                            <td class="small"><?= e((string)$r['created_at']) ?></td>
                            <td class="small js-checked-cell"><?= e((string)($r['status_checked_at'] ?? '')) ?></td>
                            <td class="small js-file-cell">
                                <div class="file-actions">
                                    <?php if ($remoteDownloadUrl !== ''): ?>
                                        <a class="button info" href="<?= e($remoteDownloadUrl) ?>" target="_blank" rel="noopener">
                                            <i class="fa-solid fa-cloud-arrow-down"></i> API Download
                                        </a>
                                    <?php endif; ?>
                                    <?php if ($localDownloadUrl !== ''): ?>
                                        <a class="button" href="<?= e($localDownloadUrl) ?>">
                                            <i class="fa-solid fa-download"></i> Local PDF
                                        </a>
                                    <?php endif; ?>
                                </div>

                                <?php if ($localDownloadUrl !== '' || $remoteDownloadUrl !== ''): ?>
                                    <div class="file-meta">
                                        <?php if ($remoteDownloadUrl !== ''): ?>
                                            API slip available<?= $localDownloadUrl !== '' ? '<br>' : '' ?>
                                        <?php endif; ?>
                                        <?php if ($localDownloadUrl !== ''): ?>
                                            Latest local file:<br>
                                            <?= e($latestOriginalName !== '' ? $latestOriginalName : $latestFile) ?>
                                        <?php endif; ?>
                                    </div>
                                <?php else: ?>
                                    <span style="color:#94a3b8">No file</span>
                                <?php endif; ?>
                            </td>
                            <td>
                                <div class="row-actions">
                                    <form class="inline-form js-update-form">
                                        <input type="hidden" name="csrf_token" value="<?= e($csrfToken) ?>">
                                        <input type="hidden" name="action" value="update_request">
                                        <input type="hidden" name="id" value="<?= (int)$r['id'] ?>">

                                        <select name="status" class="input" required>
                                            <option value="pending" <?= $status === 'pending' ? 'selected' : '' ?>>Pending</option>
                                            <option value="processing" <?= $status === 'processing' ? 'selected' : '' ?>>Processing</option>
                                            <option value="completed" <?= $status === 'completed' ? 'selected' : '' ?>>Completed</option>
                                            <option value="failed" <?= $status === 'failed' ? 'selected' : '' ?>>Failed</option>
                                            <option value="invalid" <?= $status === 'invalid' ? 'selected' : '' ?>>Invalid</option>
                                        </select>

                                        <textarea name="admin_reply" class="input" placeholder="Reply..."><?= e((string)($r['admin_reply'] ?? '')) ?></textarea>

                                        <button type="submit" class="button">
                                            <span class="btn-text">Save Status / Reply</span>
                                        </button>
                                    </form>

                                    <?php if ($canRetry): ?>
                                        <form class="inline-form js-retry-form">
                                            <input type="hidden" name="csrf_token" value="<?= e($csrfToken) ?>">
                                            <input type="hidden" name="action" value="retry_submit">
                                            <input type="hidden" name="id" value="<?= (int)$r['id'] ?>">

                                            <div class="retry-row">
                                                <button type="submit" class="button warning">
                                                    <span class="btn-text">
                                                        <i class="fa-solid fa-rotate-right"></i>
                                                        Retry Submission
                                                        <?php if ($retryCount > 0): ?>
                                                            (<?= $retryCount ?>/<?= (int)RETRY_MAX_ATTEMPTS ?>)
                                                        <?php endif; ?>
                                                    </span>
                                                </button>
                                                <div class="retry-mini">Re-submits to personalization provider</div>
                                            </div>
                                        </form>
                                    <?php endif; ?>

                                    <form class="inline-form js-check-form">
                                        <input type="hidden" name="csrf_token" value="<?= e($csrfToken) ?>">
                                        <input type="hidden" name="action" value="check_status">
                                        <input type="hidden" name="id" value="<?= (int)$r['id'] ?>">

                                        <button type="submit" class="button info">
                                            <span class="btn-text">Check Status</span>
                                        </button>
                                    </form>

                                    <form class="inline-form js-upload-form" enctype="multipart/form-data">
                                        <input type="hidden" name="csrf_token" value="<?= e($csrfToken) ?>">
                                        <input type="hidden" name="action" value="upload_pdf">
                                        <input type="hidden" name="id" value="<?= (int)$r['id'] ?>">

                                        <input type="file" name="pdf_file" accept="application/pdf" class="input" required>

                                        <div class="progress js-progress">
                                            <div class="progress-bar js-progress-bar"></div>
                                        </div>

                                        <button type="submit" class="button secondary">
                                            <span class="btn-text">Upload PDF</span>
                                        </button>
                                    </form>
                                </div>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        <?php endif; ?>
    </div>
</div>

<script>
const PAGE_URL = <?= json_encode($_SERVER['PHP_SELF']) ?>;
const RETRY_MAX = <?= (int)RETRY_MAX_ATTEMPTS ?>;

function showToast(type, message) {
    const wrap = document.getElementById('toastWrap');
    const el = document.createElement('div');
    el.className = `toast ${type}`;
    el.textContent = message;
    wrap.appendChild(el);

    setTimeout(() => {
        el.style.opacity = '0';
        el.style.transform = 'translateY(-6px)';
        el.style.transition = 'all .25s ease';
        setTimeout(() => el.remove(), 250);
    }, 3500);
}

function setButtonLoading(button, loading, idleText) {
    if (!button) return;
    const textEl = button.querySelector('.btn-text');

    if (loading) {
        button.disabled = true;
        if (textEl) {
            textEl.dataset.oldText = textEl.innerHTML;
            textEl.innerHTML = '<span class="loader"></span> Working...';
        }
    } else {
        button.disabled = false;
        if (textEl) {
            textEl.innerHTML = textEl.dataset.oldText || idleText;
        }
    }
}

async function postForm(form) {
    const formData = new FormData(form);
    const response = await fetch(PAGE_URL, {
        method: 'POST',
        body: formData,
        headers: { 'X-Requested-With': 'XMLHttpRequest' }
    });

    let data = null;
    try { data = await response.json(); }
    catch (e) { throw new Error('Invalid server response.'); }

    if (!response.ok || !data.success) {
        throw new Error(data?.message || 'Request failed.');
    }
    return data;
}

function uploadWithProgress(form, progressEl, progressBarEl) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        const formData = new FormData(form);

        xhr.open('POST', PAGE_URL, true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

        xhr.upload.addEventListener('progress', (e) => {
            if (e.lengthComputable && progressEl && progressBarEl) {
                const percent = Math.round((e.loaded / e.total) * 100);
                progressEl.style.display = 'block';
                progressBarEl.style.width = percent + '%';
            }
        });

        xhr.onload = function () {
            if (progressBarEl) progressBarEl.style.width = '100%';
            let data = null;
            try { data = JSON.parse(xhr.responseText); }
            catch (e) { reject(new Error('Invalid server response.')); return; }

            if (xhr.status >= 200 && xhr.status < 300 && data.success) {
                resolve(data);
            } else {
                reject(new Error(data?.message || 'Upload failed.'));
            }
        };

        xhr.onerror = function () { reject(new Error('Network error during upload.')); };
        xhr.send(formData);
    });
}

function updateStatusBadge(row, status, label) {
    const badge = row.querySelector('.js-status-badge');
    if (!badge) return;
    const normalized = String(status || '').toLowerCase();
    let cls = 'bad-pending';
    if (normalized === 'processing') cls = 'bad-processing';
    if (normalized === 'completed') cls = 'bad-completed';
    if (normalized === 'failed') cls = 'bad-failed';
    if (normalized === 'invalid') cls = 'bad-invalid';

    badge.className = `status-badge ${cls} js-status-badge`;
    badge.textContent = label;
}

function updateReplyBox(row, reply) {
    const box = row.querySelector('.js-reply-box');
    if (box) box.textContent = reply || '';
}

function updateReplyTextarea(row, reply) {
    const textarea = row.querySelector('.js-update-form textarea[name="admin_reply"]');
    if (textarea) textarea.value = reply || '';
}

function updateCheckedCell(row, checkedAt) {
    const cell = row.querySelector('.js-checked-cell');
    if (cell) cell.textContent = checkedAt || '';
}

function updateTrackingCell(row, trackingId) {
    const cell = row.querySelector('.js-tracking-cell');
    if (cell) cell.textContent = trackingId || '';
}

function updateRetryCell(row, retryCount, lastRetryAt) {
    const tds = row.querySelectorAll('td');
    const retriesTd = tds[5];
    if (!retriesTd) return;

    if (retryCount <= 0) {
        retriesTd.innerHTML = '<span style="color:#94a3b8">—</span>';
        return;
    }

    const time = lastRetryAt ? formatShortTime(lastRetryAt) : '';
    const titleAttr = lastRetryAt ? ` title="${escapeAttr(lastRetryAt)}"` : '';
    retriesTd.innerHTML = `
        <div class="retry-meta js-retry-meta">
            <span><strong>${retryCount}</strong> / ${RETRY_MAX}</span>
            ${time ? `<span class="retry-limit js-last-retry"${titleAttr}>${escapeHtml(time)}</span>` : ''}
        </div>
    `;
}

function formatShortTime(value) {
    if (!value) return '';
    try {
        const d = new Date(String(value).replace(' ', 'T'));
        if (isNaN(d.getTime())) return value;
        const hh = String(d.getHours()).padStart(2, '0');
        const mm = String(d.getMinutes()).padStart(2, '0');
        return `${hh}:${mm}`;
    } catch (e) { return value; }
}

function escapeHtml(text) {
    return String(text ?? '')
        .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;').replace(/'/g, '&#039;');
}
function escapeAttr(text) { return escapeHtml(text); }

function updateFileCell(row) {
    const cell = row.querySelector('.js-file-cell');
    if (!cell) return;
    const remoteUrl = row.dataset.remoteDownloadUrl || '';
    const localUrl = row.dataset.localDownloadUrl || '';
    const localName = row.dataset.localOriginalName || '';

    let html = '<div class="file-actions">';
    if (remoteUrl) {
        html += `<a class="button info" href="${escapeHtml(remoteUrl)}" target="_blank" rel="noopener">
            <i class="fa-solid fa-cloud-arrow-down"></i> API Download</a>`;
    }
    if (localUrl) {
        html += `<a class="button" href="${escapeHtml(localUrl)}">
            <i class="fa-solid fa-download"></i> Local PDF</a>`;
    }
    html += '</div>';

    if (!remoteUrl && !localUrl) {
        cell.innerHTML = '<span style="color:#94a3b8">No file</span>';
        return;
    }
    html += '<div class="file-meta">';
    if (remoteUrl) { html += 'API slip available'; if (localUrl) html += '<br>'; }
    if (localUrl)  { html += 'Latest local file:<br>' + escapeHtml(localName || 'Uploaded PDF'); }
    html += '</div>';
    cell.innerHTML = html;
}

function refreshRetryButton(row, retryCount, canRetry) {
    let form = row.querySelector('.js-retry-form');

    if (!form) {
        if (!canRetry) { row.dataset.canRetry = '0'; return; }
        const refForm = row.querySelector('.js-update-form');
        if (!refForm) return;
        const csrfInput = refForm.querySelector('input[name="csrf_token"]');
        if (!csrfInput) return;
        const rowId = row.dataset.id;
        const wrap = row.querySelector('.row-actions');
        if (!wrap) return;

        const retryHtml = `
            <form class="inline-form js-retry-form">
                <input type="hidden" name="csrf_token" value="${escapeHtml(csrfInput.value)}">
                <input type="hidden" name="action" value="retry_submit">
                <input type="hidden" name="id" value="${escapeHtml(rowId)}">
                <div class="retry-row">
                    <button type="submit" class="button warning">
                        <span class="btn-text">
                            <i class="fa-solid fa-rotate-right"></i>
                            Retry Submission
                            ${retryCount > 0 ? `(${retryCount}/${RETRY_MAX})` : ''}
                        </span>
                    </button>
                    <div class="retry-mini">Re-submits to personalization provider</div>
                </div>
            </form>
        `;
        const checkForm = row.querySelector('.js-check-form');
        if (checkForm) {
            checkForm.insertAdjacentHTML('beforebegin', retryHtml);
        } else {
            wrap.insertAdjacentHTML('afterbegin', retryHtml);
        }
        attachRetryListeners(wrap.querySelector('.js-retry-form'));
        row.dataset.canRetry = '1';
        return;
    }

    if (!canRetry) { form.remove(); row.dataset.canRetry = '0'; return; }

    const textEl = form.querySelector('.btn-text');
    if (textEl) {
        textEl.innerHTML = `
            <i class="fa-solid fa-rotate-right"></i>
            Retry Submission
            ${retryCount > 0 ? `(${retryCount}/${RETRY_MAX})` : ''}
        `;
    }
    row.dataset.canRetry = '1';
}

function attachRetryListeners(form) {
    if (!form) return;
    form.addEventListener('submit', async function (e) {
        e.preventDefault();
        const row = this.closest('tr');
        const button = this.querySelector('button[type="submit"]');

        try {
            setButtonLoading(button, true, 'Retry Submission');
            const data = await postForm(this);

            updateStatusBadge(row, data.data.status, data.data.status_label);
            updateReplyBox(row, data.data.admin_reply);
            updateReplyTextarea(row, data.data.admin_reply);
            updateTrackingCell(row, data.data.tracking_id);
            updateRetryCell(row, data.data.retry_count || 0, data.data.last_retry_at || '');
            updateCheckedCell(row, data.data.last_retry_at || '');

            row.dataset.retryCount = data.data.retry_count || 0;

            row.dataset.remoteDownloadUrl = data.data.remote_download_url || '';
            if (data.data.local_download_url) row.dataset.localDownloadUrl = data.data.local_download_url;
            if (data.data.local_file_name)   row.dataset.localOriginalName = data.data.local_file_name;
            updateFileCell(row);

            const select = row.querySelector('.js-update-form select[name="status"]');
            if (select) select.value = data.data.status;

            refreshRetryButton(row, data.data.retry_count || 0, !!data.data.can_retry);

            showToast('success', data.message || 'Retry submitted successfully.');
        } catch (err) {
            showToast('error', err.message || 'Retry failed.');
            const row = this.closest('tr');
            const select = row ? row.querySelector('.js-update-form select[name="status"]') : null;
            if (select) select.value = 'pending';
        } finally {
            if (button) setButtonLoading(button, false, 'Retry Submission');
        }
    });
}

document.getElementById('priceForm')?.addEventListener('submit', async function (e) {
    e.preventDefault();
    const button = this.querySelector('button[type="submit"]');
    try {
        setButtonLoading(button, true, 'Update Price');
        const data = await postForm(this);
        showToast('success', data.message || 'Price updated.');
    } catch (err) {
        showToast('error', err.message || 'Failed to update price.');
    } finally {
        setButtonLoading(button, false, 'Update Price');
    }
});

document.querySelectorAll('.js-update-form').forEach((form) => {
    form.addEventListener('submit', async function (e) {
        e.preventDefault();
        const row = this.closest('tr');
        const button = this.querySelector('button[type="submit"]');

        try {
            setButtonLoading(button, true, 'Save Status / Reply');
            const data = await postForm(this);

            updateStatusBadge(row, data.data.status, data.data.status_label);
            updateReplyBox(row, data.data.admin_reply);

            refreshRetryButton(
                row,
                parseInt(row.dataset.retryCount || '0', 10),
                data.data.status === 'pending' && row.dataset.canRetry === '1'
            );

            showToast('success', data.message || 'Request updated.');
        } catch (err) {
            showToast('error', err.message || 'Failed to update request.');
        } finally {
            setButtonLoading(button, false, 'Save Status / Reply');
        }
    });
});

document.querySelectorAll('.js-check-form').forEach((form) => {
    form.addEventListener('submit', async function (e) {
        e.preventDefault();
        const row = this.closest('tr');
        const button = this.querySelector('button[type="submit"]');

        try {
            setButtonLoading(button, true, 'Check Status');
            const data = await postForm(this);

            updateStatusBadge(row, data.data.status, data.data.status_label);
            updateReplyBox(row, data.data.admin_reply);
            updateReplyTextarea(row, data.data.admin_reply);
            updateCheckedCell(row, data.data.status_checked_at || '');
            updateTrackingCell(row, data.data.tracking_id || '');

            row.dataset.remoteDownloadUrl = data.data.remote_download_url || '';
            if (data.data.local_download_url) row.dataset.localDownloadUrl = data.data.local_download_url;
            if (data.data.local_file_name)   row.dataset.localOriginalName = data.data.local_file_name;
            updateFileCell(row);

            const select = row.querySelector('.js-update-form select[name="status"]');
            if (select) select.value = data.data.status;

            refreshRetryButton(
                row,
                parseInt(row.dataset.retryCount || '0', 10),
                data.data.status === 'pending' && row.dataset.canRetry === '1'
            );

            showToast('success', data.message || 'Status checked successfully.');
        } catch (err) {
            showToast('error', err.message || 'Failed to check status.');
        } finally {
            setButtonLoading(button, false, 'Check Status');
        }
    });
});

document.querySelectorAll('.js-retry-form').forEach(attachRetryListeners);

document.querySelectorAll('.js-upload-form').forEach((form) => {
    form.addEventListener('submit', async function (e) {
        e.preventDefault();
        const fileInput = this.querySelector('input[type="file"]');
        const button = this.querySelector('button[type="submit"]');
        const row = this.closest('tr');
        const progress = this.querySelector('.js-progress');
        const progressBar = this.querySelector('.js-progress-bar');

        if (!fileInput.files.length) {
            showToast('error', 'Please choose a PDF file first.');
            return;
        }

        try {
            if (progress) progress.style.display = 'block';
            if (progressBar) progressBar.style.width = '0%';

            setButtonLoading(button, true, 'Upload PDF');
            const data = await uploadWithProgress(this, progress, progressBar);

            row.dataset.localDownloadUrl = data.data.download_url || '';
            row.dataset.localOriginalName = data.data.original_name || 'Uploaded PDF';
            updateFileCell(row);
            updateStatusBadge(row, data.data.status, data.data.status_label);

            const select = row.querySelector('select[name="status"]');
            if (select) select.value = data.data.status;

            refreshRetryButton(row, parseInt(row.dataset.retryCount || '0', 10), false);

            this.reset();

            if (progressBar) progressBar.style.width = '100%';

            showToast('success', data.message || 'PDF uploaded successfully.');
        } catch (err) {
            showToast('error', err.message || 'Upload failed.');
        } finally {
            setButtonLoading(button, false, 'Upload PDF');
            setTimeout(() => {
                if (progress) progress.style.display = 'none';
                if (progressBar) progressBar.style.width = '0%';
            }, 800);
        }
    });
});
</script>
</body>
</html>
