<?php
declare(strict_types=1);

// mysqli in exception mode
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn = new mysqli(
    "database-1.ctcksjciybw3.us-east-1.rds.amazonaws.com",
    "superuser",
    "@C8639kZDWV",
    "INDR"
);
$conn->set_charset("utf8mb4");

// --- Session/auth ---
session_start();
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 1800)) {
    session_unset();
    session_destroy();
    http_response_code(440);
    header('Content-Type: application/json');
    echo json_encode(['ok' => false, 'error' => 'Session expired. Please log in again.']);
    exit;
}
$_SESSION['LAST_ACTIVITY'] = time();
if (empty($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
    header('Location: index.php');
    exit;
}
$username = $_SESSION['username'] ?? 'unknown';
session_write_close(); // release lock so same-user parallel requests don’t block

// ---------- Helpers ----------
function json_input(string $key): array
{
    if (!isset($_POST[$key]))
        return [];
    $decoded = json_decode((string) $_POST[$key], true);
    if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
        throw new RuntimeException("Invalid JSON in {$key}: " . json_last_error_msg());
    }
    return is_array($decoded) ? $decoded : [];
}
function s($v): string
{
    return (string) ($v ?? '');
}
function to_money($v): float
{
    if (is_string($v))
        $v = str_replace(',', '', $v);
    return is_numeric($v) ? (float) $v : 0.0;
}
function to_int($v): int
{
    return (int) ($v ?? 0);
}
function norm_date(?string $d): ?string
{
    if (!$d)
        return null;
    $ts = strtotime($d);
    return $ts ? date('Y-m-d', $ts) : null;
}

// Header key: INDR_CODE | CUST_CODE | TRANS_NO  (header’s own doc no)
function hdr_key(array $r): string
{
    return implode('|', [s($r['INDR_CODE'] ?? ''), s($r['CUST_CODE'] ?? ''), s($r['TRANS_NO'] ?? '')]);
}

// Detail key: INDR_CODE | CUST_CODE | LINK_DOC_NO  (points to header TRANS_NO)
function det_key(array $r): string
{
    $link = $r['LINK_DOC_NO'] ?? null;
    if ($link === null || $link === '') {
        // Fallback: if caller didn’t send LINK_DOC_NO, try TRANS_NO (not your case, but safe)
        $link = $r['TRANS_NO'] ?? '';
    }
    return implode('|', [s($r['INDR_CODE'] ?? ''), s($r['CUST_CODE'] ?? ''), s($link)]);
}

// ---------- Read inputs ----------
try {
    $array_crd = json_input('data_crd');          // headers
    $array_inv = json_input('data_inv');          // details

    // round-off sources (make tolerant to your param names)
    $array_roff_crnt = json_input('data_roff_crnt');    // [{cash_discount_roff: ...}]  (your older name)
    $array_roff_cmt = json_input('data_roff_cmt');     // [{cash_discount_roff: ...}]  (what the screenshot shows)
    $array_roff_drnt = json_input('data_roff_drnt');    // [{interest_debit_note_roff: ...}]
    $array_roff_days45 = json_input('data_roff_days45');  // [{days45_roff: ...}]
} catch (Throwable $e) {
    http_response_code(400);
    header('Content-Type: application/json');
    echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
    exit;
}

// Compute global round off (adjust if you need per-header)
$roff_crnt = 0.0;
$src_crnt = !empty($array_roff_cmt) ? $array_roff_cmt : $array_roff_crnt;
foreach ($src_crnt as $r) {
    $roff_crnt = to_money($r['cash_discount_roff'] ?? 0);
}
$roff_drnt = 0.0;
foreach ($array_roff_drnt as $r) {
    $roff_drnt = to_money($r['interest_debit_note_roff'] ?? 0);
}
$roff_d45 = 0.0;
foreach ($array_roff_days45 as $r) {
    $roff_d45 = to_money($r['days45_roff'] ?? 0);
}
$global_round_off = $roff_crnt + $roff_drnt + $roff_d45;

// Bucket details by the detail-key (INDR|CUST|LINK_DOC_NO)
$details_by_key = [];
foreach ($array_inv as $d) {
    $k = det_key($d);
    $details_by_key[$k][] = $d;
}

// ✅ NEW: is this a multi-credit save? Drives whether MULTI_GROUP_ID gets populated.
// For a single credit, MULTI_GROUP_ID is left untouched (stays NULL) — zero impact
// on the existing single-credit path, existing reports, or existing Oracle procedure.
$is_multi_credit = count($array_crd) > 1;

// ---------- Prepare statements ----------
$stmtHdr = $conn->prepare("
    INSERT INTO FA_CR_DR_HDR_TEST
        (INDR_CODE, CUST_CODE, TRANS_TYPE, DUE_DT,
         TRANS_NO, TRANS_DATE, EFF_DATE,
         CREDIT_AMOUNT, PENDING_AMOUNT, AMOUNT_SELECTED, ROUND_OFF, 
         created_date, created_by)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?)
"); // "sssssssdddds"

// ✅ NEW: only used when $is_multi_credit is true, to stamp the shared group anchor
// onto each header row after it's inserted. Left unprepared (created lazily below)
// so the single-credit path never even touches this statement.
$stmtGroupUpdate = null;

$stmtDet = $conn->prepare("
    INSERT INTO FA_CR_DR_DETAILS_TEST
        (HDR_ID, LINK_DOC_NO, SL_NO, INDR_CODE, CUST_CODE, TRANS_NO, TRANS_DATE,
         TRANS_TYPE, DUE_DATE, TDS, AMOUNT_SELECT, NO_OF_DAYS, CD_PERCENTAGE,
         TAX_TYPE, CGST, SGST, IGST, TAXABLE_VALUE, ROUND_OFF,
         CASH_DISCOUNT, INTEREST_DEBIT_NOTE,
         created_date, created_by)
    VALUES
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?)
");
$DETAIL_TYPES = "issssssssddidsddddddds"; // 22 specifiers

// ---------- Execute ----------
$summary = [];
$multi_group_id = null; // ✅ NEW: shared anchor value for this save, if multi-credit

// ✅ FIX: the whole multi-credit save now commits or rolls back as ONE unit.
// Previously each header (and its details) committed independently — meaning a
// failure on, say, the 3rd credit would leave the 1st and 2nd credits already
// committed, an inconsistent partial multi-credit save. For a single credit this
// behaves identically to before (one header, one transaction, same as always).
$conn->begin_transaction();

try {
    foreach ($array_crd as $h) {
        // Header grouping key
        $hkey = hdr_key($h);
        // The details matching key must use LINK_DOC_NO == header TRANS_NO
        $lookupKey = implode('|', [s($h['INDR_CODE'] ?? ''), s($h['CUST_CODE'] ?? ''), s($h['TRANS_NO'] ?? '')]);

        // Normalize header fields
        $INDR_CODE = s($h['INDR_CODE'] ?? '');
        $CUST_CODE = s($h['CUST_CODE'] ?? '');
        $TRANS_TYPE = s($h['TRANS_TYPE'] ?? '');
        $DUE_DT = norm_date($h['DUE_DT'] ?? null);
        $TRANS_NO = s($h['TRANS_NO'] ?? '');
        $TRANS_DATE = norm_date($h['TRANS_DATE'] ?? null);
        $EFF_DATE = norm_date($h['EFF_DATE'] ?? null);
        $CREDIT_AMOUNT = to_money($h['CREDIT_AMT'] ?? 0);
        $PENDING_AMOUNT = to_money($h['PENDING_AMOUNT'] ?? 0);
        $AMOUNT_SELECTED = to_money($h['AMOUNT_SELECT'] ?? 0);
        $ROUND_OFF = $global_round_off;

        // Insert HEADER (one row) — column list and bind unchanged from before
        $stmtHdr->bind_param(
            "sssssssdddds",
            $INDR_CODE,
            $CUST_CODE,
            $TRANS_TYPE,
            $DUE_DT,
            $TRANS_NO,
            $TRANS_DATE,
            $EFF_DATE,
            $CREDIT_AMOUNT,
            $PENDING_AMOUNT,
            $AMOUNT_SELECTED,
            $ROUND_OFF,
            $username
        );
        $stmtHdr->execute();
        $hdrId = $conn->insert_id; // FK target for details

        // ✅ NEW: populate MULTI_GROUP_ID only for multi-credit saves.
        // First header in the batch becomes the anchor (points to itself);
        // every subsequent header points to that same anchor value.
        if ($is_multi_credit) {
            if ($multi_group_id === null) {
                $multi_group_id = $hdrId; // this header IS the anchor
            }
            if ($stmtGroupUpdate === null) {
                $stmtGroupUpdate = $conn->prepare(
                    "UPDATE FA_CR_DR_HDR_TEST SET MULTI_GROUP_ID = ? WHERE ID = ?"
                );
            }
            $stmtGroupUpdate->bind_param("ii", $multi_group_id, $hdrId);
            $stmtGroupUpdate->execute();
        }

        // Fetch matching details bucket using LINK_DOC_NO == header TRANS_NO
        $bucket = $details_by_key[$lookupKey] ?? [];

        if (empty($bucket)) {
            $someDetailKeys = array_slice(array_keys($details_by_key), 0, 5);
            throw new RuntimeException(
                "No details matched header {$lookupKey}; nothing inserted. " .
                "Saw detail keys like: " . implode(', ', $someDetailKeys)
            );
        }

        // Insert ALL the details (N rows) for this header
        $detail_count = 0;
        foreach ($bucket as $d) {
            $LINK_DOC_NO = s($d['LINK_DOC_NO'] ?? '');
            $SL_NO = s($d['SL_NO'] ?? '');
            $d_INDR_CODE = s($d['INDR_CODE'] ?? $INDR_CODE);
            $d_CUST_CODE = s($d['CUST_CODE'] ?? $CUST_CODE);
            $d_TRANS_NO = s($d['TRANS_NO'] ?? '');
            $d_TRANS_DATE = norm_date($d['TRANS_DATE'] ?? null);
            $d_TRANS_TYPE = s($d['TRANS_TYPE'] ?? '');
            $d_DUE_DATE = norm_date($d['DUE_DT'] ?? null);

            $TDS = to_money($d['TDS'] ?? 0);
            $AMOUNT_SELECT = to_money($d['AMOUNT_SELECT'] ?? 0);
            $NO_OF_DAYS = to_int($d['NO_OF_DAYS'] ?? 0);
            $CD_PERCENTAGE = to_money($d['CD_PERCENTAGE'] ?? 0);

            $TAX_TYPE = s($d['TAX_TYPE'] ?? '');
            $CGST = to_money($d['CGST'] ?? 0);
            $SGST = to_money($d['SGST'] ?? 0);
            $IGST = to_money($d['IGST'] ?? 0);
            $TAXABLE_VALUE = to_money($d['TAXABLE_VALUE'] ?? 0);
            $ROUND_OFF_VAL = to_money($d['ROUND_OFF_VALUE'] ?? 0);

            $CASH_DISCOUNT = to_money($d['CASH_DISCOUNT'] ?? 0);
            $INTEREST_DEBIT_NOTE = to_money($d['INTEREST_DEBIT_NOTE'] ?? 0);

            $stmtDet->bind_param(
                $DETAIL_TYPES,
                $hdrId,
                $LINK_DOC_NO,
                $SL_NO,
                $d_INDR_CODE,
                $d_CUST_CODE,
                $d_TRANS_NO,
                $d_TRANS_DATE,
                $d_TRANS_TYPE,
                $d_DUE_DATE,
                $TDS,
                $AMOUNT_SELECT,
                $NO_OF_DAYS,
                $CD_PERCENTAGE,
                $TAX_TYPE,
                $CGST,
                $SGST,
                $IGST,
                $TAXABLE_VALUE,
                $ROUND_OFF_VAL,
                $CASH_DISCOUNT,
                $INTEREST_DEBIT_NOTE,
                $username
            );
            $stmtDet->execute();
            $detail_count++;
        }

        $summary[] = [
            'hdr_id' => $hdrId,
            'multi_group_id' => $multi_group_id, // ✅ NEW: null for single-credit saves
            'key' => $lookupKey,
            'trans_no' => $TRANS_NO,
            'details' => $detail_count
        ];
    }

    // ✅ FIX: single commit for the whole save (all headers + all details together)
    $conn->commit();

    header('Content-Type: application/json');
    echo json_encode([
        'ok' => true,
        'message' => 'Inserted',
        'multi_credit' => $is_multi_credit,   // ✅ NEW
        'multi_group_id' => $multi_group_id,  // ✅ NEW (null for single-credit)
        'summary' => $summary
    ]);

} catch (Throwable $e) {
    try {
        $conn->rollback();
    } catch (Throwable $ignore) {
    }
    http_response_code(500);
    header('Content-Type: application/json');
    echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
} finally {
    if (isset($stmtDet) && $stmtDet instanceof mysqli_stmt)
        $stmtDet->close();
    if (isset($stmtHdr) && $stmtHdr instanceof mysqli_stmt)
        $stmtHdr->close();
    if (isset($stmtGroupUpdate) && $stmtGroupUpdate instanceof mysqli_stmt)
        $stmtGroupUpdate->close();
    $conn->close();
}
