<?php

declare(strict_types=1);

function customerAuthJsonResponse(int $statusCode, array $payload): void
{
    http_response_code($statusCode);
    echo json_encode($payload);
    exit;
}

function customerAuthReadJsonInput(): array
{
    $raw = file_get_contents("php://input");
    if (!is_string($raw) || $raw === "") {
        return [];
    }

    $decoded = json_decode($raw, true);
    return is_array($decoded) ? $decoded : [];
}

function customerAuthGetInputValue(array $input, string $key, string $fallbackKey = ""): string
{
    if (isset($_POST[$key])) {
        return trim((string) $_POST[$key]);
    }
    if ($fallbackKey !== "" && isset($_POST[$fallbackKey])) {
        return trim((string) $_POST[$fallbackKey]);
    }
    if (isset($input[$key])) {
        return trim((string) $input[$key]);
    }
    if ($fallbackKey !== "" && isset($input[$fallbackKey])) {
        return trim((string) $input[$fallbackKey]);
    }
    return "";
}

function customerAuthEnsureOtpTable(mysqli $conn): void
{
    $conn->query(
        "CREATE TABLE IF NOT EXISTS customer_email_otps (
            id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            email VARCHAR(190) NOT NULL,
            full_name VARCHAR(150) NULL,
            otp_hash CHAR(64) NOT NULL,
            otp_token VARCHAR(96) NOT NULL UNIQUE,
            failed_attempts TINYINT UNSIGNED NOT NULL DEFAULT 0,
            expires_at DATETIME NOT NULL,
            verified_at DATETIME NULL,
            used_at DATETIME NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_customer_otps_email_created (email, created_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
    );
}

function customerAuthEnsureSessionsTable(mysqli $conn): void
{
    $conn->query(
        "CREATE TABLE IF NOT EXISTS user_sessions (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            user_id INT(11) NOT NULL,
            token_hash CHAR(64) NOT NULL UNIQUE,
            expires_at DATETIME NOT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            last_used_at DATETIME NULL,
            revoked_at DATETIME NULL,
            INDEX idx_user_sessions_user (user_id),
            INDEX idx_user_sessions_expiry (expires_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
    );
}

function customerAuthExtractBearerToken(): string
{
    $header = "";
    if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
        $header = trim((string) $_SERVER["HTTP_AUTHORIZATION"]);
    } elseif (function_exists("getallheaders")) {
        $headers = getallheaders();
        if (is_array($headers)) {
            $header = trim((string) ($headers["Authorization"] ?? $headers["authorization"] ?? ""));
        }
    }

    if ($header === "") {
        return "";
    }

    if (stripos($header, "Bearer ") !== 0) {
        return "";
    }

    return trim(substr($header, 7));
}

function customerAuthCreateSession(mysqli $conn, int $userId, int $ttlSeconds = 2592000): array
{
    customerAuthEnsureSessionsTable($conn);

    $rawToken = bin2hex(random_bytes(48));
    $tokenHash = hash("sha256", $rawToken);
    $expiresAt = date("Y-m-d H:i:s", time() + max(300, $ttlSeconds));

    $stmt = $conn->prepare(
        "INSERT INTO user_sessions (user_id, token_hash, expires_at) VALUES (?, ?, ?)"
    );
    if (!$stmt) {
        throw new RuntimeException("Failed to prepare session insert.");
    }

    $stmt->bind_param("iss", $userId, $tokenHash, $expiresAt);
    if (!$stmt->execute()) {
        $error = $stmt->error;
        $stmt->close();
        throw new RuntimeException("Failed to create session: " . $error);
    }
    $stmt->close();

    return [
        "token" => $rawToken,
        "expiresAt" => $expiresAt,
        "expiresInSeconds" => max(300, $ttlSeconds),
    ];
}

function customerAuthGetSessionUser(mysqli $conn, string $rawToken): ?array
{
    if ($rawToken === "") {
        return null;
    }

    customerAuthEnsureSessionsTable($conn);
    $tokenHash = hash("sha256", $rawToken);

    $stmt = $conn->prepare(
        "SELECT s.id AS session_id,
                s.user_id,
                s.expires_at,
                u.name,
                u.email,
                u.role
         FROM user_sessions s
         INNER JOIN users u ON u.id = s.user_id
         WHERE s.token_hash = ?
           AND s.revoked_at IS NULL
           AND s.expires_at > NOW()
         LIMIT 1"
    );
    if (!$stmt) {
        return null;
    }

    $stmt->bind_param("s", $tokenHash);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result ? $result->fetch_assoc() : null;
    $stmt->close();

    if (!$row) {
        return null;
    }

    $touchStmt = $conn->prepare("UPDATE user_sessions SET last_used_at = NOW() WHERE id = ?");
    if ($touchStmt) {
        $sessionId = (int) $row["session_id"];
        $touchStmt->bind_param("i", $sessionId);
        $touchStmt->execute();
        $touchStmt->close();
    }

    return [
        "session_id" => (int) $row["session_id"],
        "user_id" => (int) $row["user_id"],
        "name" => (string) ($row["name"] ?? ""),
        "email" => (string) ($row["email"] ?? ""),
        "role" => (string) ($row["role"] ?? "user"),
        "expires_at" => (string) ($row["expires_at"] ?? ""),
    ];
}

function customerAuthRequireSession(mysqli $conn): array
{
    $token = customerAuthExtractBearerToken();
    $session = customerAuthGetSessionUser($conn, $token);

    if (!$session) {
        customerAuthJsonResponse(401, [
            "success" => false,
            "message" => "Authentication required.",
        ]);
    }

    return $session;
}

function customerAuthRevokeSession(mysqli $conn, string $rawToken): bool
{
    if ($rawToken === "") {
        return false;
    }

    customerAuthEnsureSessionsTable($conn);
    $tokenHash = hash("sha256", $rawToken);

    $stmt = $conn->prepare(
        "UPDATE user_sessions
         SET revoked_at = NOW()
         WHERE token_hash = ?
           AND revoked_at IS NULL"
    );
    if (!$stmt) {
        return false;
    }

    $stmt->bind_param("s", $tokenHash);
    $stmt->execute();
    $affected = $stmt->affected_rows;
    $stmt->close();

    return $affected > 0;
}

function customerAuthGenerateOtpCode(): string
{
    return str_pad((string) random_int(0, 999999), 6, "0", STR_PAD_LEFT);
}

