<?php
/**
 * retry_etims.php
 *
 * Retries failed or stuck-pending eTIMS invoice submissions.
 *
 * Usage:
 *   A) Web / manual  — POST invoice_id to retry a single invoice immediately
 *   B) Cron / bulk   — no invoice_id, processes all retryable records
 *
 * Cron example (every 15 minutes):
 *   *\/15 * * * * php /var/www/html/api/retry_etims.php >> /var/log/etims_retry.log 2>&1
 *
 * An invoice stops being retried automatically when retry_count >= MAX_RETRIES.
 * Staff can still force a manual retry from the UI even after MAX_RETRIES is hit.
 */

define('MAX_RETRIES',           5);
define('RETRY_WINDOW_MINUTES', 15);  // cooldown between automatic retries

$is_web = (php_sapi_name() !== 'cli');

if ($is_web) {
    session_start();
    header('Content-Type: application/json');
}

include '../layouts/dbconn.php';
require_once '../includes/EtimsIntegration.php';

// ============================================================
// AUTH (web requests only)
// ============================================================
if ($is_web) {
    if (!isset($_SESSION['user_id'])) {
        echo json_encode(array('success' => false, 'message' => 'Unauthorized'));
        exit();
    }
    $allowed = array('SUPER_ADMIN', 'HOSPITAL_ADMIN', 'ACCOUNTANT', 'BILLING_CLERK');
    if (!in_array(isset($_SESSION['role_code']) ? $_SESSION['role_code'] : '', $allowed)) {
        echo json_encode(array('success' => false, 'message' => 'Insufficient permissions'));
        exit();
    }
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        echo json_encode(array('success' => false, 'message' => 'POST required'));
        exit();
    }
}

// ============================================================
// LOG HELPER
// ============================================================
$log_dir = __DIR__ . '/../logs';
if (!is_dir($log_dir)) {
    @mkdir($log_dir, 0755, true);
}

function retry_log($log_dir, $data) {
    $line = '[' . date('Y-m-d H:i:s') . '] ' . json_encode($data) . "\n";
    @file_put_contents($log_dir . '/etims_retry.log', $line, FILE_APPEND | LOCK_EX);
}

// ============================================================
// DETERMINE SCOPE
// ============================================================
$single_invoice_id = 0;
if ($is_web && !empty($_POST['invoice_id'])) {
    $single_invoice_id = intval($_POST['invoice_id']);
}

// ============================================================
// eTIMS INSTANCE
// ============================================================
$etims = new EtimsIntegration($conn);

if (!$etims->isEnabled() && !$etims->isMockMode()) {
    $msg = array('success' => false, 'message' => 'eTIMS integration disabled.');
    if ($is_web) {
        echo json_encode($msg);
    } else {
        echo "eTIMS integration disabled.\n";
    }
    exit();
}

// ============================================================
// FETCH RETRYABLE RECORDS
// ============================================================
if ($single_invoice_id > 0) {
    // Single invoice — bypass time window, ignore MAX_RETRIES for manual
    $queue_sql = "
        SELECT ei.*,
               i.invoice_number, i.total_amount, i.bill_to,
               i.insurance_provider_id, i.patient_id,
               u.full_name     AS patient_name,
               u.tax_pin       AS patient_pin,
               u.phone_primary AS patient_phone,
               u.address       AS patient_address,
               ip.provider_name AS insurance_provider_name,
               ip.kra_pin       AS insurance_kra_pin
        FROM etims_invoices ei
        JOIN invoices i ON ei.invoice_id = i.invoice_id
        LEFT JOIN patients            p  ON i.patient_id           = p.patient_id
        LEFT JOIN users               u  ON p.user_id              = u.user_id
        LEFT JOIN insurance_providers ip ON i.insurance_provider_id = ip.provider_id
        WHERE ei.invoice_id = ?
          AND ei.submission_status IN ('failed','pending')
        LIMIT 1
    ";
    $stmt = $conn->prepare($queue_sql);
    $stmt->bind_param("i", $single_invoice_id);

} else {
    // Bulk cron — respect MAX_RETRIES and cooldown window
    $queue_sql = "
        SELECT ei.*,
               i.invoice_number, i.total_amount, i.bill_to,
               i.insurance_provider_id, i.patient_id,
               u.full_name     AS patient_name,
               u.tax_pin       AS patient_pin,
               u.phone_primary AS patient_phone,
               u.address       AS patient_address,
               ip.provider_name AS insurance_provider_name,
               ip.kra_pin       AS insurance_kra_pin
        FROM etims_invoices ei
        JOIN invoices i ON ei.invoice_id = i.invoice_id
        LEFT JOIN patients            p  ON i.patient_id           = p.patient_id
        LEFT JOIN users               u  ON p.user_id              = u.user_id
        LEFT JOIN insurance_providers ip ON i.insurance_provider_id = ip.provider_id
        WHERE ei.submission_status IN ('failed','pending')
          AND ei.retry_count < ?
          AND (ei.last_retry_at IS NULL
               OR ei.last_retry_at <= DATE_SUB(NOW(), INTERVAL ? MINUTE))
        ORDER BY ei.retry_count ASC, ei.created_at ASC
        LIMIT 20
    ";
    $stmt = $conn->prepare($queue_sql);
    $stmt->bind_param("ii", MAX_RETRIES, RETRY_WINDOW_MINUTES);
}

$stmt->execute();
$queue = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

if (empty($queue)) {
    $msg = array('success' => true, 'message' => 'No retryable records found', 'processed' => 0);
    if ($is_web) {
        echo json_encode($msg);
    } else {
        echo "No retryable records.\n";
    }
    exit();
}

retry_log($log_dir, array('event' => 'retry_batch_start', 'count' => count($queue)));

// ============================================================
// PROCESS EACH RECORD
// ============================================================
$results = array();

foreach ($queue as $record) {
    $inv_id      = (int) $record['invoice_id'];
    $queue_id    = (int) $record['etims_invoice_id'];
    $retry_count = (int) $record['retry_count'];

    retry_log($log_dir, array(
        'event'        => 'retry_attempt',
        'invoice_id'   => $inv_id,
        'retry_count'  => $retry_count + 1,
    ));

    // Mark as pending and bump counter
    $bump_stmt = $conn->prepare(
        "UPDATE etims_invoices
         SET submission_status = 'pending',
             retry_count       = retry_count + 1,
             last_retry_at     = NOW()
         WHERE etims_invoice_id = ?"
    );
    $bump_stmt->bind_param("i", $queue_id);
    $bump_stmt->execute();

    // Rebuild line items
    $items_res = $conn->prepare(
        "SELECT item_id, item_type, description, quantity, unit_price
         FROM invoice_items WHERE invoice_id = ? ORDER BY item_id"
    );
    $items_res->bind_param("i", $inv_id);
    $items_res->execute();
    $items_rows = $items_res->get_result();

    if (!$items_rows || $items_rows->num_rows === 0) {
        $no_items_stmt = $conn->prepare(
            "UPDATE etims_invoices
             SET submission_status = 'failed', result_message = 'No line items'
             WHERE etims_invoice_id = ?"
        );
        $no_items_stmt->bind_param("i", $queue_id);
        $no_items_stmt->execute();
        $results[] = array('invoice_id' => $inv_id, 'success' => false, 'message' => 'No line items');
        continue;
    }

    $invoice_items = array();
    $seq = 1;
    while ($item = $items_rows->fetch_assoc()) {
        $invoice_items[] = array(
            'item_code'       => 'KE3NTU' . str_pad($seq, 5, '0', STR_PAD_LEFT),
            'item_name'       => ($item['description'] ? $item['description'] : ('Service ' . $item['item_type'])),
            'quantity'        => (int)   $item['quantity'],
            'unit_price'      => (float) $item['unit_price'],
            'tax_type'        => 'D',
            'item_class_code' => '50130000',
        );
        $seq++;
    }

    // Resolve customer
    $is_insurance  = ($record['bill_to'] === 'INSURANCE' && !empty($record['insurance_provider_name']));
    $customer_name = $is_insurance
        ? $record['insurance_provider_name']
        : (isset($record['patient_name']) ? $record['patient_name'] : 'Walk-in Patient');
    $customer_pin  = $is_insurance
        ? (isset($record['insurance_kra_pin']) ? $record['insurance_kra_pin'] : '')
        : (isset($record['patient_pin'])       ? $record['patient_pin']       : '');

    $trader_inv_no = $record['invoice_number']
                  ? $record['invoice_number']
                  : ('INV-' . str_pad($inv_id, 6, '0', STR_PAD_LEFT));

    $submit_data = array(
        'invoice_number'    => $inv_id,
        'trader_invoice_no' => $trader_inv_no,
        'customer_name'     => $customer_name,
        'customer_pin'      => $customer_pin,
        'customer_mobile'   => isset($record['patient_phone'])   ? $record['patient_phone']   : '',
        'customer_address'  => isset($record['patient_address']) ? $record['patient_address'] : '',
        'amount_paid'       => (float) $record['total_amount'],
        'change_given'      => 0.00,
        'payment_mode'      => 'Cash',
        'items'             => $invoice_items,
        'remark'            => 'Tax Invoice (Retry)',
    );

    $response = $etims->submitInvoice($submit_data);

    retry_log($log_dir, array(
        'event'      => 'retry_response',
        'invoice_id' => $inv_id,
        'success'    => $response['success'],
        'message'    => isset($response['message']) ? $response['message'] : '',
    ));

    // Handle complete API failure
    if (!$response['success']) {
        $fail_msg  = isset($response['message']) ? $response['message'] : 'Unknown error';
        $fail_raw  = json_encode($response);
        $fail_stmt = $conn->prepare(
            "UPDATE etims_invoices
             SET submission_status = 'failed',
                 result_message    = ?,
                 etims_response    = ?,
                 updated_at        = NOW()
             WHERE etims_invoice_id = ?"
        );
        $fail_stmt->bind_param("ssi", $fail_msg, $fail_raw, $queue_id);
        $fail_stmt->execute();
        $results[] = array('invoice_id' => $inv_id, 'success' => false, 'message' => $fail_msg);
        continue;
    }

    // Parse response
    $resp_top   = isset($response['data'])           ? $response['data']           : array();
    $inner_data = isset($resp_top['data'])            ? $resp_top['data']           : array();
    $kra_dto    = isset($inner_data['kraReceiptResponseDto']) ? $inner_data['kraReceiptResponseDto'] : array();

    $result_cd = null;
    if (isset($kra_dto['resultCd']))      $result_cd = $kra_dto['resultCd'];
    elseif (isset($resp_top['resultCd'])) $result_cd = $resp_top['resultCd'];
    elseif (isset($response['result_cd'])) $result_cd = $response['result_cd'];

    $result_msg = isset($kra_dto['resultMsg']) ? $kra_dto['resultMsg'] : (isset($resp_top['resultMsg']) ? $resp_top['resultMsg'] : 'OK');

    // "Invoice already exists" — extract QR and save
    $resp_message   = isset($response['message']) ? strtolower($response['message']) : '';
    $already_exists = (
        strpos($resp_message, 'invoice number exist') !== false
        && !empty($inner_data['invoiceNumberSdcIdUrl'])
    );

    if ($already_exists) {
        $e_qr       = $inner_data['invoiceNumberSdcIdUrl'];
        $e_inv_no   = isset($inner_data['invoiceNumber']) ? $inner_data['invoiceNumber'] : '';
        $e_date     = date('Y-m-d H:i:s');
        $e_internal = null;
        $rcpt_sign  = null;

    } elseif ($result_cd !== null && $result_cd !== '000') {
        // KRA rejection
        $fail_msg  = "KRA code {$result_cd}: {$result_msg}";
        $fail_raw  = json_encode($response);
        $fail_stmt = $conn->prepare(
            "UPDATE etims_invoices
             SET submission_status = 'failed',
                 result_code       = ?,
                 result_message    = ?,
                 etims_response    = ?,
                 updated_at        = NOW()
             WHERE etims_invoice_id = ?"
        );
        $fail_stmt->bind_param("sssi", $result_cd, $fail_msg, $fail_raw, $queue_id);
        $fail_stmt->execute();
        $results[] = array('invoice_id' => $inv_id, 'success' => false, 'message' => $fail_msg);
        continue;

    } else {
        // Fresh success
        $rcpt_sign = null;
        if (isset($resp_top['rcptSign']))            $rcpt_sign = $resp_top['rcptSign'];
        elseif (isset($inner_data['rcptSign']))      $rcpt_sign = $inner_data['rcptSign'];
        elseif (isset($kra_dto['data']['rcptSign'])) $rcpt_sign = $kra_dto['data']['rcptSign'];

        $intrl_data = null;
        if (isset($resp_top['intrlData']))       $intrl_data = $resp_top['intrlData'];
        elseif (isset($inner_data['intrlData'])) $intrl_data = $inner_data['intrlData'];

        $mrc_no = null;
        if (isset($resp_top['mrcNo']))       $mrc_no = $resp_top['mrcNo'];
        elseif (isset($inner_data['mrcNo'])) $mrc_no = $inner_data['mrcNo'];

        $e_inv_no = (string)($mrc_no ? $mrc_no : (isset($resp_top['totRcptNo']) ? $resp_top['totRcptNo'] : ''));

        $rcpt_dt = null;
        if (isset($resp_top['rcptPbctDt']))  $rcpt_dt = $resp_top['rcptPbctDt'];
        elseif (isset($resp_top['resultDt'])) $rcpt_dt = $resp_top['resultDt'];
        elseif (isset($kra_dto['resultDt']))  $rcpt_dt = $kra_dto['resultDt'];

        $e_date = date('Y-m-d H:i:s');
        if ($rcpt_dt && preg_match('/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/', $rcpt_dt, $m)) {
            $e_date = "{$m[1]}-{$m[2]}-{$m[3]} {$m[4]}:{$m[5]}:{$m[6]}";
        }

        $e_internal = trim(
            ($intrl_data ? $intrl_data : '') . '|' . ($rcpt_sign ? $rcpt_sign : ''),
            '|'
        );
        $e_internal = $e_internal ? $e_internal : null;

        $e_qr = $etims->extractQrCode($resp_top);
        if (empty($e_qr) && !empty($inner_data['invoiceNumberSdcIdUrl'])) {
            $e_qr = $inner_data['invoiceNumberSdcIdUrl'];
        }
    }

    // Save to etims_invoices
    $resp_raw  = json_encode($response);
    $req_raw   = json_encode($submit_data);
    $e_inv_save      = isset($e_inv_no)   ? (string)$e_inv_no : '';
    $rcpt_sign_save  = isset($rcpt_sign)  ? $rcpt_sign        : null;
    $e_qr_save       = isset($e_qr)       ? $e_qr             : null;
    $e_internal_save = isset($e_internal) ? $e_internal        : null;
    $e_date_save     = isset($e_date)     ? $e_date           : date('Y-m-d H:i:s');

    $ok_stmt = $conn->prepare(
        "UPDATE etims_invoices
         SET submission_status    = 'submitted',
             etims_invoice_number = ?,
             receipt_signature    = ?,
             qr_code_url          = ?,
             result_code          = '000',
             result_message       = 'Success',
             submission_date      = NOW(),
             internal_data        = ?,
             etims_request        = ?,
             etims_response       = ?,
             updated_at           = NOW()
         WHERE etims_invoice_id   = ?"
    );
    $ok_stmt->bind_param(
        "ssssssi",
        $e_inv_save, $rcpt_sign_save, $e_qr_save,
        $e_internal_save, $req_raw, $resp_raw,
        $queue_id
    );
    $ok_stmt->execute();

    // Save to invoices table
    $inv_stmt = $conn->prepare(
        "UPDATE invoices
         SET etims_submitted       = 1,
             etims_invoice_number  = ?,
             etims_internal_data   = ?,
             etims_qr_code         = ?,
             etims_submission_date = ?
         WHERE invoice_id = ?"
    );
    $inv_stmt->bind_param("ssssi", $e_inv_save, $e_internal_save, $e_qr_save, $e_date_save, $inv_id);
    $inv_stmt->execute();

    retry_log($log_dir, array(
        'event'             => 'retry_success',
        'invoice_id'        => $inv_id,
        'cu_invoice_number' => $e_inv_save,
        'already_existed'   => $already_exists,
    ));

    $results[] = array(
        'invoice_id'        => $inv_id,
        'success'           => true,
        'cu_invoice_number' => $e_inv_save,
        'qr_code'           => $e_qr_save,
        'already_existed'   => $already_exists,
    );
}

// ============================================================
// SUMMARY
// ============================================================
$succeeded = 0;
foreach ($results as $r) {
    if ($r['success']) $succeeded++;
}
$failed = count($results) - $succeeded;

$summary = array(
    'success'   => true,
    'message'   => "Processed: {$succeeded} succeeded, {$failed} failed",
    'succeeded' => $succeeded,
    'failed'    => $failed,
    'results'   => $results,
);

retry_log($log_dir, array('event' => 'retry_batch_done', 'succeeded' => $succeeded, 'failed' => $failed));

if ($is_web) {
    echo json_encode($summary);
} else {
    echo "Done: {$succeeded} succeeded, {$failed} failed\n";
}