<?php
/* =====================================================
   File: /home/jewellersbd/public_html/erp.jewellersbd.com/api/purchases/add_purchase.php
   Module: নতুন ক্রয়
   v3 (2026-06-05):
   - paid_amount_override support (partial payment)
   - due_amount + payment_status auto-calculate
   ===================================================== */

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

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

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

if (function_exists('is_logged_in') && !is_logged_in()) {
    out_json(401, false, 'লগইন প্রয়োজন', null);
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    out_json(405, false, 'শুধু POST অনুরোধ গ্রহণযোগ্য', null);
}

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

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec("SET NAMES utf8mb4");

$raw  = file_get_contents('php://input');
$data = json_decode($raw, true);

if (!is_array($data)) {
    out_json(400, false, 'Invalid JSON data', null);
}

/* ── Helpers ── */

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

function table_exists(PDO $pdo, $table) {
    $table = clean_identifier($table);
    $st = $pdo->query("SHOW TABLES LIKE " . $pdo->quote($table));
    return (bool)$st->fetchColumn();
}

function column_exists(PDO $pdo, $table, $column) {
    $table  = clean_identifier($table);
    $column = clean_identifier($column);
    $st = $pdo->query("SHOW COLUMNS FROM `{$table}` LIKE " . $pdo->quote($column));
    return (bool)$st->fetch(PDO::FETCH_ASSOC);
}

function ensure_column(PDO $pdo, $table, $column, $definition) {
    if (!column_exists($pdo, $table, $column)) {
        $table  = clean_identifier($table);
        $column = clean_identifier($column);
        $pdo->exec("ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}");
    }
}

function get_columns(PDO $pdo, $table) {
    $table = clean_identifier($table);
    $rows  = $pdo->query("SHOW COLUMNS FROM `{$table}`")->fetchAll(PDO::FETCH_ASSOC);
    $cols  = [];
    foreach ($rows as $r) { $cols[$r['Field']] = true; }
    return $cols;
}

function first_value($data, $keys, $default = null) {
    foreach ($keys as $k) {
        if (isset($data[$k]) && $data[$k] !== '' && $data[$k] !== null) return $data[$k];
    }
    return $default;
}

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

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

function add_insert(&$cols, &$marks, &$params, $dbCols, $col, $value) {
    if (isset($dbCols[$col])) {
        $cols[]   = "`{$col}`";
        $marks[]  = "?";
        $params[] = $value;
    }
}

function 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,'point'=>$point];
}

/* ── Input ── */

$purchase_date = trim((string)first_value($data, ['purchase_date','date'], date('Y-m-d')));
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $purchase_date)) $purchase_date = date('Y-m-d');

$item_name = trim((string)first_value($data, ['item_name','name','product_name','item'], ''));
if ($item_name === '') $item_name = 'আইটেম নাম নেই';

$party_id    = first_int($data, ['party_id','supplier_id','customer_id'], 0);
$party_id    = $party_id > 0 ? $party_id : null;

$location_id = first_int($data, ['location_id','stock_location_id'], 0);

$karat = trim((string)first_value($data, ['karat','purity','system_purity_label'], '22K'));
if ($karat === '') $karat = '22K';

$karatMap = ['24K'=>100,'22K'=>91.667,'21K'=>87.5,'18K'=>75,'other'=>91.667];
$defaultPurity = $karatMap[$karat] ?? 91.667;

$system_purity = first_float($data, ['system_purity','system_purity_percent','purity_percent','karat_ratio'], $defaultPurity);
$actual_purity = first_float($data, ['actual_purity','actual_purity_percent'], $system_purity);
$deduction     = first_float($data, ['deduction','purity_deduction'], 0);

$gross_weight = first_float($data, ['gross_weight','gross_weight_gram','total_weight_gram','weight_gram','net_weight','net_weight_gram'], 0);

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

if (($vori > 0 || $ana > 0 || $roti > 0 || $point > 0) && $gross_weight <= 0) {
    $gross_weight = round(
        ($vori * 11.664) + ($ana * (11.664/16)) + ($roti * (11.664/96)) + ($point * (11.664/960)),
        3
    );
}

if ($gross_weight > 0 && $vori <= 0 && $ana <= 0 && $roti <= 0 && $point <= 0) {
    $parts = gram_to_parts($gross_weight);
    $vori = $parts['vori']; $ana = $parts['ana']; $roti = $parts['roti']; $point = $parts['point'];
}

$total_pieces      = first_int($data,   ['total_pieces','pieces','qty','pcs','piece_count','total_piece','total_pcs','quantity'], 0);
$payment_method_id = first_int($data,   ['payment_method_id','payment_method'], 1);
$cash_account_id   = first_int($data,   ['cash_account_id','account_id'], 0);
$cash_account_id   = $cash_account_id > 0 ? $cash_account_id : null;
$note              = trim((string)first_value($data, ['note','remarks'], ''));

$rate_per_vori_24k = first_float($data, ['rate_per_vori_24k','rate_per_vori','purchase_rate_24k','gold_rate_24k','gold_rate','rate_24k','rate24k','rate'], 0);
$rate_per_gram_24k = first_float($data, ['rate_per_gram_24k','rate_per_gram'], 0);

if ($rate_per_gram_24k > 0 && $rate_per_vori_24k <= 0) {
    $rate_per_vori_24k = round($rate_per_gram_24k * 11.664, 2);
}
if ($rate_per_vori_24k > 0 && $rate_per_gram_24k <= 0) {
    if ($rate_per_vori_24k < 50000) {
        $rate_per_gram_24k = $rate_per_vori_24k;
        $rate_per_vori_24k = round($rate_per_gram_24k * 11.664, 2);
    } else {
        $rate_per_gram_24k = round($rate_per_vori_24k / 11.664, 6);
    }
}

/* ── Validation ── */

if ($location_id <= 0)          out_json(400, false, 'লোকেশন সিলেক্ট করুন', null);
if ($gross_weight <= 0)         out_json(400, false, 'সঠিক ওজন দিন', null);
if ($total_pieces <= 0)         out_json(400, false, 'সঠিক পিস সংখ্যা দিন', null);
if ($rate_per_vori_24k <= 0 || $rate_per_gram_24k <= 0) out_json(400, false, 'সঠিক 24K রেট দিন', null);
if ($system_purity <= 0)        out_json(400, false, 'System purity সঠিক নয়', null);
if ($actual_purity <= 0)        out_json(400, false, 'Actual purity সঠিক নয়', null);
if ($actual_purity > $system_purity) out_json(400, false, 'Actual purity, system purity-এর বেশি হতে পারবে না', null);
if ($payment_method_id == 1 && !$cash_account_id) out_json(400, false, 'ক্যাশ অ্যাকাউন্ট সিলেক্ট করুন', null);

$pure_ratio   = round($actual_purity / 100, 6);
$pure_weight  = round($gross_weight * $pure_ratio, 3);
$total_amount = round($pure_weight * $rate_per_gram_24k, 2);

/* ── Partial payment logic ── */

$paid_amount_override = null;
if (array_key_exists('paid_amount_override', $data) && $data['paid_amount_override'] !== null && $data['paid_amount_override'] !== '') {
    $paid_amount_override = (float)$data['paid_amount_override'];
}

$paid_amount    = ($paid_amount_override !== null) ? min($paid_amount_override, $total_amount) : $total_amount;
$due_amount_val = round($total_amount - $paid_amount, 2);
if ($due_amount_val < 0) $due_amount_val = 0;
$payment_status_val = ($due_amount_val <= 0) ? 'paid' : (($paid_amount > 0) ? 'partial' : 'due');

/* ── Save ── */

try {
    if (!table_exists($pdo, 'purchases')) out_json(500, false, 'purchases table পাওয়া যায়নি', null);

    ensure_column($pdo, 'purchases', 'purchase_date',    "DATE NULL");
    ensure_column($pdo, 'purchases', 'item_name',        "VARCHAR(255) NULL");
    ensure_column($pdo, 'purchases', 'party_id',         "INT NULL");
    ensure_column($pdo, 'purchases', 'location_id',      "INT NULL");
    ensure_column($pdo, 'purchases', 'karat',            "VARCHAR(20) NULL");
    ensure_column($pdo, 'purchases', 'total_pieces',     "INT NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'system_purity',    "DECIMAL(10,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'actual_purity',    "DECIMAL(10,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'gross_weight',     "DECIMAL(12,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'pure_weight',      "DECIMAL(12,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'vori',             "DECIMAL(12,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'ana',              "DECIMAL(12,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'roti',             "DECIMAL(12,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'point',            "DECIMAL(12,3) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'rate_24k',         "DECIMAL(15,6) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'rate_per_vori_24k',"DECIMAL(15,2) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'total_amount',     "DECIMAL(15,2) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'paid_amount',      "DECIMAL(12,2) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'due_amount',       "DECIMAL(12,2) NOT NULL DEFAULT 0");
    ensure_column($pdo, 'purchases', 'payment_status',   "ENUM('paid','partial','due') NOT NULL DEFAULT 'due'");
    ensure_column($pdo, 'purchases', 'payment_method_id',"INT NULL");
    ensure_column($pdo, 'purchases', 'cash_account_id',  "INT NULL");
    ensure_column($pdo, 'purchases', 'note',             "TEXT NULL");
    ensure_column($pdo, 'purchases', 'created_at',       "DATETIME NULL");

    $pCols = get_columns($pdo, 'purchases');

    $pdo->beginTransaction();
    $recalcCashAccountIds = [];

    $cols = []; $marks = []; $params = [];

    add_insert($cols, $marks, $params, $pCols, 'purchase_date',     $purchase_date);
    add_insert($cols, $marks, $params, $pCols, 'date',              $purchase_date);
    add_insert($cols, $marks, $params, $pCols, 'item_name',         $item_name);
    add_insert($cols, $marks, $params, $pCols, 'name',              $item_name);
    add_insert($cols, $marks, $params, $pCols, 'product_name',      $item_name);
    add_insert($cols, $marks, $params, $pCols, 'party_id',          $party_id);
    add_insert($cols, $marks, $params, $pCols, 'supplier_id',       $party_id);
    add_insert($cols, $marks, $params, $pCols, 'location_id',       $location_id);
    add_insert($cols, $marks, $params, $pCols, 'stock_location_id', $location_id);
    add_insert($cols, $marks, $params, $pCols, 'karat',             $karat);
    add_insert($cols, $marks, $params, $pCols, 'purity',            $karat);
    add_insert($cols, $marks, $params, $pCols, 'total_pieces',      $total_pieces);
    add_insert($cols, $marks, $params, $pCols, 'pieces',            $total_pieces);
    add_insert($cols, $marks, $params, $pCols, 'qty',               $total_pieces);
    add_insert($cols, $marks, $params, $pCols, 'system_purity',     $system_purity);
    add_insert($cols, $marks, $params, $pCols, 'actual_purity',     $actual_purity);
    add_insert($cols, $marks, $params, $pCols, 'deduction',         $deduction);
    add_insert($cols, $marks, $params, $pCols, 'gross_weight',      $gross_weight);
    add_insert($cols, $marks, $params, $pCols, 'gross_weight_gram', $gross_weight);
    add_insert($cols, $marks, $params, $pCols, 'total_weight_gram', $gross_weight);
    add_insert($cols, $marks, $params, $pCols, 'pure_weight',       $pure_weight);
    add_insert($cols, $marks, $params, $pCols, 'pure_weight_gram',  $pure_weight);
    add_insert($cols, $marks, $params, $pCols, 'pure_ratio',        $pure_ratio);
    add_insert($cols, $marks, $params, $pCols, 'vori',              $vori);
    add_insert($cols, $marks, $params, $pCols, 'bhori',             $vori);
    add_insert($cols, $marks, $params, $pCols, 'ana',               $ana);
    add_insert($cols, $marks, $params, $pCols, 'roti',              $roti);
    add_insert($cols, $marks, $params, $pCols, 'rati',              $roti);
    add_insert($cols, $marks, $params, $pCols, 'point',             $point);
    add_insert($cols, $marks, $params, $pCols, 'rate_24k',          $rate_per_gram_24k);
    add_insert($cols, $marks, $params, $pCols, 'rate_per_gram_24k', $rate_per_gram_24k);
    add_insert($cols, $marks, $params, $pCols, 'rate_per_gram',     $rate_per_gram_24k);
    add_insert($cols, $marks, $params, $pCols, 'rate_per_vori_24k', $rate_per_vori_24k);
    add_insert($cols, $marks, $params, $pCols, 'rate_per_vori',     $rate_per_vori_24k);
    add_insert($cols, $marks, $params, $pCols, 'gold_rate',         $rate_per_vori_24k);
    add_insert($cols, $marks, $params, $pCols, 'total_amount',      $total_amount);
    add_insert($cols, $marks, $params, $pCols, 'amount',            $total_amount);

    // ── Partial payment (v3) ──
    add_insert($cols, $marks, $params, $pCols, 'paid_amount',    $paid_amount);
    add_insert($cols, $marks, $params, $pCols, 'due_amount',     $due_amount_val);
    add_insert($cols, $marks, $params, $pCols, 'payment_status', $payment_status_val);

    add_insert($cols, $marks, $params, $pCols, 'payment_method_id', $payment_method_id);
    add_insert($cols, $marks, $params, $pCols, 'cash_account_id',   $cash_account_id);
    add_insert($cols, $marks, $params, $pCols, 'note',              $note);
    add_insert($cols, $marks, $params, $pCols, 'remarks',           $note);
    add_insert($cols, $marks, $params, $pCols, 'created_by',        $_SESSION['user_id'] ?? null);
    add_insert($cols, $marks, $params, $pCols, 'created_at',        date('Y-m-d H:i:s'));

    if (empty($cols)) throw new Exception('Insert column পাওয়া যায়নি');

    $sql  = "INSERT INTO purchases (" . implode(',', $cols) . ") VALUES (" . implode(',', $marks) . ")";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    $purchase_id = (int)$pdo->lastInsertId();

    /* ── Cash entry — paid_amount টাকাই কাটবে (due নয়) ── */
    if ($payment_method_id == 1 && $cash_account_id && $paid_amount > 0 && table_exists($pdo, 'cash_transactions')) {
        $catStmt = $pdo->prepare("SELECT id FROM cash_categories WHERE category_code = 'PURCHASE_PAYMENT' LIMIT 1");
        $catStmt->execute();
        $purchaseCatId = (int)$catStmt->fetchColumn();

        if ($purchaseCatId <= 0) {
            throw new RuntimeException("PURCHASE_PAYMENT category পাওয়া যায়নি");
        }

        if (function_exists('gs_insert_cash_transaction')) {
            gs_insert_cash_transaction($pdo, [
                'category_id'      => $purchaseCatId,
                'cash_account_id'  => $cash_account_id,
                'amount'           => $paid_amount,
                'transaction_date' => $purchase_date,
                'note'             => 'নতুন ক্রয়: ' . $item_name . ($due_amount_val > 0 ? ' (আংশিক পরিশোধ)' : ''),
                'reference_type'   => 'new_purchase',
                'reference_id'     => $purchase_id,
                'created_by'       => $_SESSION['user_id'] ?? null,
            ]);
            $recalcCashAccountIds[] = (int)$cash_account_id;
        }
    }

    if ($party_id && table_exists($pdo, 'parties')) {
        $partyCols = get_columns($pdo, 'parties');
        if (isset($partyCols['balance'])) {
            $p = $pdo->prepare("UPDATE parties SET balance = balance - ? WHERE id = ?");
            $p->execute([$total_amount, $party_id]);
        }
    }

    if (function_exists('gs_recalc_cash_accounts')) {
        gs_recalc_cash_accounts($pdo, $recalcCashAccountIds);
    }

    $pdo->commit();

    out_json(200, true, 'ক্রয় সফলভাবে সেভ হয়েছে', [
        'purchase_id'     => $purchase_id,
        'item_name'       => $item_name,
        'party_id'        => $party_id,
        'location_id'     => $location_id,
        'karat'           => $karat,
        'total_pieces'    => $total_pieces,
        'vori'            => $vori,
        'ana'             => $ana,
        'roti'            => $roti,
        'point'           => $point,
        'gross_weight'    => $gross_weight,
        'pure_weight'     => $pure_weight,
        'rate_per_vori_24k' => $rate_per_vori_24k,
        'rate_per_gram_24k' => $rate_per_gram_24k,
        'total_amount'    => $total_amount,
        'paid_amount'     => $paid_amount,
        'due_amount'      => $due_amount_val,
        'payment_status'  => $payment_status_val,
    ]);

} catch (Throwable $e) {
    if ($pdo instanceof PDO && $pdo->inTransaction()) $pdo->rollBack();
    out_json(500, false, 'সার্ভার সমস্যা: ' . $e->getMessage(), null);
}