<?php
// =====================================================
// File: api/sales/barcode_sale_add.php
// Purpose: Barcode Sale - save invoice, sale items, payments, mark barcode stock sold
// Stack: PHP + MySQL(PDO)
// v2 FIX (2026-06-01): প্রতিটা পেমেন্টের জন্য cash_transactions IN এন্ট্রি যোগ
// =====================================================

ini_set('display_errors', 0);
error_reporting(E_ALL);
header('Content-Type: application/json; charset=utf-8');

function out_json(array $data, int $code = 200): void {
    http_response_code($code);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

try {
    $root = dirname(__DIR__, 2);
    $dbFile = $root . '/config/db.php';
    $sessionFile = $root . '/config/session.php';

    if (file_exists($dbFile)) {
        require_once $dbFile;
    }

    if (file_exists($sessionFile)) {
        require_once $sessionFile;
    }

    // _finance.php — gs_insert_cash_transaction / gs_recalc_cash_accounts
    $financeFile = $root . '/api/_finance.php';
    if (file_exists($financeFile)) {
        require_once $financeFile;
    }

    if (function_exists('is_logged_in') && !is_logged_in()) {
        out_json(['success' => false, 'message' => 'লগইন প্রয়োজন'], 401);
    }

    if (!isset($pdo) || !($pdo instanceof PDO)) {
        out_json(['success' => false, 'message' => 'Database connection পাওয়া যায়নি'], 500);
    }

    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

    function request_data(): array {
        $raw = file_get_contents('php://input');
        $json = json_decode($raw, true);
        if (is_array($json)) return $json;
        return $_POST ?: [];
    }

    function table_columns(PDO $pdo, string $table): array {
        static $cache = [];
        if (isset($cache[$table])) return $cache[$table];
        $stmt = $pdo->query("SHOW COLUMNS FROM `{$table}`");
        $cols = [];
        foreach ($stmt->fetchAll() as $row) {
            $cols[strtolower($row['Field'])] = $row['Field'];
        }
        return $cache[$table] = $cols;
    }

    function first_value(array $row, array $keys, $default = null) {
        foreach ($keys as $key) {
            foreach ($row as $rk => $rv) {
                if (strtolower((string)$rk) === strtolower((string)$key)
                    && $rv !== null && $rv !== '') {
                    return $rv;
                }
            }
        }
        return $default;
    }

    function num_value($value, float $default = 0.0): float {
        if ($value === null || $value === '') return $default;
        $value = str_replace([',', '৳', ' '], '', (string)$value);
        return is_numeric($value) ? (float)$value : $default;
    }

    function str_value($value, string $default = ''): string {
        if ($value === null) return $default;
        return trim((string)$value);
    }

    function stock_is_unavailable(array $row): bool {
        $deletedAt = first_value($row, ['deleted_at'], null);
        if ($deletedAt !== null && trim((string)$deletedAt) !== '') return true;

        $saleId = first_value($row, ['sale_id', 'barcode_sale_id', 'sold_sale_id'], null);
        if ($saleId !== null && trim((string)$saleId) !== '' && (int)$saleId > 0) return true;

        $status = strtolower(trim((string)first_value($row, ['status'], '')));
        $soldStatuses = ['sold', 'sale', 'sales', 'out', 'out_stock', 'out-of-stock', 'delivered'];
        if (in_array($status, $soldStatuses, true)) return true;

        return false;
    }

    function insert_row(PDO $pdo, string $table, array $data): int {
        $colsMap = table_columns($pdo, $table);
        $insert = [];
        foreach ($data as $key => $value) {
            $lk = strtolower($key);
            if (isset($colsMap[$lk])) $insert[$colsMap[$lk]] = $value;
        }
        if (!$insert) throw new Exception("{$table} insert করার মতো column পাওয়া যায়নি");

        $cols = array_keys($insert);
        $quoted = array_map(fn($c) => "`{$c}`", $cols);
        $params = array_map(fn($c) => ':' . $c, $cols);

        $sql = "INSERT INTO `{$table}` (" . implode(',', $quoted) . ") VALUES (" . implode(',', $params) . ")";
        $stmt = $pdo->prepare($sql);
        foreach ($insert as $col => $value) $stmt->bindValue(':' . $col, $value);
        $stmt->execute();
        return (int)$pdo->lastInsertId();
    }

    function update_row_by_id(PDO $pdo, string $table, int $id, array $data): void {
        if ($id <= 0) return;
        $colsMap = table_columns($pdo, $table);
        $update = [];
        foreach ($data as $key => $value) {
            $lk = strtolower($key);
            if (isset($colsMap[$lk])) $update[$colsMap[$lk]] = $value;
        }
        if (!$update) return;

        $sets = [];
        foreach (array_keys($update) as $col) $sets[] = "`{$col}` = :{$col}";

        $sql = "UPDATE `{$table}` SET " . implode(', ', $sets) . " WHERE id = :_id LIMIT 1";
        $stmt = $pdo->prepare($sql);
        foreach ($update as $col => $value) $stmt->bindValue(':' . $col, $value);
        $stmt->bindValue(':_id', $id, PDO::PARAM_INT);
        $stmt->execute();
    }

    function current_gold_rate(PDO $pdo, string $karat): float {
        try {
            $stmt = $pdo->query("SELECT * FROM gold_rates ORDER BY id DESC LIMIT 1");
            $rateRow = $stmt->fetch();
            if (!$rateRow) return 0.0;

            $k = preg_replace('/[^0-9]/', '', $karat);
            $candidates = [];
            if ($k !== '') {
                $candidates = [
                    "rate_{$k}k", "rate_{$k}", "gold_{$k}k", "gold_{$k}", "k{$k}",
                    "rate_per_vori_{$k}k", "rate_per_vori_{$k}", "sale_rate_{$k}k", "sale_rate_{$k}"
                ];
            }
            $candidates = array_merge($candidates, [
                'rate_per_vori', 'gold_rate_per_vori', 'sale_rate_per_vori',
                'sale_rate', 'gold_rate', 'rate', 'amount'
            ]);
            return num_value(first_value($rateRow, $candidates, 0), 0);
        } catch (Throwable $e) {
            return 0.0;
        }
    }

    function build_item_from_stock(PDO $pdo, array $row, array $payloadItem): array {
        $barcode = str_value($payloadItem['barcode'] ?? first_value($row, ['barcode', 'code', 'barcode_no'], ''));
        $name = str_value($payloadItem['item_name'] ?? first_value($row, ['product_name', 'item_name', 'name', 'title', 'product_title'], 'Barcode Item'));
        $karat = str_value($payloadItem['karat'] ?? first_value($row, ['karat', 'carat', 'purity_name'], ''));

        $weightG = num_value($payloadItem['weight_g'] ?? first_value($row, [
            'weight_g', 'weight_gram', 'net_weight_gram', 'net_weight',
            'gross_weight_gram', 'gross_weight', 'weight'
        ], 0), 0);

        $weightVori = num_value($payloadItem['weight_vori'] ?? first_value($row, [
            'weight_vori', 'vori', 'total_vori'
        ], 0), 0);
        if ($weightVori <= 0 && $weightG > 0) $weightVori = $weightG / 11.664;

        $ratePerVori = num_value($payloadItem['sale_rate_per_vori'] ?? first_value($row, [
            'sale_rate_per_vori', 'rate_per_vori', 'gold_rate_per_vori', 'rate_vori'
        ], 0), 0);
        if ($ratePerVori <= 0) $ratePerVori = current_gold_rate($pdo, $karat);

        $making = num_value($payloadItem['making_charge'] ?? first_value($row, [
            'making_charge', 'making', 'labour_charge'
        ], 0), 0);

        $itemPrice = num_value($payloadItem['item_price'] ?? first_value($row, [
            'item_price', 'item_total', 'total_price', 'sale_price',
            'selling_price', 'store_value', 'stock_value', 'gold_value', 'price'
        ], 0), 0);
        if ($itemPrice <= 0 && $weightVori > 0 && $ratePerVori > 0) {
            $itemPrice = ($weightVori * $ratePerVori) + $making;
        }

        return [
            'barcode_stock_id' => (int)first_value($row, ['id'], 0),
            'barcode'          => $barcode,
            'item_name'        => $name,
            'karat'            => $karat,
            'weight_g'         => round($weightG, 3),
            'weight_vori'      => round($weightVori, 5),
            'sale_rate_per_vori' => round($ratePerVori, 2),
            'item_price'       => round($itemPrice, 2),
            'source'           => 'barcode_stock',
            'source_json'      => $row,
        ];
    }

    function create_invoice_no(PDO $pdo): string {
        $prefix = 'BINV-' . date('Ymd') . '-';
        $stmt = $pdo->prepare("SELECT invoice_no FROM barcode_sales WHERE invoice_no LIKE :p ORDER BY id DESC LIMIT 1");
        $stmt->execute([':p' => $prefix . '%']);
        $last = (string)($stmt->fetchColumn() ?: '');
        $n = 1;
        if ($last !== '' && preg_match('/-(\d+)$/', $last, $m)) {
            $n = ((int)$m[1]) + 1;
        }
        return $prefix . str_pad((string)$n, 3, '0', STR_PAD_LEFT);
    }

    // ── Input ──
    $data = request_data();
    $itemsInput = $data['items'] ?? [];
    if (!is_array($itemsInput) || count($itemsInput) < 1) {
        out_json(['success' => false, 'message' => 'কমপক্ষে ১টি বারকোড আইটেম যোগ করুন'], 400);
    }

    $customerType = str_value($data['customer_type'] ?? 'cash');
    if (!in_array($customerType, ['cash', 'party'], true)) $customerType = 'cash';

    $partyId = (int)($data['party_id'] ?? 0);
    if ($customerType !== 'party') $partyId = 0;

    $customerName   = str_value($data['customer_name']   ?? '');
    $customerPhone  = str_value($data['customer_phone']  ?? '');
    $previousBalance = num_value($data['previous_balance'] ?? 0);
    $makingCharge   = num_value($data['making_charge']   ?? 0);
    $discount       = num_value($data['discount']        ?? 0);
    $vatPercent     = num_value($data['vat_percent']     ?? 0);
    $exchangeAmount = num_value($data['exchange_amount'] ?? 0);
    $notes          = str_value($data['notes']           ?? '');
    $paymentsInput  = is_array($data['payments'] ?? null) ? $data['payments'] : [];
    $now            = date('Y-m-d H:i:s');

    $pdo->beginTransaction();

    $invoiceNo = create_invoice_no($pdo);
    $seen  = [];
    $items = [];

    foreach ($itemsInput as $rawItem) {
        if (!is_array($rawItem)) continue;

        $stockId = (int)($rawItem['barcode_stock_id'] ?? $rawItem['id'] ?? 0);
        $barcode = str_value($rawItem['barcode'] ?? '');

        if ($stockId <= 0 && $barcode === '') {
            throw new Exception('আইটেমের barcode_stock_id অথবা barcode পাওয়া যায়নি');
        }

        if ($stockId > 0) {
            $stmt = $pdo->prepare("SELECT * FROM barcode_stock WHERE id = :id LIMIT 1 FOR UPDATE");
            $stmt->execute([':id' => $stockId]);
        } else {
            $stmt = $pdo->prepare("SELECT * FROM barcode_stock WHERE barcode = :barcode ORDER BY id DESC LIMIT 1 FOR UPDATE");
            $stmt->execute([':barcode' => $barcode]);
        }

        $stockRow = $stmt->fetch();
        if (!$stockRow) throw new Exception('বারকোড স্টক পাওয়া যায়নি: ' . ($barcode ?: $stockId));

        if (stock_is_unavailable($stockRow)) {
            throw new Exception('এই বারকোডটি আগে বিক্রি হয়েছে: ' . str_value(first_value($stockRow, ['barcode'], $barcode)));
        }

        $item = build_item_from_stock($pdo, $stockRow, $rawItem);
        $uniqueKey = $item['barcode_stock_id'] > 0 ? 'id_' . $item['barcode_stock_id'] : 'bc_' . $item['barcode'];

        if (isset($seen[$uniqueKey])) {
            throw new Exception('একই বারকোড একাধিকবার যোগ করা হয়েছে: ' . $item['barcode']);
        }

        $seen[$uniqueKey] = true;
        $items[] = $item;
    }

    if (!$items) throw new Exception('Valid item পাওয়া যায়নি');

    $itemsTotal = 0.0;
    foreach ($items as $item) $itemsTotal += num_value($item['item_price']);

    $taxableAmount = max(0, $itemsTotal + $makingCharge - $discount - $exchangeAmount);
    $vatAmount     = round(($taxableAmount * $vatPercent) / 100, 2);
    $grandTotal    = round($taxableAmount + $vatAmount, 2);

    $normalizedPayments = [];
    $paidAmount = 0.0;

    foreach ($paymentsInput as $p) {
        if (!is_array($p)) continue;
        $amount = round(num_value($p['amount'] ?? 0), 2);
        if ($amount <= 0) continue;
        $accountId   = (int)($p['account_id'] ?? 0);
        $accountName = str_value($p['account_name'] ?? $p['name'] ?? 'Cash');
        $normalizedPayments[] = [
            'account_id'   => $accountId > 0 ? $accountId : 1,
            'account_name' => $accountName !== '' ? $accountName : 'Cash',
            'amount'       => $amount,
        ];
        $paidAmount += $amount;
    }

    if (!$normalizedPayments && num_value($data['paid_amount'] ?? 0) > 0) {
        $paidAmount = round(num_value($data['paid_amount'] ?? 0), 2);
        $normalizedPayments[] = ['account_id' => 1, 'account_name' => 'Cash', 'amount' => $paidAmount];
    }

    $paidAmount = round($paidAmount, 2);
    $dueAmount  = max(0, round($grandTotal - $paidAmount, 2));
    $status = 'due';
    if ($paidAmount > 0 && $dueAmount > 0)     $status = 'partial';
    if ($paidAmount > 0 && $dueAmount <= 0.01) { $status = 'paid'; $dueAmount = 0.0; }

    // ── barcode_sales (invoice) ──
    $saleId = insert_row($pdo, 'barcode_sales', [
        'invoice_no'      => $invoiceNo,
        'sale_date'       => $now,
        'customer_type'   => $customerType,
        'party_id'        => $partyId > 0 ? $partyId : null,
        'customer_name'   => $customerName,
        'customer_phone'  => $customerPhone,
        'previous_balance' => round($previousBalance, 2),
        'items_total'     => round($itemsTotal, 2),
        'making_charge'   => round($makingCharge, 2),
        'discount'        => round($discount, 2),
        'vat_percent'     => round($vatPercent, 2),
        'vat_amount'      => round($vatAmount, 2),
        'exchange_amount' => round($exchangeAmount, 2),
        'grand_total'     => round($grandTotal, 2),
        'paid_amount'     => round($paidAmount, 2),
        'due_amount'      => round($dueAmount, 2),
        'payment_json'    => json_encode($normalizedPayments, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        'notes'           => $notes,
        'status'          => $status,
        'created_at'      => $now,
        'updated_at'      => $now,
        'deleted_at'      => null,
    ]);

    // ── barcode_sale_items + barcode_stock update ──
    foreach ($items as $item) {
        insert_row($pdo, 'barcode_sale_items', [
            'sale_id'          => $saleId,
            'barcode_stock_id' => $item['barcode_stock_id'],
            'barcode'          => $item['barcode'],
            'item_name'        => $item['item_name'],
            'item_price'       => round(num_value($item['item_price']), 2),
            'karat'            => $item['karat'],
            'sale_rate_per_vori' => round(num_value($item['sale_rate_per_vori']), 2),
            'source'           => $item['source'],
            'source_json'      => json_encode($item['source_json'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'status'           => 'sold',
            'weight_g'         => round(num_value($item['weight_g']), 3),
            'weight_vori'      => round(num_value($item['weight_vori']), 5),
            'created_at'       => $now,
        ]);

        update_row_by_id($pdo, 'barcode_stock', (int)$item['barcode_stock_id'], [
            'status'          => 'sold',
            'sale_id'         => $saleId,
            'barcode_sale_id' => $saleId,
            'sold_sale_id'    => $saleId,
            'invoice_no'      => $invoiceNo,
            'sold_invoice_no' => $invoiceNo,
            'sold_at'         => $now,
            'updated_at'      => $now,
        ]);
    }

    // ── barcode_sale_payments ──
    foreach ($normalizedPayments as $payment) {
        insert_row($pdo, 'barcode_sale_payments', [
            'sale_id'      => $saleId,
            'invoice_no'   => $invoiceNo,
            'account_id'   => (int)$payment['account_id'],
            'account_name' => $payment['account_name'],
            'amount'       => round(num_value($payment['amount']), 2),
            'created_at'   => $now,
        ]);
    }

    // ── ক্যাশ IN — central helper দিয়ে (v2 ADD, 2026-06-01) ──
    // প্রতিটা পেমেন্টের জন্য cash_transactions-এ SALE_RECEIPT IN এন্ট্রি
    // try-catch: ক্যাশ এন্ট্রি ব্যর্থ হলেও বিক্রয় সেভ চালু থাকবে
    try {
        if (function_exists('gs_insert_cash_transaction') && !empty($normalizedPayments)) {
            $catStmt = $pdo->prepare(
                "SELECT id FROM cash_categories WHERE category_code = 'SALE_RECEIPT' LIMIT 1"
            );
            $catStmt->execute();
            $saleCatId = (int)$catStmt->fetchColumn();

            if ($saleCatId > 0) {
                $saleNote = 'বারকোড বিক্রয় - ' . $invoiceNo . ($customerName ? ' - ' . $customerName : '');
                $recalcIds = [];

                foreach ($normalizedPayments as $pm) {
                    $pmAmt = round(num_value($pm['amount']), 2);
                    if ($pmAmt <= 0) continue;

                    $pmAccId = (int)($pm['account_id'] ?? 1);
                    if ($pmAccId <= 0) $pmAccId = 1;

                    gs_insert_cash_transaction($pdo, [
                        'category_id'      => $saleCatId,           // IN — বিক্রির টাকা
                        'cash_account_id'  => $pmAccId,
                        'amount'           => $pmAmt,
                        'transaction_date' => date('Y-m-d'),
                        'note'             => $saleNote,
                        'reference_type'   => 'BARCODE_SALE',
                        'reference_id'     => $saleId,
                        'party_id'         => $partyId > 0 ? $partyId : null,
                        'created_by'       => $_SESSION['user_id'] ?? null
                    ]);

                    $recalcIds[] = $pmAccId;
                }

                if (function_exists('gs_recalc_cash_accounts') && !empty($recalcIds)) {
                    gs_recalc_cash_accounts($pdo, array_unique($recalcIds));
                }
            }
        }
    } catch (Throwable $cashEx) {
        // ক্যাশ এন্ট্রি ব্যর্থ হলেও বিক্রয় সেভ বন্ধ হবে না
        // পরবর্তী সংস্করণে এখানে error log যোগ করা হবে
    }

    $pdo->commit();

    out_json([
        'success'    => true,
        'message'    => 'বারকোড সেল সফলভাবে সেভ হয়েছে',
        'sale_id'    => $saleId,
        'invoice_no' => $invoiceNo,
        'status'     => $status,
        'summary'    => [
            'items_total'     => round($itemsTotal, 2),
            'making_charge'   => round($makingCharge, 2),
            'discount'        => round($discount, 2),
            'vat_percent'     => round($vatPercent, 2),
            'vat_amount'      => round($vatAmount, 2),
            'exchange_amount' => round($exchangeAmount, 2),
            'grand_total'     => round($grandTotal, 2),
            'paid_amount'     => round($paidAmount, 2),
            'due_amount'      => round($dueAmount, 2),
        ],
    ]);

} catch (Throwable $e) {
    if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    out_json(['success' => false, 'message' => 'Save failed: ' . $e->getMessage()], 500);
}
