<?php
/**
 * Helper functions — flash messages, JSON, redirects, settings
 */

// ── Redirect ─────────────────────────────────────────────────
function redirect(string $path): never {
    $baseUrl = rtrim(APP_URL, '/');
    header('Location: ' . $baseUrl . '/' . ltrim($path, '/'));
    exit;
}

function redirectBack(): never {
    $ref = $_SERVER['HTTP_REFERER'] ?? APP_URL;
    header('Location: ' . $ref);
    exit;
}

// ── Flash Messages ────────────────────────────────────────────
function flash(string $type, string $message): void {
    $_SESSION['flash'][$type] = $message;
}

function getFlash(string $type): ?string {
    $msg = $_SESSION['flash'][$type] ?? null;
    unset($_SESSION['flash'][$type]);
    return $msg;
}

// ── JSON Response ─────────────────────────────────────────────
function jsonResponse(array $data, int $code = 200): never {
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data);
    exit;
}

function jsonSuccess(string $message = 'Success', array $data = []): never {
    jsonResponse(['success' => true, 'message' => $message] + $data);
}

function jsonError(string $message = 'Error', int $code = 400): never {
    jsonResponse(['success' => false, 'message' => $message], $code);
}

// ── Settings (per tenant, cached) ─────────────────────────────
$_SETTINGS_CACHE = [];

function setting(string $key, mixed $default = null): mixed {
    global $_SETTINGS_CACHE;
    $tenantId = Auth::tenantId();
    
    // 1. Ensure Global Cache is loaded
    if (!isset($_SETTINGS_CACHE['__global__'])) {
        $rows = db()->query("SELECT setting_key, setting_value FROM settings WHERE tenant_id IS NULL");
        $_SETTINGS_CACHE['__global__'] = array_column($rows->fetchAll(), 'setting_value', 'setting_key');
    }
    
    // 2. Load Tenant Cache if logged in
    if ($tenantId && !isset($_SETTINGS_CACHE[$tenantId])) {
        $rows = db()->prepare("SELECT setting_key, setting_value FROM settings WHERE tenant_id = ?");
        $rows->execute([$tenantId]);
        $_SETTINGS_CACHE[$tenantId] = array_column($rows->fetchAll(), 'setting_value', 'setting_key');
    }
    
    // 3. Resolve: Tenant Overrides > Global > Default
    if ($tenantId && isset($_SETTINGS_CACHE[$tenantId][$key])) {
        return $_SETTINGS_CACHE[$tenantId][$key];
    }
    
    return $_SETTINGS_CACHE['__global__'][$key] ?? $default;
}

function clearSettingsCache(?int $tenantId = null): void {
    global $_SETTINGS_CACHE;
    if ($tenantId === null) {
        $_SETTINGS_CACHE = [];
    } else {
        unset($_SETTINGS_CACHE[$tenantId]);
    }
}

function saveSetting(string $key, mixed $value, int $tenantId): void {
    db()->prepare(
        "INSERT INTO settings (tenant_id,setting_key,setting_value) VALUES (?,?,?)
         ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)"
    )->execute([$tenantId, $key, $value]);
    clearSettingsCache($tenantId);
}

// ── Currency formatting ───────────────────────────────────────
function money(float $amount): string {
    $symbol   = setting('currency_symbol', 'GH₵');
    $position = setting('currency_position', 'before');
    $rounding = setting('rounding', 'none');

    // Apply rounding based on setting
    switch ($rounding) {
        case 'whole':
            $amount = round($amount);
            $decimals = 0;
            break;
        case 'half':
            $amount = round($amount * 2) / 2;
            $decimals = 1;
            break;
        case 'decimal':
        default:
            $decimals = 2;
            break;
    }

    $formatted = number_format($amount, $decimals);
    return $position === 'before' ? $symbol . $formatted : $formatted . $symbol;
}

// ── Generate reference numbers ────────────────────────────────
function generateRef(string $prefix): string {
    return strtoupper($prefix) . '-' . date('ymd') . '-' . strtoupper(bin2hex(random_bytes(3)));
}

// ── Date formatting ───────────────────────────────────────────
function formatDate(string $date, string $format = 'M d, Y'): string {
    return date($format, strtotime($date));
}

// ── XSS-safe output ──────────────────────────────────────────
function e(mixed $value): string {
    // Decode HTML entities first, then escape for safe output
    $decoded = html_entity_decode((string)$value, ENT_QUOTES, 'UTF-8');
    return htmlspecialchars($decoded, ENT_QUOTES, 'UTF-8');
}

// ── Pagination HTML ───────────────────────────────────────────
function paginationLinks(array $paginator, string $baseUrl, array $params = []): string {
    if ($paginator['last_page'] <= 1) return '';
    
    $perPage = $_GET['per_page'] ?? (defined('ITEMS_PER_PAGE') ? ITEMS_PER_PAGE : 15);
    $perPageOptions = [15, 50, 100, 'all'];
    
    // Build query string preserving existing params
    $queryString = http_build_query($params);
    $separator = (strpos($baseUrl, '?') !== false) ? '&' : '?';
    $baseUrl .= ($queryString ? $separator . $queryString . '&' : $separator);
    
    $html = '<div class="pagination-wrapper flex justify-between items-center w-full gap-4 flex-wrap">';
    
    // Per-page selector
    $html .= '<div class="pagination-per-page flex items-center gap-2 text-sm text-muted">';
    $html .= '<label>Show:</label>';
    $html .= '<select onchange="changePerPage(this.value)" class="form-control" style="width: auto; padding: 4px 24px 4px 8px; min-height: 32px; font-size: 0.8rem;">';
    foreach ($perPageOptions as $option) {
        $selected = $perPage == $option ? 'selected' : '';
        $html .= "<option value=\"{$option}\" {$selected}>{$option}</option>";
    }
    $html .= '</select>';
    $html .= '<span>per page</span>';
    $html .= '</div>';
    
    // Page numbers
    $html .= '<div class="pagination flex items-center gap-2" style="margin-top: 0;">';
    
    $currentPage = (int)$paginator['page'];
    $lastPage = (int)$paginator['last_page'];
    
    // Previous Link
    if ($currentPage > 1) {
        $prev = $currentPage - 1;
        $html .= "<a href=\"{$baseUrl}page={$prev}&per_page={$perPage}\" title=\"Previous\">&laquo;</a>";
    } else {
        $html .= "<span class=\"disabled\" style=\"opacity: 0.5; cursor: not-allowed;\">&laquo;</span>";
    }
    
    // Sliding window logic
    $window = 2; // Number of pages to show around current page
    
    for ($i = 1; $i <= $lastPage; $i++) {
        if ($i == 1 || $i == $lastPage || ($i >= $currentPage - $window && $i <= $currentPage + $window)) {
            if ($i == $currentPage) {
                $html .= "<span class=\"active\">{$i}</span>";
            } else {
                $html .= "<a href=\"{$baseUrl}page={$i}&per_page={$perPage}\">{$i}</a>";
            }
        } elseif ($i == $currentPage - $window - 1 || $i == $currentPage + $window + 1) {
            $html .= "<span class=\"dots\" style=\"border: none; background: transparent; cursor: default;\">...</span>";
        }
    }
    
    // Next Link
    if ($currentPage < $lastPage) {
        $next = $currentPage + 1;
        $html .= "<a href=\"{$baseUrl}page={$next}&per_page={$perPage}\" title=\"Next\">&raquo;</a>";
    } else {
        $html .= "<span class=\"disabled\" style=\"opacity: 0.5; cursor: not-allowed;\">&raquo;</span>";
    }
    
    $html .= '</div>'; // End .pagination
    
    $html .= '</div>'; // End .pagination-wrapper
    
    // JavaScript for per-page change
    $html .= '<script>
    if (typeof window.changePerPage !== "function") {
        window.changePerPage = function(value) {
            const url = new URL(window.location.href);
            url.searchParams.set("per_page", value);
            url.searchParams.set("page", 1);
            window.location.href = url.toString();
        };
    }
    </script>';
    
    return $html;
}

// ── Stock helpers ─────────────────────────────────────────────
function getStock(int $productId, int $locationId, ?int $batchId = null, int $tenantId = 0): float {
    $tenantId = $tenantId ?: Auth::tenantId();
    if ($batchId) {
        $sql = "SELECT COALESCE(quantity,0) FROM stock WHERE tenant_id=? AND product_id=? AND location_id=? AND batch_id=?";
        $params = [$tenantId, $productId, $locationId, $batchId];
    } else {
        $sql = "SELECT COALESCE(SUM(quantity),0) FROM stock WHERE tenant_id=? AND product_id=? AND location_id=?";
        $params = [$tenantId, $productId, $locationId];
    }
    $stmt = db()->prepare($sql);
    $stmt->execute($params);
    return (float)$stmt->fetchColumn();
}

function isOversellingAllowed(): bool {
    return (bool)setting('allow_negative_stock', 0);
}

function ensureStockAvailable(int $productId, int $locationId, float $qty, ?int $batchId = null, int $tenantId = 0): void {
    if (isOversellingAllowed()) {
        return;
    }

    $current = getStock($productId, $locationId, $batchId, $tenantId);
    if ($current + $qty < 0) {
        throw new Exception('Insufficient stock for product ID ' . $productId . '. Available: ' . $current . ', Required: ' . abs($qty) . '. Cannot complete the sale because it would oversell.');
    }
}

function adjustStock(int $productId, int $locationId, float $qty, ?int $batchId = null, int $tenantId = 0): void {
    $tenantId = $tenantId ?: Auth::tenantId();
    $sql = "INSERT INTO stock (tenant_id,product_id,location_id,batch_id,quantity)
            VALUES (?,?,?,?,?)
            ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity)";
    db()->prepare($sql)->execute([$tenantId, $productId, $locationId, $batchId, $qty]);
}

function logMovement(int $productId, int $locationId, string $type, float $qty, string $ref = '', string $note = '', ?int $batchId = null, int $tenantId = 0): void {
    $tenantId = $tenantId ?: Auth::tenantId();
    $balance  = getStock($productId, $locationId, $batchId, $tenantId);
    db()->prepare(
        "INSERT INTO stock_movements (tenant_id,product_id,location_id,batch_id,type,quantity,balance,reference,note,user_id)
         VALUES (?,?,?,?,?,?,?,?,?,?)"
    )->execute([$tenantId,$productId,$locationId,$batchId,$type,$qty,$balance,$ref,$note,Auth::userId()]);
}

function logMovementWithBalance(int $productId, int $locationId, string $type, float $qty, string $ref = '', string $note = '', ?int $batchId = null, int $tenantId = 0, float $balance = 0): void {
    $tenantId = $tenantId ?: Auth::tenantId();
    db()->prepare(
        "INSERT INTO stock_movements (tenant_id,product_id,location_id,batch_id,type,quantity,balance,reference,note,user_id)
         VALUES (?,?,?,?,?,?,?,?,?,?)"
    )->execute([$tenantId,$productId,$locationId,$batchId,$type,$qty,$balance,$ref,$note,Auth::userId()]);
}
