

<?php
/**
 * Rig N Dig - Core Functions
 */

require_once __DIR__ . '/db.php';

// ─── Security ──────────────────────────────────────────────
function startSecureSession(): void {
    if (session_status() === PHP_SESSION_NONE) {
        ini_set('session.cookie_httponly', 1);
        ini_set('session.cookie_samesite', 'Strict');
        session_set_cookie_params(['lifetime' => SESSION_LIFETIME, 'httponly' => true, 'samesite' => 'Strict']);
        session_start();
    }
}

function csrfToken(): string {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function verifyCsrf(): void {
    $token = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
    if (!hash_equals($_SESSION['csrf_token'] ?? '', $token)) {
        http_response_code(403);
        die('Invalid CSRF token.');
    }
}

function sanitize(string $value): string {
    return htmlspecialchars(strip_tags(trim($value)), ENT_QUOTES, 'UTF-8');
}

function e($value): string {
    if (is_array($value)) {
        return htmlspecialchars(implode(', ', array_map(function($item) {
            return is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string)$item;
        }, $value)), ENT_QUOTES, 'UTF-8', false);
    }
    return htmlspecialchars((string)($value ?? ''), ENT_QUOTES, 'UTF-8', false);
}

// ─── Authentication ─────────────────────────────────────────
function isAdminLoggedIn(): bool {
    return !empty($_SESSION['admin_id']);
}

function requireAdmin(): void {
    if (!isAdminLoggedIn()) {
        header('Location: ' . rtrim(SITE_URL, '/') . '/admin/login.php?redirect=' . urlencode($_SERVER['REQUEST_URI']));
        exit;
    }
}

function isCustomerLoggedIn(): bool {
    return !empty($_SESSION['customer_id']);
}

function requireCustomer(): void {
    if (!isCustomerLoggedIn()) {
        header('Location: ' . SITE_URL . '/customer/login.php?redirect=' . urlencode($_SERVER['REQUEST_URI']));
        exit;
    }
}

function getCurrentCustomer(): ?array {
    if (!isCustomerLoggedIn()) return null;
    return Database::fetchOne("SELECT * FROM customers WHERE id = ?", [$_SESSION['customer_id']]);
}

function logActivity(string $userType, int $userId, string $action, string $details = ''): void {
    Database::insert(
        "INSERT INTO activity_log (user_type, user_id, action, details, ip_address) VALUES (?,?,?,?,?)",
        [$userType, $userId, $action, $details, $_SERVER['REMOTE_ADDR'] ?? '']
    );
}

// ─── Slugs & Numbers ────────────────────────────────────────
function slugify(string $text): string {
    $text = strtolower(trim($text));
    $text = preg_replace('/[^a-z0-9-]+/', '-', $text);
    return trim($text, '-');
}

function generateNumber(string $prefix, string $table, string $column): string {
    $count = Database::fetchOne("SELECT COUNT(*) as c FROM `$table`")['c'] ?? 0;
    return $prefix . str_pad((int)$count + 1, 5, '0', STR_PAD_LEFT);
}

// ─── File Upload ─────────────────────────────────────────────
function handleFileUpload(array $file, string $folder, array $allowedTypes, int $maxSize = 10485760): array {
    // Map upload error codes to human-readable messages
    $uploadErrors = [
        UPLOAD_ERR_INI_SIZE   => 'File exceeds server upload_max_filesize limit.',
        UPLOAD_ERR_FORM_SIZE  => 'File exceeds the form MAX_FILE_SIZE limit.',
        UPLOAD_ERR_PARTIAL    => 'File was only partially uploaded.',
        UPLOAD_ERR_NO_FILE    => 'No file was uploaded.',
        UPLOAD_ERR_NO_TMP_DIR => 'Missing server temp folder.',
        UPLOAD_ERR_CANT_WRITE => 'Server failed to write file to disk.',
        UPLOAD_ERR_EXTENSION  => 'A PHP extension blocked the upload.',
    ];
    if ($file['error'] !== UPLOAD_ERR_OK) {
        return ['success' => false, 'error' => $uploadErrors[$file['error']] ?? 'Upload error code: ' . $file['error']];
    }
    if ($file['size'] > $maxSize) {
        return ['success' => false, 'error' => 'File size exceeds ' . ($maxSize / 1024 / 1024) . 'MB limit.'];
    }

    // MIME detection — finfo preferred, extension map as fallback
    $extMimeMap = [
        'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png',
        'gif' => 'image/gif', 'webp' => 'image/webp', 'pdf' => 'application/pdf',
        'doc' => 'application/msword',
        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    ];
    // Aliases accepted by browsers but not always returned by finfo
    $mimeAliases = ['image/jpg' => 'image/jpeg', 'image/pjpeg' => 'image/jpeg', 'image/x-png' => 'image/png'];

    $mime = null;
    if (function_exists('finfo_open')) {
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $detected = $finfo->file($file['tmp_name']);
        $mime = $mimeAliases[$detected] ?? $detected;
    }
    // Fallback: use extension if finfo unavailable or returned nothing
    if (!$mime) {
        $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        $mime = $extMimeMap[$ext] ?? 'application/octet-stream';
    }

    if (!in_array($mime, $allowedTypes)) {
        return ['success' => false, 'error' => 'File type not allowed (' . $mime . '). Allowed: ' . implode(', ', $allowedTypes)];
    }

    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    // Normalise jpg → jpeg in extension
    if ($ext === 'jpg') $ext = 'jpeg';
    $filename = uniqid('', true) . '.' . $ext;
    $dir  = UPLOAD_PATH . '/' . $folder;
    $dest = $dir . '/' . $filename;
    if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
        return ['success' => false, 'error' => 'Could not create upload directory: ' . $dir];
    }
    if (!move_uploaded_file($file['tmp_name'], $dest)) {
        return ['success' => false, 'error' => 'Could not save file. Check that ' . $dir . ' is writable.'];
    }
    return ['success' => true, 'filename' => $folder . '/' . $filename];
}

// ─── Quote Cart ───────────────────────────────────────────────
function getCartKey(): string {
    if (empty($_SESSION['cart_key'])) {
        $_SESSION['cart_key'] = bin2hex(random_bytes(16));
    }
    return $_SESSION['cart_key'];
}

function getCartItems(): array {
    $key = getCartKey();
    return Database::fetchAll(
        "SELECT qs.*, p.name as product_name, p.image, p.sku, p.slug
         FROM quote_sessions qs
         LEFT JOIN products p ON qs.product_id = p.id
         WHERE qs.session_key = ?",
        [$key]
    );
}

function getCartCount(): int {
    $key = getCartKey();
    $row = Database::fetchOne("SELECT COUNT(*) as c FROM quote_sessions WHERE session_key = ?", [$key]);
    return (int)($row['c'] ?? 0);
}

function addToCart(int $productId, int $qty = 1, string $notes = ''): bool {
    $key = getCartKey();
    $existing = Database::fetchOne("SELECT id FROM quote_sessions WHERE session_key=? AND product_id=?", [$key, $productId]);
    if ($existing) {
        Database::execute("UPDATE quote_sessions SET quantity=quantity+?, notes=? WHERE id=?", [$qty, $notes, $existing['id']]);
    } else {
        Database::insert("INSERT INTO quote_sessions (session_key, customer_id, product_id, quantity, notes) VALUES (?,?,?,?,?)",
            [$key, $_SESSION['customer_id'] ?? null, $productId, $qty, $notes]);
    }
    return true;
}

function removeFromCart(int $itemId): void {
    Database::execute("DELETE FROM quote_sessions WHERE id=? AND session_key=?", [$itemId, getCartKey()]);
}

function clearCart(): void {
    Database::execute("DELETE FROM quote_sessions WHERE session_key=?", [getCartKey()]);
}

// ─── Email ─────────────────────────────────────────────────────
function emailHtmlWrapper(string $bodyHtml): string {
    $co    = setting('company_name', 'Rig N Dig');
    $addr  = setting('address', '');
    $phone = setting('phone', '');
    $email = setting('email', '');
    $logo  = setting('logo', '');
    $year  = date('Y');
    $logoHtml = $logo
        ? '<img src="' . (defined('SITE_URL') ? rtrim(SITE_URL,'/') : '') . '/uploads/' . htmlspecialchars($logo,ENT_QUOTES) . '" alt="' . htmlspecialchars($co,ENT_QUOTES) . '" style="max-height:50px;max-width:180px;object-fit:contain;">'
        : '<span style="font-size:24px;font-weight:900;color:#fff;letter-spacing:1px;">' . htmlspecialchars($co) . '</span>';
    return '<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Email</title></head>
<body style="margin:0;padding:0;background:#f1f3f6;font-family:\'Segoe UI\',Arial,sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f1f3f6;padding:32px 0;">
<tr><td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:10px;overflow:hidden;box-shadow:0 4px 16px rgba(0,0,0,.10);">

  <!-- HEADER -->
  <tr>
    <td style="background:#ff6b00;padding:24px 32px;text-align:center;">
      ' . $logoHtml . '
      <p style="color:rgba(255,255,255,.85);margin:6px 0 0;font-size:11px;text-transform:uppercase;letter-spacing:1.5px;">API Standard Industrial Equipment</p>
    </td>
  </tr>

  <!-- BODY -->
  <tr>
    <td style="padding:36px 36px 28px;color:#1a1a2e;font-size:14px;line-height:1.7;">
      ' . $bodyHtml . '
    </td>
  </tr>

  <!-- DIVIDER -->
  <tr><td style="padding:0 36px;"><hr style="border:none;border-top:1px solid #e5e7eb;margin:0;"></td></tr>

  <!-- FOOTER -->
  <tr>
    <td style="background:#0a0e1a;padding:20px 32px;text-align:center;">
      <p style="color:#9ca3af;font-size:12px;margin:0 0 4px;">' . htmlspecialchars($co) . ' &bull; ' . htmlspecialchars($addr) . '</p>
      ' . ($phone ? '<p style="color:#9ca3af;font-size:12px;margin:0;">' . htmlspecialchars($phone) . ($email ? ' &bull; ' . htmlspecialchars($email) : '') . '</p>' : '') . '
      <p style="color:#4b5563;font-size:11px;margin:10px 0 0;">&copy; ' . $year . ' ' . htmlspecialchars($co) . '. All rights reserved.</p>
    </td>
  </tr>

</table>
</td></tr>
</table>
</body></html>';
}

function sendEmail(string $to, string $subject, string $body, string $fromName = ''): bool {
    if (!SMTP_HOST) return false;
    if (!$fromName) $fromName = setting('company_name', 'Rig N Dig');
    $headers  = "MIME-Version: 1.0\r\n";
    $headers .= "Content-type: text/html; charset=UTF-8\r\n";
    $headers .= "From: {$fromName} <" . SMTP_USER . ">\r\n";
    $headers .= "Reply-To: " . SMTP_USER . "\r\n";
    $headers .= "X-Mailer: PHP/" . phpversion() . "\r\n";
    return mail($to, $subject, $body, $headers);
}

function sendEmailFromTemplate(string $templateName, string $to, array $vars = []): bool {
    $tpl = Database::fetchOne("SELECT * FROM email_templates WHERE name=?", [$templateName]);
    if (!$tpl) return false;
    // Auto-inject global company vars
    $vars = array_merge([
        'company_name' => setting('company_name', 'Rig N Dig'),
        'site_url'     => defined('SITE_URL') ? SITE_URL : '',
        'portal_url'   => (defined('SITE_URL') ? rtrim(SITE_URL,'/') : '') . '/portal/login.php',
        'year'         => date('Y'),
    ], $vars);
    $subject = $tpl['subject'];
    $body    = $tpl['body'];
    foreach ($vars as $k => $v) {
        $subject = str_replace('{{' . $k . '}}', $v, $subject);
        $body    = str_replace('{{' . $k . '}}', $v, $body);
    }
    $wrappedBody = emailHtmlWrapper($body);
    return sendEmail($to, $subject, $wrappedBody);
}

// ─── Formatting ────────────────────────────────────────────────
function formatPrice(float $amount): string {
    return 'PKR ' . number_format($amount, 2);
}

function formatDate(string $date): string {
    return date('d M Y', strtotime($date));
}

function statusBadge(string $status): string {
    $map = [
        'pending'    => 'bg-yellow-600',
        'reviewed'   => 'bg-blue-600',
        'sent'       => 'bg-indigo-600',
        'approved'   => 'bg-green-600',
        'rejected'   => 'bg-red-600',
        'expired'    => 'bg-gray-600',
        'draft'      => 'bg-gray-600',
        'paid'       => 'bg-green-600',
        'partial'    => 'bg-yellow-600',
        'overdue'    => 'bg-red-700',
        'cancelled'  => 'bg-gray-700',
        'confirmed'  => 'bg-blue-600',
        'processing' => 'bg-purple-600',
        'shipped'    => 'bg-cyan-600',
        'delivered'  => 'bg-green-600',
        'in_stock'   => 'bg-green-600',
        'out_of_stock' => 'bg-red-600',
        'on_order'   => 'bg-yellow-600',
    ];
    $cls = $map[$status] ?? 'bg-gray-600';
    return '<span class="' . $cls . ' text-white text-xs px-2 py-0.5 rounded-full">' . ucwords(str_replace('_', ' ', $status)) . '</span>';
}

// ─── Pagination ────────────────────────────────────────────────
function paginate(int $total, int $perPage, int $page, string $baseUrl): array {
    $totalPages = max(1, ceil($total / $perPage));
    $page = max(1, min($page, $totalPages));
    $offset = ($page - 1) * $perPage;
    return ['total' => $total, 'per_page' => $perPage, 'page' => $page, 'total_pages' => $totalPages, 'offset' => $offset, 'base_url' => $baseUrl];
}

function paginationHtml(array $p): string {
    if ($p['total_pages'] <= 1) return '';
    $html = '<div class="flex items-center gap-2 justify-center mt-8">';
    for ($i = 1; $i <= $p['total_pages']; $i++) {
        $url = $p['base_url'] . (strpos($p['base_url'], '?') !== false ? '&' : '?') . 'page=' . $i;
        $active = $i === $p['page'];
        $html .= '<a href="' . e($url) . '" class="px-3 py-1.5 rounded text-sm ' . ($active ? 'bg-orange-600 text-white' : 'bg-gray-800 text-gray-300 hover:bg-gray-700') . '">' . $i . '</a>';
    }
    $html .= '</div>';
    return $html;
}

// ─── Settings helper ────────────────────────────────────────────
function setting(string $key, string $default = ''): string {
    static $cache = [];
    if (!isset($cache[$key])) {
        $cache[$key] = Database::getSetting($key, $default);
    }
    return $cache[$key];
}
