<?php
declare(strict_types=1);

require_once __DIR__ . '/session-guard.php';
session_guard_start_session();
header('Content-Type: application/json; charset=utf-8');

const AUTH_FAILURE_MESSAGE = 'Identifiant ou mot de passe incorrect.';
const AUTH_ATTEMPTS_MAX = 5;
const AUTH_ATTEMPTS_WINDOW = 900;
const AUTH_FAILURE_DELAY_SECONDS = 1;

function respond(int $statusCode, array $data): void
{
    http_response_code($statusCode);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function auth_attempts_file(): string
{
    return dirname(__DIR__) . '/privatedata/login-attempts.json';
}

function auth_delay_and_fail(): void
{
    sleep(AUTH_FAILURE_DELAY_SECONDS);
    respond(401, [
        'ok' => false,
        'message' => AUTH_FAILURE_MESSAGE
    ]);
}

function auth_attempts_read(): array
{
    $file = auth_attempts_file();
    if (!is_file($file)) {
        return [];
    }

    $data = json_decode((string) file_get_contents($file), true);

    return is_array($data) ? $data : [];
}

function auth_attempts_write(array $attempts): void
{
    file_put_contents(
        auth_attempts_file(),
        json_encode($attempts, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT),
        LOCK_EX
    );
}

function auth_attempts_cleanup(array $attempts, int $now): array
{
    foreach ($attempts as $key => $entry) {
        $lastFailedAt = (int) ($entry['last_failed_at'] ?? 0);
        if ($lastFailedAt <= 0 || ($now - $lastFailedAt) >= AUTH_ATTEMPTS_WINDOW) {
            unset($attempts[$key]);
        }
    }

    return $attempts;
}

function auth_client_ip(): string
{
    $remoteAddr = trim((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
    return $remoteAddr !== '' ? $remoteAddr : 'ip-inconnue';
}

function auth_login_alert_email(): string
{
    return 'gcse.webdigital@gmail.com';
}

function auth_send_successful_login_email(string $clientId, string $clientName): void
{
    $to = trim(auth_login_alert_email());
    if ($to === '') {
        return;
    }

    $ip = auth_client_ip();
    $userAgent = trim((string) ($_SERVER['HTTP_USER_AGENT'] ?? 'inconnu'));

    $subject = 'Connexion Chrono Party';

    $message =
        "Nouvelle connexion Chrono Party\n\n" .
        "Client ID : " . $clientId . "\n" .
        "Client : " . $clientName . "\n" .
        "Date : " . date('Y-m-d') . "\n" .
        "Heure : " . date('H:i:s') . "\n" .
        "IP : " . $ip . "\n" .
        "User-Agent : " . $userAgent . "\n";

    $headers = [];
    $headers[] = 'From: contact@chrono-party.fr';
    $headers[] = 'Reply-To: contact@chrono-party.fr';
    $headers[] = 'Content-Type: text/plain; charset=UTF-8';

    @mail($to, $subject, $message, implode("\r\n", $headers));
}

function auth_attempt_key(string $login): string
{
    return sha1(auth_client_ip() . '|' . $login);
}

function auth_attempts_is_blocked(array $attempts, string $key, int $now): bool
{
    $entry = $attempts[$key] ?? null;
    if (!is_array($entry)) {
        return false;
    }

    $count = (int) ($entry['count'] ?? 0);
    $lastFailedAt = (int) ($entry['last_failed_at'] ?? 0);

    return $count >= AUTH_ATTEMPTS_MAX
        && $lastFailedAt > 0
        && ($now - $lastFailedAt) < AUTH_ATTEMPTS_WINDOW;
}

function auth_attempts_register_failure(array $attempts, string $key, int $now): array
{
    $entry = $attempts[$key] ?? null;

    if (!is_array($entry)) {
        $attempts[$key] = [
            'count' => 1,
            'first_failed_at' => $now,
            'last_failed_at' => $now,
        ];

        return $attempts;
    }

    $firstFailedAt = (int) ($entry['first_failed_at'] ?? 0);
    if ($firstFailedAt <= 0 || ($now - $firstFailedAt) >= AUTH_ATTEMPTS_WINDOW) {
        $attempts[$key] = [
            'count' => 1,
            'first_failed_at' => $now,
            'last_failed_at' => $now,
        ];

        return $attempts;
    }

    $attempts[$key] = [
        'count' => (int) ($entry['count'] ?? 0) + 1,
        'first_failed_at' => $firstFailedAt,
        'last_failed_at' => $now,
    ];

    return $attempts;
}

$configFile = dirname(__DIR__) . '/privatedata/client-config.php';

if (!is_file($configFile)) {
    respond(500, [
        'ok' => false,
        'message' => 'Configuration introuvable.'
    ]);
}

$config = require $configFile;

$expectedLogin = trim((string) ($config['login'] ?? ''));
$expectedPasswordHash = trim((string) ($config['password_hash'] ?? ''));
$expectedPassword = (string) ($config['password'] ?? '');
$clientId = (string) ($config['client_id'] ?? '');
$clientName = (string) ($config['client_name'] ?? 'Client');
$accessEnd = (string) ($config['access_end'] ?? '');

$raw = file_get_contents('php://input');
$data = json_decode($raw ?: '', true);

$login = trim((string) ($data['login'] ?? ''));
$password = trim((string) ($data['password'] ?? ''));

if ($login === '') {
    respond(400, [
        'ok' => false,
        'message' => 'Veuillez saisir votre identifiant.'
    ]);
}

if ($password === '') {
    respond(400, [
        'ok' => false,
        'message' => 'Veuillez saisir votre mot de passe.'
    ]);
}

if ($expectedLogin === '') {
    respond(500, [
        'ok' => false,
        'message' => 'Identifiant non configuré.'
    ]);
}

if ($expectedPasswordHash === '' && $expectedPassword === '') {
    respond(500, [
        'ok' => false,
        'message' => 'Mot de passe non configuré.'
    ]);
}

if ($accessEnd !== '' && date('Y-m-d') > $accessEnd) {
    respond(403, [
        'ok' => false,
        'message' => 'Accès expiré.'
    ]);
}

$now = time();
$attemptKey = auth_attempt_key($login);
$attempts = auth_attempts_cleanup(auth_attempts_read(), $now);

if (auth_attempts_is_blocked($attempts, $attemptKey, $now)) {
    auth_attempts_write($attempts);
    auth_delay_and_fail();
}

$loginOk = hash_equals($expectedLogin, $login);
$passwordOk = false;

if ($loginOk) {
    if ($expectedPasswordHash !== '') {
        $passwordOk = password_verify($password, $expectedPasswordHash);
    } else {
        $passwordOk = hash_equals($expectedPassword, $password);
    }
}

if (!$loginOk || !$passwordOk) {
    $attempts = auth_attempts_register_failure($attempts, $attemptKey, $now);
    auth_attempts_write($attempts);
    auth_delay_and_fail();
}

unset($attempts[$attemptKey]);
auth_attempts_write($attempts);

session_regenerate_id(true);

$_SESSION['logged_in'] = true;
$_SESSION['client_id'] = $clientId;
$_SESSION['client_name'] = $clientName;
$_SESSION['access_end'] = $accessEnd;
if (!session_guard_issue_remember_me_token($clientId, $clientName, $accessEnd)) {
    $_SESSION = [];
    session_write_close();
    respond(500, [
        'ok' => false,
        'message' => 'Connexion impossible : la mémorisation du navigateur a échoué.'
    ]);
}

auth_send_successful_login_email($clientId, $clientName);

session_write_close();

respond(200, [
    'ok' => true,
    'redirect' => '../'
]);
