<?php
declare(strict_types=1);
require_once __DIR__ . '/db.php';

header('Content-Type: application/json; charset=utf-8');

function respond(array $payload, int $status = 200): void {
    http_response_code($status);
    echo json_encode($payload, JSON_UNESCAPED_UNICODE);
    exit;
}

function require_post(): void {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        respond(['success' => false, 'message' => 'POST required'], 405);
    }
}

function get_post(string $key, string $default = ''): string {
    return trim((string)($_POST[$key] ?? $default));
}

function get_query(string $key, string $default = ''): string {
    return trim((string)($_GET[$key] ?? $default));
}

function get_user_by_id(int $userId): ?array {
    $conn = db();
    $stmt = $conn->prepare('SELECT id, full_name, username, role FROM users WHERE id = ? LIMIT 1');
    $stmt->bind_param('i', $userId);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    return $row ?: null;
}

function require_user(int $userId): array {
    $user = get_user_by_id($userId);
    if (!$user) {
        respond(['success' => false, 'message' => 'Invalid user'], 401);
    }
    return $user;
}

function is_admin_role(array $user): bool {
    $role = strtolower(trim((string)($user['role'] ?? '')));
    return in_array($role, ['admin', 'superadmin', 'super_admin', 'system admin', 'system_admin'], true);
}

function is_viewer_role(array $user): bool {
    return strtolower((string)($user['role'] ?? '')) === 'viewer';
}

function require_admin(int $userId): array {
    $user = require_user($userId);
    if (!is_admin_role($user)) {
        respond(['success' => false, 'message' => 'Admin access required'], 403);
    }
    return $user;
}

function require_write_user(int $userId): array {
    $user = require_user($userId);
    if (is_viewer_role($user)) {
        respond(['success' => false, 'message' => 'Viewer role is read-only'], 403);
    }
    return $user;
}

function require_view_all_user(int $userId): array {
    $user = require_user($userId);
    if (!is_admin_role($user) && !is_viewer_role($user)) {
        respond(['success' => false, 'message' => 'This role cannot view all records'], 403);
    }
    return $user;
}

function validate_role(string $role): bool {
    return in_array(strtolower(trim($role)), ['admin', 'cashier', 'viewer', 'superadmin', 'super_admin', 'system admin', 'system_admin'], true);
}

function table_has_column(string $table, string $column): bool {
    $conn = db();
    $table = $conn->real_escape_string($table);
    $column = $conn->real_escape_string($column);
    $result = $conn->query("SHOW COLUMNS FROM `$table` LIKE '$column'");
    return $result instanceof mysqli_result && $result->num_rows > 0;
}

function validate_item_type(string $itemType): bool {
    return in_array($itemType, ['sale', 'other'], true);
}


function ensure_sales_status_columns(): void {
    $conn = db();
    $alterParts = [];
    if (!table_has_column('sales', 'sale_status')) {
        $alterParts[] = "ADD COLUMN sale_status VARCHAR(20) NOT NULL DEFAULT 'active' AFTER sale_datetime";
    }
    if (!table_has_column('sales', 'status_reason')) {
        $alterParts[] = "ADD COLUMN status_reason TEXT NULL AFTER sale_status";
    }
    if (!table_has_column('sales', 'status_changed_at')) {
        $alterParts[] = "ADD COLUMN status_changed_at DATETIME NULL AFTER status_reason";
    }
    if (!table_has_column('sales', 'status_changed_by')) {
        $alterParts[] = "ADD COLUMN status_changed_by INT NULL AFTER status_changed_at";
    }
    if ($alterParts !== []) {
        $sql = 'ALTER TABLE sales ' . implode(', ', $alterParts);
        if (!$conn->query($sql)) {
            respond(['success' => false, 'message' => 'Unable to prepare sales status columns: ' . $conn->error], 500);
        }
    }
}

function build_sales_status_update_sql(string $targetStatus): string {
    $parts = ["sale_status = '" . db()->real_escape_string($targetStatus) . "'", 'status_reason = ?'];
    if (table_has_column('sales', 'status_changed_at')) {
        $parts[] = 'status_changed_at = NOW()';
    }
    if (table_has_column('sales', 'status_changed_by')) {
        $parts[] = 'status_changed_by = ?';
    }
    return 'UPDATE sales SET ' . implode(', ', $parts) . ' WHERE id = ?';
}

function bind_sales_status_update(mysqli_stmt $stmt, string $reason, int $adminUserId, int $saleId): void {
    if (table_has_column('sales', 'status_changed_by')) {
        $stmt->bind_param('sii', $reason, $adminUserId, $saleId);
    } else {
        $stmt->bind_param('si', $reason, $saleId);
    }
}



function parse_sale_status_from_notes(?string $notes): array {
    $notes = trim((string)$notes);
    if ($notes !== '' && preg_match('/^\[SALE_STATUS:(voided|deleted)\]\s*(.*?)(?:\n\n(.*))?$/s', $notes, $m)) {
        return [
            'sale_status' => strtolower((string)$m[1]),
            'status_reason' => trim((string)($m[2] ?? '')) !== '' ? trim((string)$m[2]) : null,
            'notes' => isset($m[3]) && trim((string)$m[3]) !== '' ? trim((string)$m[3]) : null,
        ];
    }
    return [
        'sale_status' => 'active',
        'status_reason' => null,
        'notes' => $notes !== '' ? $notes : null,
    ];
}

function sale_status_payload_from_row(array $row): array {
    $rawStatus = strtolower(trim((string)($row['sale_status'] ?? '')));
    if ($rawStatus !== '') {
        return [
            'sale_status' => $rawStatus,
            'status_reason' => isset($row['status_reason']) && trim((string)$row['status_reason']) !== '' ? trim((string)$row['status_reason']) : null,
            'notes' => isset($row['notes']) && trim((string)$row['notes']) !== '' ? trim((string)$row['notes']) : null,
        ];
    }
    return parse_sale_status_from_notes($row['notes'] ?? null);
}

function build_sale_status_note(string $status, string $reason, ?string $existingNotes): string {
    $parsed = parse_sale_status_from_notes($existingNotes);
    $baseNotes = $parsed['notes'] ?? null;
    $prefix = '[SALE_STATUS:' . strtolower(trim($status)) . '] ' . trim($reason);
    return $baseNotes ? ($prefix . "\n\n" . $baseNotes) : $prefix;
}

function sales_status_select_sql(string $alias = 'sales'): string {
    $prefix = $alias !== '' ? rtrim($alias, '.') . '.' : '';
    if (table_has_column('sales', 'sale_status')) {
        return $prefix . 'sale_status';
    }
    if (table_has_column('sales', 'notes')) {
        return "CASE WHEN {$prefix}notes LIKE '[SALE_STATUS:voided]%' THEN 'voided' WHEN {$prefix}notes LIKE '[SALE_STATUS:deleted]%' THEN 'deleted' ELSE 'active' END";
    }
    return "'active'";
}

function sales_active_where_sql(string $alias = 'sales'): string {
    $prefix = $alias !== '' ? rtrim($alias, '.') . '.' : '';
    if (table_has_column('sales', 'sale_status')) {
        return $prefix . "sale_status = 'active'";
    }
    if (table_has_column('sales', 'notes')) {
        return "({$prefix}notes IS NULL OR ({$prefix}notes NOT LIKE '[SALE_STATUS:voided]%' AND {$prefix}notes NOT LIKE '[SALE_STATUS:deleted]%'))";
    }
    return '1=1';
}

function ensure_void_request_table(): void {
    $conn = db();
    $sql = "CREATE TABLE IF NOT EXISTS sale_void_requests (
      id INT NOT NULL AUTO_INCREMENT,
      sale_id INT NOT NULL,
      requested_by INT NOT NULL,
      request_reason TEXT NOT NULL,
      request_status ENUM('pending','approved','rejected') NOT NULL DEFAULT 'pending',
      admin_note TEXT NULL,
      processed_by INT NULL,
      processed_at DATETIME NULL,
      created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (id),
      KEY idx_void_requests_status (request_status),
      KEY idx_void_requests_sale_id (sale_id),
      CONSTRAINT fk_void_requests_sale FOREIGN KEY (sale_id) REFERENCES sales(id) ON DELETE CASCADE,
      CONSTRAINT fk_void_requests_requested_by FOREIGN KEY (requested_by) REFERENCES users(id) ON DELETE RESTRICT,
      CONSTRAINT fk_void_requests_processed_by FOREIGN KEY (processed_by) REFERENCES users(id) ON DELETE SET NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
    if (!$conn->query($sql)) {
        respond(['success' => false, 'message' => 'Unable to prepare void request table: ' . $conn->error], 500);
    }
}

function generate_item_sku(string $prefix = 'YK'): string {
    return strtoupper($prefix) . '-' . date('YmdHis') . '-' . random_int(100, 999);
}

function log_activity(int $userId, string $action, string $details): void {
    $user = get_user_by_id($userId);
    if (!$user) return;
    $conn = db();
    $stmt = $conn->prepare('INSERT INTO activity_logs (user_id, user_name, role, action, details, logged_at) VALUES (?, ?, ?, ?, ?, NOW())');
    $stmt->bind_param('issss', $userId, $user['full_name'], $user['role'], $action, $details);
    $stmt->execute();
}

function get_currency_settings(): array {
    $conn = db();
    $result = $conn->query("SELECT currency_symbol, currency_code, currency_name, updated_at FROM store_settings WHERE id = 1 LIMIT 1");
    $row = $result ? $result->fetch_assoc() : null;
    if (!$row) {
        return [
            'currency_symbol' => '₱',
            'currency_code' => 'PHP',
            'currency_name' => 'Philippine Peso',
            'updated_at' => null,
        ];
    }
    return $row;
}

function valid_date(string $value): bool {
    if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
        return false;
    }
    [$y, $m, $d] = array_map('intval', explode('-', $value));
    return checkdate($m, $d, $y);
}

function as_float(string $value): float { return (float)$value; }
function as_int(string $value): int { return (int)$value; }
