<?php
/*
  cPanel path: /home/jewellersbd/public_html/erp.jewellersbd.com/api/new_stock/update_stock.php
  Purpose: New Stock / Old Purchase Stock update API
  Data change: YES

  Safety:
  - NO ALTER / CREATE / DROP
  - transaction + FOR UPDATE
  - already used pcs/weight preserve করবে
  - total pcs/weight used এর নিচে নামানো যাবে না
  - update_payment=true হলে old cash entry reverse + new cash OUT
  - update_payment=false হলে payment untouched
  - audit_log থাকলে entry করবে
*/

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

require_once __DIR__ . '/../../config/session.php';
require_once __DIR__ . '/../../config/db.php';

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

function nsu_out($code, $ok, $msg, $data = null) {
    http_response_code($code);
    echo json_encode([
        'success' => (bool)$ok,
        'status'  => (bool)$ok,
        'message' => $msg,
        'data'    => $data
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

if (function_exists('require_login')) {
    require_login();
} elseif (function_exists('is_logged_in') && !is_logged_in()) {
    nsu_out(401, false, 'লগইন প্রয়োজন');
} elseif (empty($_SESSION['user_id']) && empty($_SESSION['user']['id'])) {
    nsu_out(401, false, 'লগইন প্রয়োজন');
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    nsu_out(405, false, 'শুধু POST অনুমোদিত');
}

if (!isset($pdo) || !($pdo instanceof PDO)) {
    nsu_out(500, false, 'PDO DB connection পাওয়া যায়নি');
}

try {
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->exec("SET NAMES utf8mb4");
} catch (Throwable $e) {
    nsu_out(500, false, 'ডাটাবেজ কানেকশন সমস্যা');
}

function nsu_clean($name) {
    return preg_replace('/[^a-zA-Z0-9_]/', '', (string)$name);
}

function nsu_qi($name) {
    return '`' . str_replace('`', '', nsu_clean($name)) . '`';
}

function nsu_table_exists(PDO $pdo, $table) {
    $table = nsu_clean($table);
    if ($table === '') return false;

    $st = $pdo->query("SHOW TABLES LIKE " . $pdo->quote($table));
    return (bool)$st->fetchColumn();
}

function nsu_get_columns(PDO $pdo, $table) {
    $table = nsu_clean($table);
    if ($table === '' || !nsu_table_exists($pdo, $table)) return [];

    $rows = $pdo->query("SHOW COLUMNS FROM " . nsu_qi($table))->fetchAll(PDO::FETCH_ASSOC);
    $cols = [];

    foreach ($rows as $r) {
        if (isset($r['Field'])) {
            $cols[$r['Field']] = true;
        }
    }

    return $cols;
}

function nsu_pick_col($cols, $names) {
    foreach ($names as $n) {
        if (isset($cols[$n])) return $n;
    }
    return null;
}

function nsu_first_value($arr, $keys, $default = '') {
    foreach ($keys as $k) {
        if (array_key_exists($k, $arr) && $arr[$k] !== null && $arr[$k] !== '') {
            return $arr[$k];
        }
    }
    return $default;
}

function nsu_first_float($arr, $keys, $default = 0) {
    foreach ($keys as $k) {
        if (array_key_exists($k, $arr) && $arr[$k] !== null && $arr[$k] !== '') {
            return (float)$arr[$k];
        }
    }
    return (float)$default;
}

function nsu_first_int($arr, $keys, $default = 0) {
    foreach ($keys as $k) {
        if (array_key_exists($k, $arr) && $arr[$k] !== null && $arr[$k] !== '') {
            return (int)$arr[$k];
        }
    }
    return (int)$default;
}

function nsu_user_id() {
    foreach (['user_id', 'id', 'auth_user_id'] as $k) {
        if (isset($_SESSION[$k]) && (int)$_SESSION[$k] > 0) return (int)$_SESSION[$k];
    }

    if (isset($_SESSION['user']) && is_array($_SESSION['user'])) {
        foreach (['id', 'user_id'] as $k) {
            if (isset($_SESSION['user'][$k]) && (int)$_SESSION['user'][$k] > 0) return (int)$_SESSION['user'][$k];
        }
    }

    return 0;
}

function nsu_client_ip() {
    foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'] as $k) {
        if (!empty($_SERVER[$k])) {
            $v = trim((string)$_SERVER[$k]);
            if ($k === 'HTTP_X_FORWARDED_FOR') {
                $parts = explode(',', $v);
                return trim($parts[0]);
            }
            return $v;
        }
    }
    return '';
}

function nsu_valid_date($d) {
    return is_string($d) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $d);
}

function nsu_gram_from_parts($vori, $ana, $roti, $point) {
    $VORI = 11.664;

    return round(
        ((float)$vori * $VORI) +
        ((float)$ana * ($VORI / 16)) +
        ((float)$roti * ($VORI / 96)) +
        ((float)$point * ($VORI / 960)),
        3
    );
}

function nsu_gram_to_parts($gram) {
    $VORI = 11.664;
    $totalPoint = (int)round(((float)$gram / $VORI) * 960);

    $vori = intdiv($totalPoint, 960);
    $totalPoint %= 960;

    $ana = intdiv($totalPoint, 60);
    $totalPoint %= 60;

    $roti = intdiv($totalPoint, 10);
    $point = $totalPoint % 10;

    return [
        'vori'  => $vori,
        'ana'   => $ana,
        'roti'  => $roti,
        'rati'  => $roti,
        'point' => $point
    ];
}

function nsu_purity_percent($karat, $fallback = 91.667) {
    $k = strtoupper(trim((string)$karat));

    $map = [
        '24K' => 100,
        '22K' => 91.667,
        '21K' => 87.5,
        '18K' => 75
    ];

    return $map[$k] ?? $fallback;
}

function nsu_json_text($value) {
    return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

function nsu_audit(PDO $pdo, $tableName, $recordId, $action, $userId, $oldValues, $newValues, $note) {
    try {
        if (!nsu_table_exists($pdo, 'audit_log')) return;

        $cols = nsu_get_columns($pdo, 'audit_log');
        if (empty($cols)) return;

        $fields = [];
        $marks = [];
        $params = [];

        $add = function($col, $val) use (&$fields, &$marks, &$params, $cols) {
            if (isset($cols[$col])) {
                $fields[] = nsu_qi($col);
                $marks[] = '?';
                $params[] = $val;
            }
        };

        $add('table_name', $tableName);
        $add('record_id', (int)$recordId);
        $add('action', strtoupper((string)$action));

        if (isset($cols['changed_by'])) {
            $add('changed_by', (int)$userId);
        } elseif (isset($cols['user_id'])) {
            $add('user_id', (int)$userId);
        }

        if (isset($cols['old_values'])) {
            $add('old_values', nsu_json_text($oldValues));
        }

        if (isset($cols['new_values'])) {
            $add('new_values', nsu_json_text($newValues));
        }

        $add('ip_address', nsu_client_ip());
        $add('note', $note);

        if (isset($cols['changed_at'])) {
            $fields[] = nsu_qi('changed_at');
            $marks[] = 'NOW()';
        } elseif (isset($cols['created_at'])) {
            $fields[] = nsu_qi('created_at');
            $marks[] = 'NOW()';
        }

        if (empty($fields)) return;

        $sql = "INSERT INTO `audit_log` (" . implode(',', $fields) . ") VALUES (" . implode(',', $marks) . ")";
        $st = $pdo->prepare($sql);
        $st->execute($params);

    } catch (Throwable $e) {
        // audit fail হলেও main update fail হবে না
    }
}

function nsu_cash_account_col($cols) {
    if (isset($cols['cash_account_id'])) return 'cash_account_id';
    if (isset($cols['account_id'])) return 'account_id';
    return null;
}

function nsu_cash_direction($row) {
    foreach (['direction', 'transaction_direction', 'transaction_type'] as $c) {
        if (isset($row[$c]) && trim((string)$row[$c]) !== '') {
            $v = strtoupper(trim((string)$row[$c]));
            if ($v === 'OUT' || $v === 'CASH_OUT') return 'OUT';
            if ($v === 'IN' || $v === 'CASH_IN') return 'IN';
        }
    }

    if (isset($row['type'])) {
        $v = strtolower(trim((string)$row['type']));
        if ($v === 'cash_out' || $v === 'out') return 'OUT';
        if ($v === 'cash_in' || $v === 'in') return 'IN';
    }

    return 'OUT';
}

function nsu_update_cash_balance(PDO $pdo, $cashAccountId, $direction, $amount) {
    if ($cashAccountId <= 0 || $amount <= 0) return;

    if (!nsu_table_exists($pdo, 'cash_accounts')) return;

    $cols = nsu_get_columns($pdo, 'cash_accounts');
    $balanceCol = nsu_pick_col($cols, ['current_balance', 'balance']);
    if (!$balanceCol || !isset($cols['id'])) return;

    $sign = strtoupper($direction) === 'IN' ? 1 : -1;

    $st = $pdo->prepare("
        UPDATE `cash_accounts`
        SET " . nsu_qi($balanceCol) . " = COALESCE(" . nsu_qi($balanceCol) . ",0) + ?
        WHERE `id` = ?
    ");
    $st->execute([$sign * (float)$amount, $cashAccountId]);
}

function nsu_find_cash_category_id(PDO $pdo) {
    if (!nsu_table_exists($pdo, 'cash_categories')) return 0;

    $cols = nsu_get_columns($pdo, 'cash_categories');
    if (!isset($cols['id'])) return 0;

    $parts = [];
    $params = [];

    if (isset($cols['category_code'])) {
        $parts[] = "`category_code` = ?";
        $params[] = 'PURCHASE_PAYMENT';
    }

    if (isset($cols['code'])) {
        $parts[] = "`code` = ?";
        $params[] = 'PURCHASE_PAYMENT';
    }

    if (isset($cols['name'])) {
        $parts[] = "`name` LIKE ?";
        $params[] = '%purchase%';
    }

    if (empty($parts)) return 0;

    $sql = "SELECT `id` FROM `cash_categories` WHERE " . implode(' OR ', $parts) . " LIMIT 1";
    $st = $pdo->prepare($sql);
    $st->execute($params);

    return (int)$st->fetchColumn();
}

function nsu_insert_cash_transaction(PDO $pdo, $cashAccountId, $amount, $date, $note, $referenceType, $referenceId, $userId, $direction = 'OUT', $isReversal = 0, $reversedTransactionId = null, $paymentMethodId = null) {
    if ($cashAccountId <= 0 || $amount <= 0) {
        return 0;
    }

    if (!nsu_table_exists($pdo, 'cash_transactions')) {
        throw new RuntimeException('cash_transactions table পাওয়া যায়নি');
    }

    $ctCols = nsu_get_columns($pdo, 'cash_transactions');
    $catId = nsu_find_cash_category_id($pdo);

    $fields = [];
    $marks = [];
    $params = [];

    $add = function($col, $val) use (&$fields, &$marks, &$params, $ctCols) {
        if (isset($ctCols[$col])) {
            $fields[] = nsu_qi($col);
            $marks[] = '?';
            $params[] = $val;
        }
    };

    $addRaw = function($col, $expr) use (&$fields, &$marks, $ctCols) {
        if (isset($ctCols[$col])) {
            $fields[] = nsu_qi($col);
            $marks[] = $expr;
        }
    };

    if (isset($ctCols['cash_account_id'])) {
        $add('cash_account_id', $cashAccountId);
    } elseif (isset($ctCols['account_id'])) {
        $add('account_id', $cashAccountId);
    }

    if (isset($ctCols['category_id'])) {
        if ($catId <= 0) {
            throw new RuntimeException('PURCHASE_PAYMENT cash category পাওয়া যায়নি');
        }
        $add('category_id', $catId);
    }

    if (isset($ctCols['payment_method_id'])) {
        $add('payment_method_id', $paymentMethodId ?: null);
    }

    if (isset($ctCols['transaction_type_id'])) {
        $add('transaction_type_id', 1);
    }

    $direction = strtoupper($direction) === 'IN' ? 'IN' : 'OUT';
    $type = $direction === 'IN' ? 'cash_in' : 'cash_out';

    $add('direction', $direction);
    $add('transaction_direction', $direction);
    $add('transaction_type', $direction);
    $add('type', $type);

    $add('amount', round((float)$amount, 2));

    if (isset($ctCols['transaction_date'])) {
        $add('transaction_date', $date);
    } elseif (isset($ctCols['date'])) {
        $add('date', $date);
    }

    $add('note', $note);
    $add('purpose', $note);
    $add('description', $note);
    $add('remarks', $note);

    $add('reference_type', $referenceType);
    $add('ref_type', $referenceType);
    $add('source_type', $referenceType);

    $add('reference_id', $referenceId);
    $add('ref_id', $referenceId);
    $add('source_id', $referenceId);

    $add('created_by', $userId);
    $add('user_id', $userId);

    if (isset($ctCols['is_reversal'])) {
        $add('is_reversal', (int)$isReversal);
    }

    if ($reversedTransactionId !== null) {
        $add('reversed_transaction_id', (int)$reversedTransactionId);
    }

    if (isset($ctCols['is_deleted'])) {
        $add('is_deleted', 0);
    }

    $addRaw('created_at', 'NOW()');
    $addRaw('updated_at', 'NOW()');

    if (empty($fields)) {
        throw new RuntimeException('cash_transactions insert করার মতো column পাওয়া যায়নি');
    }

    $sql = "INSERT INTO `cash_transactions` (" . implode(',', $fields) . ") VALUES (" . implode(',', $marks) . ")";
    $st = $pdo->prepare($sql);
    $st->execute($params);

    $txId = (int)$pdo->lastInsertId();

    nsu_update_cash_balance($pdo, $cashAccountId, $direction, $amount);

    return $txId;
}

function nsu_reverse_old_cash(PDO $pdo, $stockId, $date, $userId) {
    if (!nsu_table_exists($pdo, 'cash_transactions')) {
        return [];
    }

    $ctCols = nsu_get_columns($pdo, 'cash_transactions');
    $accountCol = nsu_cash_account_col($ctCols);
    if (!$accountCol || !isset($ctCols['id']) || !isset($ctCols['amount'])) {
        return [];
    }

    $where = [];
    $params = [];

    $where[] = "1=1";

    if (isset($ctCols['reference_type']) && isset($ctCols['reference_id'])) {
        $where[] = "`reference_type` = 'new_stock'";
        $where[] = "`reference_id` = ?";
        $params[] = $stockId;
    } elseif (isset($ctCols['ref_type']) && isset($ctCols['ref_id'])) {
        $where[] = "`ref_type` = 'new_stock'";
        $where[] = "`ref_id` = ?";
        $params[] = $stockId;
    } elseif (isset($ctCols['source_type']) && isset($ctCols['source_id'])) {
        $where[] = "`source_type` = 'new_stock'";
        $where[] = "`source_id` = ?";
        $params[] = $stockId;
    } else {
        return [];
    }

    if (isset($ctCols['is_reversal'])) {
        $where[] = "COALESCE(`is_reversal`,0) = 0";
    }

    if (isset($ctCols['is_deleted'])) {
        $where[] = "COALESCE(`is_deleted`,0) = 0";
    }

    if (isset($ctCols['deleted_at'])) {
        $where[] = "`deleted_at` IS NULL";
    }

    $sql = "
        SELECT *
        FROM `cash_transactions`
        WHERE " . implode(' AND ', $where) . "
        ORDER BY `id` DESC
        LIMIT 5
        FOR UPDATE
    ";

    $st = $pdo->prepare($sql);
    $st->execute($params);
    $rows = $st->fetchAll(PDO::FETCH_ASSOC);

    $reversed = [];

    foreach ($rows as $old) {
        $oldId = (int)$old['id'];
        $amount = (float)$old['amount'];
        $accId = (int)$old[$accountCol];

        if ($amount <= 0 || $accId <= 0) continue;

        $oldDirection = nsu_cash_direction($old);
        $reverseDirection = $oldDirection === 'OUT' ? 'IN' : 'OUT';

        $revId = nsu_insert_cash_transaction(
            $pdo,
            $accId,
            $amount,
            $date,
            'স্টক এডিট - পুরনো লেনদেন রিভার্স',
            'new_stock_reverse',
            $oldId,
            $userId,
            $reverseDirection,
            1,
            $oldId,
            isset($old['payment_method_id']) ? (int)$old['payment_method_id'] : null
        );

        $reversed[] = [
            'old_transaction_id' => $oldId,
            'reverse_transaction_id' => $revId,
            'cash_account_id' => $accId,
            'amount' => round($amount, 2),
            'old_direction' => $oldDirection,
            'reverse_direction' => $reverseDirection
        ];
    }

    return $reversed;
}

try {
    $raw = json_decode(file_get_contents('php://input'), true);
    if (!is_array($raw)) {
        $raw = $_POST;
    }

    if (!is_array($raw)) {
        nsu_out(400, false, 'ভুল JSON data');
    }

    $userId = nsu_user_id();
    if ($userId <= 0) {
        nsu_out(401, false, 'Login session পাওয়া যায়নি');
    }

    if (!nsu_table_exists($pdo, 'new_stock')) {
        nsu_out(500, false, 'new_stock table পাওয়া যায়নি');
    }

    $cols = nsu_get_columns($pdo, 'new_stock');
    $idCol = nsu_pick_col($cols, ['id', 'stock_id', 'new_stock_id']);

    if (!$idCol) {
        nsu_out(500, false, 'new_stock ID column পাওয়া যায়নি');
    }

    $id = nsu_first_int($raw, ['id', 'stock_id', 'new_stock_id'], 0);
    if ($id <= 0) {
        nsu_out(400, false, 'ID লাগবে');
    }

    $stockDate = trim((string)nsu_first_value($raw, ['stock_date', 'purchase_date', 'date'], date('Y-m-d')));
    if (!nsu_valid_date($stockDate)) {
        nsu_out(400, false, 'সঠিক তারিখ দিন');
    }

    $itemName = trim((string)nsu_first_value($raw, ['item_name', 'product_name', 'name', 'category'], ''));
    if ($itemName === '') {
        nsu_out(400, false, 'আইটেমের নাম দিন');
    }
    if (mb_strlen($itemName, 'UTF-8') > 255) {
        nsu_out(400, false, 'আইটেমের নাম 255 অক্ষরের বেশি হতে পারবে না');
    }

    $category = trim((string)nsu_first_value($raw, ['category'], $itemName));
    if ($category === '') $category = $itemName;

    $invoiceNo = trim((string)nsu_first_value($raw, ['invoice_no', 'invoice', 'voucher_no'], ''));

    $supplierId = nsu_first_int($raw, ['supplier_id', 'party_id'], 0);
    $karat = trim((string)nsu_first_value($raw, ['karat', 'purity'], '22K'));

    $totalPcs = nsu_first_int($raw, ['total_pcs', 'total_pieces', 'pcs', 'quantity', 'qty'], 1);
    if ($totalPcs <= 0) $totalPcs = 1;
    if ($totalPcs > 100000) {
        nsu_out(400, false, 'পিস সংখ্যা অস্বাভাবিক বড়');
    }

    $vori  = nsu_first_float($raw, ['vori', 'bhori'], 0);
    $ana   = nsu_first_float($raw, ['ana', 'aana'], 0);
    $roti  = nsu_first_float($raw, ['roti', 'rati'], 0);
    $point = nsu_first_float($raw, ['point', 'points'], 0);

    $totalWeight = nsu_first_float($raw, [
        'total_weight_gram',
        'gross_weight_gram',
        'weight_gram',
        'weight'
    ], 0);

    if ($totalWeight <= 0) {
        $totalWeight = nsu_gram_from_parts($vori, $ana, $roti, $point);
    }

    if ($totalWeight <= 0) {
        nsu_out(400, false, 'ওজন দিন');
    }

    if ($totalWeight > 100000) {
        nsu_out(400, false, 'ওজন অস্বাভাবিক বড়, আবার চেক করুন');
    }

    if ($vori <= 0 && $ana <= 0 && $roti <= 0 && $point <= 0) {
        $parts = nsu_gram_to_parts($totalWeight);
        $vori  = $parts['vori'];
        $ana   = $parts['ana'];
        $roti  = $parts['roti'];
        $point = $parts['point'];
    }

    $purityPercent = nsu_first_float($raw, ['purity_percent', 'actual_purity', 'system_purity'], nsu_purity_percent($karat));
    if ($purityPercent <= 0) {
        $purityPercent = nsu_purity_percent($karat);
    }
    if ($purityPercent > 100) {
        nsu_out(400, false, 'Purity 100% এর বেশি হতে পারবে না');
    }

    $pureWeight = nsu_first_float($raw, ['pure_weight_gram', 'pure_weight', 'pure_gold_gram'], 0);
    if ($pureWeight <= 0) {
        $pureWeight = round($totalWeight * ($purityPercent / 100), 3);
    }

    $locationId = nsu_first_int($raw, ['location_id', 'stock_location_id'], 1);
    if ($locationId <= 0) $locationId = 1;

    $ratePerVori = nsu_first_float($raw, ['rate_per_vori'], 0);
    $totalAmount = nsu_first_float($raw, ['total_amount', 'amount'], 0);

    $paymentMethodId = nsu_first_int($raw, ['payment_method_id', 'payment_id'], 0);
    $cashAccountId   = nsu_first_int($raw, ['cash_account_id', 'account_id'], 0);
    $updatePayment   = !empty($raw['update_payment']);

    $note = trim((string)nsu_first_value($raw, ['note', 'remarks'], ''));
    if (mb_strlen($note, 'UTF-8') > 2000) {
        nsu_out(400, false, 'নোট 2000 অক্ষরের বেশি হতে পারবে না');
    }

    if ($supplierId > 0 && nsu_table_exists($pdo, 'parties')) {
        $partyCols = nsu_get_columns($pdo, 'parties');
        if (isset($partyCols['id'])) {
            $pq = $pdo->prepare("SELECT COUNT(*) FROM `parties` WHERE `id` = ?");
            $pq->execute([$supplierId]);
            if ((int)$pq->fetchColumn() !== 1) {
                nsu_out(400, false, 'Supplier/Party পাওয়া যায়নি');
            }
        }
    }

    if ($locationId > 0 && nsu_table_exists($pdo, 'stock_locations')) {
        $locCols = nsu_get_columns($pdo, 'stock_locations');
        if (isset($locCols['id'])) {
            $locWhere = "`id` = ?";
            if (isset($locCols['is_active'])) {
                $locWhere .= " AND (`is_active` IS NULL OR `is_active` = 1)";
            }
            $lq = $pdo->prepare("SELECT COUNT(*) FROM `stock_locations` WHERE {$locWhere}");
            $lq->execute([$locationId]);
            if ((int)$lq->fetchColumn() !== 1) {
                nsu_out(400, false, 'সঠিক active location বাছাই করুন');
            }
        }
    }

    if ($updatePayment && $cashAccountId > 0 && $totalAmount > 0 && nsu_table_exists($pdo, 'cash_accounts')) {
        $accCols = nsu_get_columns($pdo, 'cash_accounts');
        if (isset($accCols['id'])) {
            $accWhere = "`id` = ?";
            if (isset($accCols['is_active'])) {
                $accWhere .= " AND (`is_active` IS NULL OR `is_active` = 1)";
            }
            if (isset($accCols['deleted_at'])) {
                $accWhere .= " AND `deleted_at` IS NULL";
            }

            $aq = $pdo->prepare("SELECT COUNT(*) FROM `cash_accounts` WHERE {$accWhere}");
            $aq->execute([$cashAccountId]);

            if ((int)$aq->fetchColumn() !== 1) {
                nsu_out(400, false, 'সঠিক active cash account বাছাই করুন');
            }
        }
    }

    $pdo->beginTransaction();

    $oldQ = $pdo->prepare("
        SELECT *
        FROM `new_stock`
        WHERE " . nsu_qi($idCol) . " = ?
        " . (isset($cols['deleted_at']) ? " AND `deleted_at` IS NULL" : "") . "
        " . (isset($cols['is_deleted']) ? " AND (`is_deleted` IS NULL OR `is_deleted` = 0)" : "") . "
        LIMIT 1
        FOR UPDATE
    ");
    $oldQ->execute([$id]);
    $old = $oldQ->fetch(PDO::FETCH_ASSOC);

    if (!$old) {
        $pdo->rollBack();
        nsu_out(404, false, 'স্টক পাওয়া যায়নি');
    }

    $oldTotalPcs = nsu_first_int($old, ['total_pcs', 'pcs', 'quantity', 'qty'], 0);
    $oldAvailablePcs = nsu_first_int($old, ['available_pcs', 'balance_pcs'], $oldTotalPcs);

    $oldTotalWeight = nsu_first_float($old, ['total_weight_gram', 'gross_weight_gram', 'weight_gram', 'weight'], 0);
    $oldAvailableWeight = nsu_first_float($old, ['available_weight_gram', 'balance_weight_gram'], $oldTotalWeight);

    $usedPcs = max(0, $oldTotalPcs - $oldAvailablePcs);
    $usedWeight = max(0, $oldTotalWeight - $oldAvailableWeight);

    if ($totalPcs < $usedPcs) {
        $pdo->rollBack();
        nsu_out(400, false, "পিস কমানো যাবে না — ইতিমধ্যে {$usedPcs} পিস ব্যবহৃত");
    }

    if ($totalWeight + 0.0001 < $usedWeight) {
        $pdo->rollBack();
        nsu_out(400, false, "ওজন কমানো যাবে না — ইতিমধ্যে {$usedWeight}g ব্যবহৃত");
    }

    $newAvailablePcs = max(0, $totalPcs - $usedPcs);
    $newAvailableWeight = max(0, round($totalWeight - $usedWeight, 3));

    $set = [];
    $params = [];

    $add = function($col, $val) use (&$set, &$params, $cols) {
        if (isset($cols[$col])) {
            $set[] = nsu_qi($col) . " = ?";
            $params[] = $val;
        }
    };

    $addRaw = function($col, $expr) use (&$set, $cols) {
        if (isset($cols[$col])) {
            $set[] = nsu_qi($col) . " = " . $expr;
        }
    };

    $add('stock_date', $stockDate);
    $add('purchase_date', $stockDate);
    $add('date', $stockDate);

    $add('supplier_id', $supplierId > 0 ? $supplierId : null);
    $add('party_id', $supplierId > 0 ? $supplierId : null);

    $add('invoice_no', $invoiceNo !== '' ? $invoiceNo : null);
    $add('invoice', $invoiceNo !== '' ? $invoiceNo : null);
    $add('voucher_no', $invoiceNo !== '' ? $invoiceNo : null);

    $add('category', $category);
    $add('stock_type', $category);

    $add('product_name', $itemName);
    $add('item_name', $itemName);
    $add('name', $itemName);

    $add('karat', $karat);
    $add('purity', $karat);
    $add('purity_percent', round($purityPercent, 3));

    $add('total_pcs', $totalPcs);
    $add('total_pieces', $totalPcs);
    $add('pcs', $totalPcs);
    $add('quantity', $totalPcs);
    $add('qty', $totalPcs);

    $add('available_pcs', $newAvailablePcs);
    $add('balance_pcs', $newAvailablePcs);

    $add('vori', $vori);
    $add('bhori', $vori);
    $add('ana', $ana);
    $add('aana', $ana);
    $add('roti', $roti);
    $add('rati', $roti);
    $add('point', $point);
    $add('points', $point);

    $add('total_weight_gram', round($totalWeight, 3));
    $add('gross_weight_gram', round($totalWeight, 3));
    $add('weight_gram', round($totalWeight, 3));
    $add('weight', round($totalWeight, 3));

    $add('available_weight_gram', round($newAvailableWeight, 3));
    $add('balance_weight_gram', round($newAvailableWeight, 3));

    $add('pure_weight_gram', round($pureWeight, 3));
    $add('pure_weight', round($pureWeight, 3));
    $add('pure_gold_gram', round($pureWeight, 3));

    $add('rate_per_vori', $ratePerVori > 0 ? $ratePerVori : null);
    $add('total_amount', $totalAmount > 0 ? $totalAmount : null);

    if ($updatePayment) {
        $add('payment_method_id', $paymentMethodId > 0 ? $paymentMethodId : null);
        $add('payment_id', $paymentMethodId > 0 ? $paymentMethodId : null);
        $add('cash_account_id', $cashAccountId > 0 ? $cashAccountId : null);
        $add('account_id', $cashAccountId > 0 ? $cashAccountId : null);
    }

    $add('location_id', $locationId);
    $add('stock_location_id', $locationId);

    $add('note', $note !== '' ? $note : null);
    $add('remarks', $note !== '' ? $note : null);

    $addRaw('updated_at', 'NOW()');

    if (empty($set)) {
        throw new RuntimeException('new_stock update করার মতো column পাওয়া যায়নি');
    }

    $params[] = $id;

    $sql = "
        UPDATE `new_stock`
        SET " . implode(', ', $set) . "
        WHERE " . nsu_qi($idCol) . " = ?
    ";

    $st = $pdo->prepare($sql);
    $st->execute($params);

    $cashInfo = [
        'payment_updated' => $updatePayment,
        'reversed' => [],
        'new_cash_transaction_id' => 0
    ];

    if ($updatePayment) {
        $cashInfo['reversed'] = nsu_reverse_old_cash($pdo, $id, $stockDate, $userId);

        if ($cashAccountId > 0 && $totalAmount > 0) {
            $cashInfo['new_cash_transaction_id'] = nsu_insert_cash_transaction(
                $pdo,
                $cashAccountId,
                $totalAmount,
                $stockDate,
                'স্টক এডিট: ' . $itemName,
                'new_stock',
                $id,
                $userId,
                'OUT',
                0,
                null,
                $paymentMethodId > 0 ? $paymentMethodId : null
            );
        }

        if (function_exists('gs_recalc_cash_account')) {
            $accountIds = [];

            foreach ($cashInfo['reversed'] as $r) {
                if (!empty($r['cash_account_id'])) $accountIds[] = (int)$r['cash_account_id'];
            }

            if ($cashAccountId > 0) $accountIds[] = $cashAccountId;
            $accountIds = array_values(array_unique(array_filter($accountIds)));

            foreach ($accountIds as $accId) {
                gs_recalc_cash_account($pdo, $accId);
            }
        } elseif (function_exists('gs_recalc_cash_accounts')) {
            $accountIds = [];

            foreach ($cashInfo['reversed'] as $r) {
                if (!empty($r['cash_account_id'])) $accountIds[] = (int)$r['cash_account_id'];
            }

            if ($cashAccountId > 0) $accountIds[] = $cashAccountId;

            gs_recalc_cash_accounts($pdo, array_values(array_unique(array_filter($accountIds))));
        }
    }

    nsu_audit(
        $pdo,
        'new_stock',
        $id,
        'UPDATE',
        $userId,
        [
            'id' => $id,
            'stock_date' => nsu_first_value($old, ['stock_date', 'purchase_date', 'date'], ''),
            'item_name' => nsu_first_value($old, ['product_name', 'item_name', 'name', 'category'], ''),
            'karat' => nsu_first_value($old, ['karat', 'purity'], ''),
            'total_pcs' => $oldTotalPcs,
            'available_pcs' => $oldAvailablePcs,
            'total_weight_gram' => $oldTotalWeight,
            'available_weight_gram' => $oldAvailableWeight,
            'location_id' => nsu_first_int($old, ['location_id', 'stock_location_id'], 0),
            'total_amount' => nsu_first_float($old, ['total_amount', 'amount'], 0)
        ],
        [
            'id' => $id,
            'stock_date' => $stockDate,
            'item_name' => $itemName,
            'karat' => $karat,
            'total_pcs' => $totalPcs,
            'available_pcs' => $newAvailablePcs,
            'total_weight_gram' => round($totalWeight, 3),
            'available_weight_gram' => round($newAvailableWeight, 3),
            'location_id' => $locationId,
            'total_amount' => $totalAmount,
            'payment_updated' => $updatePayment
        ],
        'New stock updated | ' . $itemName
    );

    $pdo->commit();

    nsu_out(200, true, '✅ স্টক সফলভাবে আপডেট হয়েছে', [
        'id' => $id,
        'stock_id' => $id,
        'stock_date' => $stockDate,
        'item_name' => $itemName,
        'product_name' => $itemName,
        'category' => $category,
        'karat' => $karat,
        'total_pcs' => $totalPcs,
        'available_pcs' => $newAvailablePcs,
        'used_pcs' => $usedPcs,
        'total_weight_gram' => round($totalWeight, 3),
        'available_weight_gram' => round($newAvailableWeight, 3),
        'used_weight_gram' => round($usedWeight, 3),
        'pure_weight_gram' => round($pureWeight, 3),
        'location_id' => $locationId,
        'total_amount' => $totalAmount > 0 ? $totalAmount : null,
        'payment_updated' => $updatePayment,
        'cash' => $cashInfo
    ]);

} catch (Throwable $e) {
    if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
        $pdo->rollBack();
    }

    nsu_out(500, false, 'সার্ভার সমস্যা: ' . $e->getMessage());
}