<?php

/**
 * Auth - Authentication handler for session and API token
 *
 * Supports:
 * - Session-based authentication (for UI)
 * - API token authentication (for API calls)
 */

declare(strict_types=1);

namespace Notifier;

class Auth
{
    private string $password;
    private string $apiToken;
    private int $sessionLifetime;
    private bool $sessionStarted = false;

    public function __construct(array $config)
    {
        $authConfig = $config['auth'] ?? $config;
        $this->password = $authConfig['password'] ?? '';
        $this->apiToken = $authConfig['api_token'] ?? '';
        $sessionLifetime = $config['session_lifetime'] ?? $authConfig['session_lifetime'] ?? (90 * 24 * 60 * 60);
        $this->sessionLifetime = max(0, (int) $sessionLifetime);
    }

    /**
     * Start the session with configured lifetime.
     */
    public function startSession(): void
    {
        if ($this->sessionStarted) {
            return;
        }

        // Set server-side session lifetime to match cookie lifetime.
        \ini_set('session.gc_maxlifetime', (string) $this->sessionLifetime);
        \ini_set('session.cookie_lifetime', (string) $this->sessionLifetime);

        if (\session_status() === PHP_SESSION_ACTIVE) {
            // Session already started (e.g., auto_start); refresh cookie lifetime.
            $this->refreshSessionCookie();
            $this->sessionStarted = true;
            return;
        }

        \session_set_cookie_params($this->getCookieParams());

        \session_start();
        $this->sessionStarted = true;
    }

    private function getCookieParams(): array
    {
        return [
            'lifetime' => $this->sessionLifetime,
            'path' => '/',
            'secure' => $this->isSecureRequest(),
            'httponly' => true,
            'samesite' => 'Lax',
        ];
    }

    private function refreshSessionCookie(): void
    {
        $params = $this->getCookieParams();
        \setcookie(
            \session_name(),
            \session_id(),
            [
                'expires' => \time() + $this->sessionLifetime,
                'path' => $params['path'],
                'secure' => $params['secure'],
                'httponly' => $params['httponly'],
                'samesite' => $params['samesite'],
            ]
        );
    }

    private function isSecureRequest(): bool
    {
        if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
            return true;
        }

        return isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https';
    }

    /**
     * Check if user is authenticated (via session or API token).
     */
    public function isAuthenticated(): bool
    {
        // Check API token first (for API requests)
        if ($this->checkApiToken()) {
            return true;
        }

        // Check session (for UI requests)
        return $this->checkSession();
    }

    /**
     * Check if the request has a valid API token.
     */
    public function checkApiToken(): bool
    {
        $token = $_SERVER['HTTP_X_API_TOKEN'] ?? '';

        if (empty($token) || empty($this->apiToken)) {
            return false;
        }

        return \hash_equals($this->apiToken, $token);
    }

    /**
     * Check if the session is authenticated.
     */
    public function checkSession(): bool
    {
        $this->startSession();
        return !empty($_SESSION['authenticated']) && $_SESSION['authenticated'] === true;
    }

    /**
     * Attempt to login with password.
     */
    public function login(string $password): bool
    {
        if (empty($this->password)) {
            return false;
        }

        $isValid = false;

        // If stored password looks like a hash, use password_verify
        if (\strlen($this->password) >= 60 && \str_starts_with($this->password, '$')) {
            $isValid = \password_verify($password, $this->password);
        } else {
            // Plaintext comparison
            $isValid = \hash_equals($this->password, $password);
        }

        if ($isValid) {
            $this->startSession();
            $_SESSION['authenticated'] = true;
            $_SESSION['login_time'] = \time();
            \session_regenerate_id(true);
            return true;
        }

        return false;
    }

    /**
     * Logout and destroy session.
     */
    public function logout(): void
    {
        $this->startSession();
        $_SESSION = [];

        if (\ini_get('session.use_cookies')) {
            $params = \session_get_cookie_params();
            \setcookie(
                \session_name(),
                '',
                \time() - 42000,
                $params['path'],
                $params['domain'],
                $params['secure'],
                $params['httponly']
            );
        }

        \session_destroy();
    }

    /**
     * Require authentication or send 401 response.
     */
    public function requireAuth(bool $jsonResponse = true): void
    {
        if ($this->isAuthenticated()) {
            return;
        }

        if ($jsonResponse) {
            \http_response_code(401);
            \header('Content-Type: application/json');
            echo \json_encode([
                'success' => false,
                'error' => 'Unauthorized. Provide X-API-Token header or login.',
            ]);
            exit;
        }

        \header('Location: /notifier/login/');
        exit;
    }

    /**
     * Generate a secure random API token.
     */
    public static function generateApiToken(int $length = 32): string
    {
        return 'nf_api_' . \bin2hex(\random_bytes($length));
    }

    /**
     * Hash a password for storage.
     */
    public static function hashPassword(string $password): string
    {
        return \password_hash($password, PASSWORD_DEFAULT);
    }
}
