<?php

declare(strict_types=1);

require_once "../db.php";
require_once "./customer_auth_common.php";

error_reporting(E_ERROR | E_PARSE);
ini_set("display_errors", "0");

const CUSTOMER_MAX_OTP_ATTEMPTS = 3;

if (($_SERVER["REQUEST_METHOD"] ?? "") !== "POST") {
    customerAuthJsonResponse(405, [
        "success" => false,
        "message" => "Method not allowed",
    ]);
}

$input = customerAuthReadJsonInput();
$email = strtolower(customerAuthGetInputValue($input, "email"));
$otpCode = customerAuthGetInputValue($input, "otpCode");
$otpToken = customerAuthGetInputValue($input, "otpToken");
$fullName = customerAuthGetInputValue($input, "fullName", "name");
$password = customerAuthGetInputValue($input, "password");
$mode = strtolower(customerAuthGetInputValue($input, "mode", "authMode"));

if ($mode === "") {
    $mode = "signup";
}

if (!in_array($mode, ["signup"], true)) {
    customerAuthJsonResponse(422, [
        "success" => false,
        "message" => "OTP verification is available only for signup.",
    ]);
}

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    customerAuthJsonResponse(422, [
        "success" => false,
        "message" => "Please enter a valid email address.",
    ]);
}

if (!preg_match('/^\d{6}$/', $otpCode)) {
    customerAuthJsonResponse(422, [
        "success" => false,
        "message" => "Verification code must be 6 digits.",
    ]);
}

if ($otpToken === "") {
    customerAuthJsonResponse(422, [
        "success" => false,
        "message" => "Verification session token is missing.",
    ]);
}

if (strlen($password) < 8) {
    customerAuthJsonResponse(422, [
        "success" => false,
        "message" => "Password must be at least 8 characters.",
    ]);
}

customerAuthEnsureOtpTable($conn);

$stmt = $conn->prepare(
    "SELECT id, full_name, otp_hash, failed_attempts, expires_at, verified_at, used_at
     FROM customer_email_otps
     WHERE email = ? AND otp_token = ?
     ORDER BY id DESC
     LIMIT 1"
);

if (!$stmt) {
    customerAuthJsonResponse(500, [
        "success" => false,
        "message" => "Unable to prepare verification lookup.",
    ]);
}

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

if (!$row) {
    customerAuthJsonResponse(404, [
        "success" => false,
        "message" => "Verification session not found. Please request a new code.",
    ]);
}

$otpRowId = (int) $row["id"];

if (!empty($row["used_at"])) {
    customerAuthJsonResponse(409, [
        "success" => false,
        "message" => "This verification session is no longer active. Please request a new code.",
    ]);
}

if (!empty($row["verified_at"])) {
    customerAuthJsonResponse(409, [
        "success" => false,
        "message" => "This verification session is already used.",
    ]);
}

$expiresTimestamp = strtotime((string) ($row["expires_at"] ?? ""));
if ($expiresTimestamp === false || $expiresTimestamp < time()) {
    $expireStmt = $conn->prepare("UPDATE customer_email_otps SET used_at = NOW() WHERE id = ?");
    if ($expireStmt) {
        $expireStmt->bind_param("i", $otpRowId);
        $expireStmt->execute();
        $expireStmt->close();
    }

    customerAuthJsonResponse(410, [
        "success" => false,
        "message" => "Verification code expired. Please request a new code.",
    ]);
}

$failedAttempts = (int) ($row["failed_attempts"] ?? 0);
if ($failedAttempts >= CUSTOMER_MAX_OTP_ATTEMPTS) {
    $lockStmt = $conn->prepare("UPDATE customer_email_otps SET used_at = NOW() WHERE id = ?");
    if ($lockStmt) {
        $lockStmt->bind_param("i", $otpRowId);
        $lockStmt->execute();
        $lockStmt->close();
    }

    customerAuthJsonResponse(409, [
        "success" => false,
        "message" => "Maximum verification attempts reached. Please request a new code.",
    ]);
}

$expectedHash = (string) $row["otp_hash"];
$receivedHash = hash("sha256", $otpCode);
if (!hash_equals($expectedHash, $receivedHash)) {
    $nextAttempt = $failedAttempts + 1;
    $remainingAttempts = CUSTOMER_MAX_OTP_ATTEMPTS - $nextAttempt;

    if ($nextAttempt >= CUSTOMER_MAX_OTP_ATTEMPTS) {
        $failStmt = $conn->prepare(
            "UPDATE customer_email_otps
             SET failed_attempts = ?, used_at = NOW()
             WHERE id = ?"
        );
        if ($failStmt) {
            $failStmt->bind_param("ii", $nextAttempt, $otpRowId);
            $failStmt->execute();
            $failStmt->close();
        }

        customerAuthJsonResponse(401, [
            "success" => false,
            "message" => "Invalid verification code. Maximum attempts reached.",
        ]);
    }

    $attemptStmt = $conn->prepare(
        "UPDATE customer_email_otps SET failed_attempts = ? WHERE id = ?"
    );
    if ($attemptStmt) {
        $attemptStmt->bind_param("ii", $nextAttempt, $otpRowId);
        $attemptStmt->execute();
        $attemptStmt->close();
    }

    customerAuthJsonResponse(401, [
        "success" => false,
        "message" => "Invalid verification code. {$remainingAttempts} attempt(s) remaining.",
    ]);
}

$userStmt = $conn->prepare("SELECT id, name, email, role FROM users WHERE email = ? LIMIT 1");
if (!$userStmt) {
    customerAuthJsonResponse(500, [
        "success" => false,
        "message" => "Failed to prepare user lookup.",
    ]);
}

$userStmt->bind_param("s", $email);
$userStmt->execute();
$userResult = $userStmt->get_result();
$user = $userResult ? $userResult->fetch_assoc() : null;
$userStmt->close();

$resolvedName = trim($fullName);
if ($resolvedName === "") {
    $storedOtpName = trim((string) ($row["full_name"] ?? ""));
    if ($storedOtpName !== "") {
        $resolvedName = $storedOtpName;
    }
}
if ($resolvedName === "") {
    $resolvedName = strstr($email, "@", true) ?: "User";
}

if ($user) {
    customerAuthJsonResponse(409, [
        "success" => false,
        "message" => "Account already exists. Please login instead.",
    ]);
}

if ($resolvedName === "") {
    customerAuthJsonResponse(422, [
        "success" => false,
        "message" => "Full name is required for signup.",
    ]);
}

$passwordHash = password_hash($password, PASSWORD_DEFAULT);
$userRole = "user";

$createStmt = $conn->prepare(
    "INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)"
);
if (!$createStmt) {
    customerAuthJsonResponse(500, [
        "success" => false,
        "message" => "Failed to create account.",
    ]);
}

$createStmt->bind_param("ssss", $resolvedName, $email, $passwordHash, $userRole);
if (!$createStmt->execute()) {
    $errorCode = (int) $createStmt->errno;
    $createStmt->close();

    if ($errorCode === 1062) {
        customerAuthJsonResponse(409, [
            "success" => false,
            "message" => "Account already exists. Please login instead.",
        ]);
    }

    customerAuthJsonResponse(500, [
        "success" => false,
        "message" => "Failed to create account.",
    ]);
}

$userId = (int) $conn->insert_id;
$createStmt->close();
$userName = $resolvedName;

$markStmt = $conn->prepare(
    "UPDATE customer_email_otps
     SET verified_at = NOW(), used_at = NOW(), otp_hash = '', failed_attempts = 0
     WHERE id = ?"
);
if ($markStmt) {
    $markStmt->bind_param("i", $otpRowId);
    $markStmt->execute();
    $markStmt->close();
}

try {
    $session = customerAuthCreateSession($conn, $userId);
} catch (RuntimeException $exception) {
    customerAuthJsonResponse(500, [
        "success" => false,
        "message" => "Unable to create login session.",
    ]);
}

customerAuthJsonResponse(200, [
    "success" => true,
    "message" => "Authentication successful.",
    "token" => $session["token"],
    "expiresAt" => $session["expiresAt"],
    "expiresInSeconds" => $session["expiresInSeconds"],
    "user" => [
        "id" => $userId,
        "name" => $userName,
        "email" => $email,
        "role" => $userRole ?? "user",
    ],
]);
