<?php
// ============================================================
//  MediCore — Recycle Bin & Soft Delete API
//  File: api/recycle.php
// ============================================================
require_once '../includes/db.php';
require_once '../includes/auth.php';
require_once '../includes/audit.php';
require_login();
session_write_close();

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

// ── Add super_admin role to users ENUM ────────────────────────
try {
    $enumVal = db_val(
        "SELECT COLUMN_TYPE FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA = DATABASE()
            AND TABLE_NAME   = 'users'
            AND COLUMN_NAME  = 'role'"
    );
    if ($enumVal && !str_contains($enumVal, 'super_admin')) {
        db_run(
            "ALTER TABLE users MODIFY COLUMN role
             ENUM('super_admin','admin','manager','cashier','warehouse','staff')
             DEFAULT 'cashier'"
        );
    }
} catch (Throwable $e) { /* best-effort */ }

// ── Add soft-delete columns to all modules ────────────────────
// (shared helper — see includes/db.php for why this used to fail
// silently on every table except `products`)
$softDeleteTables = [
    'products', 'customers', 'suppliers',
    'sales', 'purchases', 'sales_returns', 'purchase_returns',
];
foreach ($softDeleteTables as $tbl) {
    ensure_soft_delete($tbl);
}

// Restoring a sale/PO from the recycle bin also moves stock (see
// restore_sale()/restore_purchase() below) — must run before any
// transaction starts, same DDL-inside-transaction rule as every other
// stock-mutating endpoint in this app.
ensure_stock_ledger_table();

// ── Ensure audit_logs has old_values / new_values columns ─────
try {
    $hasOld = db_val(
        "SELECT COUNT(*) FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA = DATABASE()
            AND TABLE_NAME   = 'audit_logs'
            AND COLUMN_NAME  = 'old_values'"
    );
    if (!$hasOld) {
        db_run("ALTER TABLE audit_logs
            ADD COLUMN old_values TEXT DEFAULT NULL AFTER details,
            ADD COLUMN new_values TEXT DEFAULT NULL AFTER old_values");
    }
} catch (Throwable $e) { /* best-effort */ }

// ── Helpers ───────────────────────────────────────────────────
// is_super_admin() is already defined in includes/auth.php — redeclaring
// it here caused a fatal "Cannot redeclare" error on every request to
// this file. Removed; the auth.php version is used instead.
function require_manager_up(): void {
    $role = $_SESSION['user_role'] ?? '';
    if (!in_array($role, ['super_admin', 'admin', 'manager'])) {
        http_response_code(403);
        echo json_encode(['success' => false, 'error' => 'Permission denied']);
        exit;
    }
}

$body   = json_decode(file_get_contents('php://input'), true) ?? [];
$action = trim($body['action'] ?? '');

try {
    match($action) {
        'list'           => api_list($body),
        'restore'        => api_restore($body),
        'permanent_delete' => api_permanent_delete($body),
        'stats'          => api_stats(),
        default          => fail('Unknown action'),
    };
} catch (Throwable $e) {
    fail($e->getMessage());
}

// ── List deleted records across all modules ───────────────────
function api_list(array $b): void {
    require_manager_up();

    $module  = trim($b['module'] ?? 'all');
    $q       = trim($b['q']      ?? '');
    $result  = [];
    $failed  = []; // modules whose query errored — surfaced to the UI so a
                    // silently-skipped table doesn't look like "no data"

    $modules = [
        'products'         => ['tbl' => 'products',         'label' => 'Product',          'name_col' => 'name'],
        'customers'        => ['tbl' => 'customers',        'label' => 'Customer',         'name_col' => 'name'],
        'suppliers'        => ['tbl' => 'suppliers',        'label' => 'Supplier',         'name_col' => 'name'],
        'sales'            => ['tbl' => 'sales',            'label' => 'Sale',             'name_col' => 'invoice'],
        'purchases'        => ['tbl' => 'purchases',        'label' => 'Purchase',         'name_col' => 'invoice'],
        'sale_returns'     => ['tbl' => 'sales_returns',    'label' => 'Sale Return',      'name_col' => 'srn'],
        'purchase_returns' => ['tbl' => 'purchase_returns', 'label' => 'Purchase Return',  'name_col' => 'prn'],
    ];

    $toQuery = ($module === 'all') ? $modules : (isset($modules[$module]) ? [$module => $modules[$module]] : []);

    foreach ($toQuery as $key => $cfg) {
        // Each module's query is independently guarded — a single
        // module with a table that doesn't exist yet, or a schema
        // mismatch on one table, must never abort the whole list.
        // Previously this whole loop shared one outer try/catch (in
        // the dispatcher), so ANY module's query failure surfaced as
        // a single opaque error and the page appeared to hang/never
        // render (the same class of bug as the earlier
        // sales.advance_used missing-column incident).
        try {
            $nameCol = $cfg['name_col'];
            $tbl     = $cfg['tbl'];

            // Check if table has is_deleted column
            $hasDel = db_val(
                "SELECT COUNT(*) FROM information_schema.COLUMNS
                  WHERE TABLE_SCHEMA = DATABASE()
                    AND TABLE_NAME   = ?
                    AND COLUMN_NAME  = 'is_deleted'",
                [$tbl]
            );
            if (!$hasDel) continue;

            $where  = "WHERE is_deleted = 1";
            $params = [];
            if ($q) {
                $where  .= " AND `$nameCol` LIKE ?";
                $params[] = "%$q%";
            }

            $rows = db_rows(
                "SELECT id, `$nameCol` AS record_name, deleted_at, deleted_by
                   FROM `$tbl` $where
                  ORDER BY deleted_at DESC
                  LIMIT 100",
                $params
            );

            foreach ($rows as $row) {
                $result[] = [
                    'module'      => $key,
                    'module_label'=> $cfg['label'],
                    'id'          => $row['id'],
                    'record_name' => $row['record_name'],
                    'deleted_at'  => $row['deleted_at'],
                    'deleted_by'  => $row['deleted_by'],
                    'can_restore' => true,
                ];
            }
        } catch (Throwable $e) {
            error_log("recycle api_list: module '$key' ($tbl) failed: " . $e->getMessage());
            $failed[] = ['module' => $key, 'label' => $cfg['label'], 'error' => $e->getMessage()];
            continue; // skip this module, keep the rest of the list working
        }
    }

    // Sort by deleted_at desc across all modules
    usort($result, fn($a,$b) => strcmp($b['deleted_at'] ?? '', $a['deleted_at'] ?? ''));
    ok(['data' => $result, 'failed' => $failed]);
}

// ── Restore a soft-deleted record ─────────────────────────────
function api_restore(array $b): void {
    require_manager_up();

    $module = trim($b['module'] ?? '');
    $id     = (int)($b['id']    ?? 0);
    if (!$module || !$id) fail('Module and id required');

    $tblMap = [
        'products'         => 'products',
        'customers'        => 'customers',
        'suppliers'        => 'suppliers',
        'sales'            => 'sales',
        'purchases'        => 'purchases',
        'sale_returns'     => 'sales_returns',
        'purchase_returns' => 'purchase_returns',
    ];
    if (!isset($tblMap[$module])) fail('Unknown module');
    $tbl = $tblMap[$module];

    $rec = db_row("SELECT * FROM `$tbl` WHERE id = ? AND is_deleted = 1", [$id]);
    if (!$rec) fail('Record not found or not deleted');

    db()->beginTransaction();
    try {
        // Clear soft-delete flags
        db_run(
            "UPDATE `$tbl`
                SET is_deleted = 0, deleted_at = NULL, deleted_by = NULL
              WHERE id = ?",
            [$id]
        );

        // Module-specific restore side-effects
        match ($module) {
            'sales'    => restore_sale($id, $rec),
            'purchases'=> restore_purchase($id, $rec),
            default    => null,
        };

        db()->commit();

        log_action('restore', $module, $id,
            "Restored {$module} record #{$id}");
        ok(['msg' => ucfirst(str_replace('_', ' ', $module)) . ' restored successfully']);

    } catch (Throwable $e) {
        db()->rollBack();
        fail('Restore failed: ' . $e->getMessage());
    }
}

function restore_sale(int $saleId, array $sale): void {
    // Re-deduct stock that was added back at soft-delete time
    $items = db_rows("SELECT * FROM sale_items WHERE sale_id = ?", [$saleId]);
    foreach ($items as $it) {
        db_run(
            "UPDATE products SET stock_pcs = GREATEST(0, stock_pcs - ?) WHERE id = ?",
            [$it['qty'], $it['product_id']]
        );
        $newStock = (int) db_val("SELECT stock_pcs FROM products WHERE id = ?", [$it['product_id']]);
        record_stock_ledger((int) $it['product_id'], 'sale', 0, (int) $it['qty'], $newStock,
            'sales', $saleId, $sale['invoice'] ?? '', 'Sale restored from Recycle Bin');
    }
    // Restore customer due balance
    if (($sale['customer_id'] ?? 1) > 1 && ($sale['due'] ?? 0) > 0) {
        db_run(
            "UPDATE customers SET due = due + ? WHERE id = ?",
            [$sale['due'], $sale['customer_id']]
        );
    }
}

function restore_purchase(int $poId, array $po): void {
    // Re-deduct stock that was reversed at soft-delete time
    $items = db_rows("SELECT * FROM purchase_items WHERE purchase_id = ?", [$poId]);
    foreach ($items as $it) {
        $addBack = ($it['qty_pcs'] ?? 0) + ($it['free'] ?? 0);
        db_run(
            "UPDATE products SET stock_pcs = stock_pcs + ? WHERE id = ?",
            [$addBack, $it['product_id']]
        );
        $newStock = (int) db_val("SELECT stock_pcs FROM products WHERE id = ?", [$it['product_id']]);
        record_stock_ledger((int) $it['product_id'], 'purchase', (int) $addBack, 0, $newStock,
            'purchases', $poId, $po['invoice'] ?? '', 'PO restored from Recycle Bin');
    }
    // Restore supplier balance
    if (($po['due'] ?? 0) > 0 && ($po['supplier_id'] ?? 0) > 0) {
        db_run(
            "UPDATE suppliers SET balance = balance + ? WHERE id = ?",
            [$po['due'], $po['supplier_id']]
        );
    }
}

// ── Permanent Delete — Super Admin only ───────────────────────
function api_permanent_delete(array $b): void {
    if (!is_super_admin()) {
        fail('Permanent delete is restricted to Super Admin only.');
    }

    $module = trim($b['module'] ?? '');
    $id     = (int)($b['id']    ?? 0);
    if (!$module || !$id) fail('Module and id required');

    $tblMap = [
        'products'         => 'products',
        'customers'        => 'customers',
        'suppliers'        => 'suppliers',
        'sales'            => 'sales',
        'purchases'        => 'purchases',
        'sale_returns'     => 'sales_returns',
        'purchase_returns' => 'purchase_returns',
    ];
    if (!isset($tblMap[$module])) fail('Unknown module');
    $tbl = $tblMap[$module];

    $rec = db_row("SELECT * FROM `$tbl` WHERE id = ? AND is_deleted = 1", [$id]);
    if (!$rec) fail('Record not found or not in Recycle Bin (must be soft-deleted first)');

    log_action('permanent_delete', $module, $id,
        "Permanently deleted {$module} record #{$id}: " .
        json_encode(array_intersect_key($rec, array_flip(['id','invoice','srn','prn','name']))));

    db_run("DELETE FROM `$tbl` WHERE id = ? AND is_deleted = 1", [$id]);
    ok(['msg' => 'Record permanently deleted']);
}

// ── Stats for dashboard badge ─────────────────────────────────
function api_stats(): void {
    $tables = ['products','customers','suppliers','sales','purchases','sales_returns','purchase_returns'];
    $total  = 0;
    foreach ($tables as $tbl) {
        try {
            $cnt = db_val("SELECT COUNT(*) FROM `$tbl` WHERE is_deleted = 1");
            $total += (int)$cnt;
        } catch (Throwable $e) { /* table may not have is_deleted yet */ }
    }
    ok(['total' => $total]);
}

function ok(array $extra = []): void {
    echo json_encode(array_merge(['success' => true], $extra)); exit;
}
function fail(string $msg): never {
    echo json_encode(['success' => false, 'error' => $msg]); exit;
}
