<?php
/**
 * submit_invoice_to_etims.php
 *
 * Queue-first eTIMS invoice submission.
 *
 * Flow:
 *  1. Validate & fetch invoice from DB
 *  2. Upsert record in etims_invoices (status = pending) — committed immediately
 *  3. Call KRA API  (up to 3 auto-retries on network errors)
 *  4. Success  → mark submitted, store QR + signature + invoice number,
 *               advance invoice status DRAFT → SENT
 *  5. Failure  → mark failed, store error — cron/manual retry will pick it up
 *
 * "invoice number exist" response is treated as a success:
 *   KRA already has the invoice, so we extract the QR from the response and save it.
 */

session_start();
header('Content-Type: application/json');

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

// ============================================================
// AUTH & PERMISSION CHECKS
// ============================================================
if (!isset($_SESSION['user_id'])) {
    echo json_encode(['success' => false, 'message' => 'Unauthorized']);
    exit();
}

$allowed_roles = ['SUPER_ADMIN', 'HOSPITAL_ADMIN', 'ACCOUNTANT', 'BILLING_CLERK', 'FRONT_OFFICE'];
if (!isset($_SESSION['role_code']) || !in_array($_SESSION['role_code'], $allowed_roles)) {
    echo json_encode(['success' => false, 'message' => 'Insufficient permissions']);
    exit();
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'message' => 'Invalid request method']);
    exit();
}

// ============================================================
// INPUT
// ============================================================
$invoice_id           = isset($_POST['invoice_id'])  ? intval($_POST['invoice_id'])  : 0;
$kra_pin_override     = isset($_POST['kra_pin'])      ? trim($_POST['kra_pin'])       : '';
$client_name_override = isset($_POST['client_name'])  ? trim($_POST['client_name'])   : '';
$payment_mode         = isset($_POST['payment_mode']) ? trim($_POST['payment_mode'])  : 'Cash';
$is_retry             = isset($_POST['is_retry'])     ? (bool)$_POST['is_retry']      : false;

if ($invoice_id <= 0) {
    echo json_encode(['success' => false, 'message' => 'Invalid invoice ID']);
    exit();
}

// ============================================================
// eTIMS CHECK
// ============================================================
$etims = new EtimsIntegration($conn);
if (!$etims->isEnabled() && !$etims->isMockMode()) {
    echo json_encode(['success' => false, 'message' => 'eTIMS integration is disabled system-wide.']);
    exit();
}

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

function etims_log($log_dir, $data) {
    @file_put_contents(
        $log_dir . '/etims_api_response.jsonl',
        json_encode($data) . "\n",
        LOCK_EX | FILE_APPEND
    );
}

// ============================================================
// MAIN
// ============================================================
mysqli_begin_transaction($conn);

try {

    // ----------------------------------------------------------
    // 1. Fetch invoice + patient + insurance
    // ----------------------------------------------------------
    $invoice_query = "
        SELECT
            i.invoice_id,
            i.invoice_number,
            i.bill_to,
            i.status,
            i.total_amount,
            i.etims_invoice_number,
            i.insurance_provider_id,
            u.full_name     AS patient_name,
            u.tax_pin       AS patient_pin,
            u.phone_primary AS patient_phone,
            u.address       AS patient_address,
            p.patient_id,
            p.insurance_number,
            p.policy_number,
            ip.provider_name AS insurance_provider_name,
            ip.kra_pin       AS insurance_kra_pin
        FROM invoices i
        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 i.invoice_id = ?
    ";

    $stmt = $conn->prepare($invoice_query);
    $stmt->bind_param("i", $invoice_id);
    $stmt->execute();
    $invoice_result = $stmt->get_result();

    if ($invoice_result->num_rows === 0) {
        throw new Exception("Invoice not found");
    }

    $invoice = $invoice_result->fetch_assoc();

    // Block re-submission unless this is an explicit retry
    if (!$is_retry && !empty($invoice['etims_invoice_number'])) {
        throw new Exception(
            "Invoice already submitted to eTIMS (CU#: {$invoice['etims_invoice_number']})"
        );
    }

    // ----------------------------------------------------------
    // 2. Check for existing queue record
    // ----------------------------------------------------------
    $existing_queue = null;
    $eq_stmt = $conn->prepare(
        "SELECT etims_invoice_id, retry_count, submission_status
         FROM etims_invoices WHERE invoice_id = ? LIMIT 1"
    );
    $eq_stmt->bind_param("i", $invoice_id);
    $eq_stmt->execute();
    $eq_result = $eq_stmt->get_result();
    if ($eq_result->num_rows > 0) {
        $existing_queue = $eq_result->fetch_assoc();
    }

    // Block if already marked submitted (unless forced retry)
    if (!$is_retry && $existing_queue && $existing_queue['submission_status'] === 'submitted') {
        throw new Exception("Invoice is already marked as submitted in the eTIMS queue.");
    }

    // ----------------------------------------------------------
    // 3. Fetch line items
    // ----------------------------------------------------------
    $stmt_items = $conn->prepare(
        "SELECT item_id, item_type, description, quantity, unit_price, total_price
         FROM invoice_items WHERE invoice_id = ? ORDER BY item_id"
    );
    $stmt_items->bind_param("i", $invoice_id);
    $stmt_items->execute();
    $items_result = $stmt_items->get_result();

    if ($items_result->num_rows === 0) {
        throw new Exception("Invoice has no line items");
    }

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

    // ----------------------------------------------------------
    // 4. Resolve customer name and PIN
    //    Priority: POST override > insurance record > patient record
    // ----------------------------------------------------------
    $is_insurance_client = (
        isset($invoice['bill_to'])
        && $invoice['bill_to'] === 'INSURANCE'
        && !empty($invoice['insurance_provider_name'])
    );

    if ($is_insurance_client) {
        $customer_name = ($client_name_override !== '') ? $client_name_override : $invoice['insurance_provider_name'];
        $customer_pin  = ($kra_pin_override     !== '') ? $kra_pin_override     : ($invoice['insurance_kra_pin'] ?? '');
    } else {
        $customer_name = ($client_name_override !== '') ? $client_name_override : ($invoice['patient_name']  ?? 'Walk-in Patient');
        $customer_pin  = ($kra_pin_override     !== '') ? $kra_pin_override     : ($invoice['patient_pin']   ?? '');
    }

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

    // ----------------------------------------------------------
    // 5. Fetch business name and address for receipt block
    // ----------------------------------------------------------
    $biz_res = $conn->query(
        "SELECT setting_key, setting_value FROM system_settings
         WHERE category = 'hospital' AND setting_key IN ('hospital_name','hospital_address')"
    );
    $biz_cfg = [];
    if ($biz_res) {
        while ($r = $biz_res->fetch_assoc()) {
            $biz_cfg[$r['setting_key']] = $r['setting_value'];
        }
    }

    // ----------------------------------------------------------
    // 6. Upsert etims_invoices queue record (status = pending)
    //    Committed immediately so it survives even if API call fails
    // ----------------------------------------------------------
    if ($existing_queue) {
        $upsert = $conn->prepare(
            "UPDATE etims_invoices
             SET submission_status = 'pending',
                 retry_count       = retry_count + 1,
                 last_retry_at     = NOW(),
                 updated_at        = NOW()
             WHERE invoice_id = ?"
        );
        $upsert->bind_param("i", $invoice_id);
    } else {
        $upsert = $conn->prepare(
            "INSERT INTO etims_invoices
                 (invoice_id, invoice_number, submission_status, retry_count, created_at, updated_at)
             VALUES (?, ?, 'pending', 0, NOW(), NOW())"
        );
        $upsert->bind_param("is", $invoice_id, $trader_inv_no);
    }

    if (!$upsert->execute()) {
        throw new Exception("Failed to create eTIMS queue record: " . $conn->error);
    }

    $etims_queue_id = $existing_queue
        ? (int) $existing_queue['etims_invoice_id']
        : (int) $conn->insert_id;

    // Commit queue record NOW before hitting the API
    mysqli_commit($conn);
    mysqli_begin_transaction($conn);

    // ----------------------------------------------------------
    // 7. Build payload and submit
    // ----------------------------------------------------------
    $submit_data = [
        'invoice_number'    => (int) $invoice_id,
        'trader_invoice_no' => $trader_inv_no,
        'customer_name'     => $customer_name,
        'customer_pin'      => $customer_pin,
        'customer_mobile'   => $invoice['patient_phone']   ?? '',
        'customer_address'  => $invoice['patient_address'] ?? '',
        'amount_paid'       => (float) $invoice['total_amount'],
        'change_given'      => 0.00,
        'payment_mode'      => $payment_mode,
        'items'             => $invoice_items,
        'remark'            => 'Tax Invoice',
        'business_name'     => $biz_cfg['hospital_name']    ?? 'PRODIGY',
        'business_address'  => $biz_cfg['hospital_address'] ?? '',
    ];

    etims_log($log_dir, [
        'timestamp'     => date('Y-m-d\TH:i:sP'),
        'invoice_id'    => $invoice_id,
        'queue_id'      => $etims_queue_id,
        'event'         => 'pre_submission',
        'customer_name' => $customer_name,
        'customer_pin'  => $customer_pin,
    ]);

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

    etims_log($log_dir, [
        'timestamp'    => date('Y-m-d\TH:i:sP'),
        'invoice_id'   => $invoice_id,
        'queue_id'     => $etims_queue_id,
        'event'        => 'api_response',
        'success'      => $response['success'],
        'raw_response' => $response,
    ]);

    // ----------------------------------------------------------
    // 8. Handle complete API failure (connection / 4xx / 5xx)
    // ----------------------------------------------------------
    if (!$response['success']) {
        $fail_msg = $response['message'] ?? 'Unknown error';
        $fail_raw = json_encode($response);

        $fail_stmt = $conn->prepare(
            "UPDATE etims_invoices
             SET submission_status = 'failed',
                 result_code       = 'ERR',
                 result_message    = ?,
                 etims_response    = ?,
                 updated_at        = NOW()
             WHERE etims_invoice_id = ?"
        );
        $fail_stmt->bind_param("ssi", $fail_msg, $fail_raw, $etims_queue_id);
        $fail_stmt->execute();
        mysqli_commit($conn);

        throw new Exception("eTIMS API Error: {$fail_msg}");
    }

    // ----------------------------------------------------------
    // 9. Parse response
    //    Handles three known response shapes:
    //      A) Fresh success  — resultCd = "000", rcptSign present
    //      B) Already exists — "invoice number exist", invoiceNumberSdcIdUrl present
    //      C) KRA rejection  — resultCd != "000"
    // ----------------------------------------------------------
    $resp_top   = $response['data']          ?? [];
    $inner_data = $resp_top['data']           ?? [];
    $kra_dto    = $inner_data['kraReceiptResponseDto'] ?? [];

    // Resolve KRA result code from any nesting level
    $result_cd = $kra_dto['resultCd'] ?? $resp_top['resultCd'] ?? $response['result_cd'] ?? null;
    $result_msg = $kra_dto['resultMsg'] ?? $resp_top['resultMsg'] ?? 'OK';

    // Detect "invoice number exist" — already on KRA
    $resp_message   = isset($response['message']) ? strtolower($response['message']) : '';
    $already_exists = (
        strpos($resp_message, 'invoice number exist') !== false
        && !empty($inner_data['invoiceNumberSdcIdUrl'])
    );

    // ----------------------------------------------------------
    // 9A. Invoice already exists on KRA — extract QR and save
    // ----------------------------------------------------------
    if ($already_exists) {
        $e_qr       = $inner_data['invoiceNumberSdcIdUrl'];
        $e_inv_no   = $inner_data['invoiceNumber'] ?? '';
        $e_date     = date('Y-m-d H:i:s');
        $e_internal = null;
        $rcpt_sign  = null;

        etims_log($log_dir, [
            'timestamp'  => date('Y-m-d\TH:i:sP'),
            'invoice_id' => $invoice_id,
            'note'       => 'Invoice already on KRA — saving QR from response',
            'qr'         => $e_qr,
        ]);

    // ----------------------------------------------------------
    // 9B. KRA rejected the invoice
    // ----------------------------------------------------------
    } elseif ($result_cd !== null && $result_cd !== '000') {
        $kra_errors = [
            '924' => 'Invoice number already exists on KRA (code 924).',
            '803' => 'Duplicate trader invoice number (code 803).',
            '902' => 'Invalid KRA PIN supplied (code 902).',
            '910' => "Request parameter error — {$result_msg}",
        ];
        $err_msg = $kra_errors[$result_cd]
                 ?? "KRA rejected invoice (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, $err_msg, $fail_raw, $etims_queue_id);
        $fail_stmt->execute();
        mysqli_commit($conn);

        throw new Exception($err_msg);

    // ----------------------------------------------------------
    // 9C. Fresh successful submission
    // ----------------------------------------------------------
    } else {
        $rcpt_sign = $resp_top['rcptSign']
                  ?? $inner_data['rcptSign']
                  ?? $kra_dto['data']['rcptSign']
                  ?? null;

        $intrl_data = $resp_top['intrlData'] ?? $inner_data['intrlData'] ?? null;
        $mrc_no     = $resp_top['mrcNo']     ?? $inner_data['mrcNo']     ?? null;

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

        // Parse receipt date
        $rcpt_dt = $resp_top['rcptPbctDt']
                ?? $resp_top['resultDt']
                ?? $kra_dto['resultDt']
                ?? null;

        $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 ?? '') . '|' . ($rcpt_sign ?? ''), '|') ?: null;

        // QR Code — try API response first, then invoiceNumberSdcIdUrl fallback
        $e_qr = $etims->extractQrCode($resp_top);
        if (empty($e_qr) && !empty($inner_data['invoiceNumberSdcIdUrl'])) {
            $e_qr = $inner_data['invoiceNumberSdcIdUrl'];
        }

        if (empty($e_qr)) {
            etims_log($log_dir, [
                'timestamp'  => date('Y-m-d\TH:i:sP'),
                'invoice_id' => $invoice_id,
                'warning'    => 'QR could not be built — rcptSign missing from response',
                'resp_keys'  => array_keys($resp_top),
            ]);
        }
    }

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

    $update_queue = $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(),
             etims_request        = ?,
             etims_response       = ?,
             internal_data        = ?,
             updated_at           = NOW()
         WHERE etims_invoice_id   = ?"
    );
    $update_queue->bind_param(
        "ssssssi",
        $e_inv_save, $rcpt_sign_save, $e_qr_save,
        $req_raw, $resp_raw, $e_internal_save,
        $etims_queue_id
    );

    if (!$update_queue->execute()) {
        throw new Exception("DB update (etims_invoices) failed: " . $conn->error);
    }

    // ----------------------------------------------------------
    // 11. Update main invoices table
    //     Also advance status: DRAFT → SENT on successful eTIMS sign.
    //     All other statuses (PARTIAL_PAYMENT, PAID, INSURANCE_PENDING,
    //     OVERDUE) are preserved — eTIMS signing ≠ payment.
    // ----------------------------------------------------------
    $update_inv = $conn->prepare(
        "UPDATE invoices
         SET etims_submitted       = 1,
             etims_invoice_number  = ?,
             etims_internal_data   = ?,
             etims_qr_code         = ?,
             etims_submission_date = ?,
             status = CASE
                          WHEN status = 'DRAFT' THEN 'SENT'
                          ELSE status
                      END
         WHERE invoice_id = ?"
    );
    $update_inv->bind_param(
        "ssssi",
        $e_inv_save, $e_internal_save, $e_qr_save, $e_date_save,
        $invoice_id
    );

    if (!$update_inv->execute()) {
        throw new Exception("DB update (invoices) failed: " . $conn->error);
    }

    mysqli_commit($conn);

    echo json_encode([
        'success' => true,
        'message' => $already_exists
            ? 'Invoice was already on KRA — QR code retrieved and saved.'
            : 'Invoice submitted to eTIMS successfully',
        'data' => [
            'etims_queue_id'    => $etims_queue_id,
            'cu_invoice_number' => $e_inv_save,
            'receipt_date'      => $e_date_save,
            'receipt_signature' => $rcpt_sign_save,
            'internal_data'     => $e_internal_save,
            'qr_code'           => $e_qr_save,
            'customer_name'     => $customer_name,
            'customer_pin'      => $customer_pin,
            'result_code'       => $result_cd,
            'already_existed'   => $already_exists,
        ],
    ]);

} catch (Exception $e) {
    mysqli_rollback($conn);

    $err = ['success' => false, 'message' => $e->getMessage()];
    if (isset($response)) {
        $err['api_response'] = $response;
    }

    etims_log($log_dir, [
        'timestamp'  => date('Y-m-d\TH:i:sP'),
        'invoice_id' => $invoice_id,
        'event'      => 'error',
        'error'      => $e->getMessage(),
    ]);

    echo json_encode($err);
}