<?php

declare(strict_types=1);

namespace App\Core;

use App\Helpers\UrlHelper;

/**
 * Gerenciador de Sessões
 */
class Session
{
    private bool $started = false;

    public function __construct(array $config = [])
    {
        if (!$this->started && session_status() === PHP_SESSION_NONE) {
            // IMPORTANTE: Todas as configurações devem ser feitas ANTES de session_start()

            // Detecta o base path primeiro
            $cookiePath = $this->getCookiePath();
            $sameSite = $config['same_site'] ?? 'Lax';
            $isSecure = $config['secure'] ?? false;
            $lifetime = (int) ($config['lifetime'] ?? 7200);

            // Usa session_set_cookie_params para configurar o cookie corretamente
            // Isso garante que as configurações sejam aplicadas antes de iniciar a sessão
            session_set_cookie_params([
                'lifetime' => $lifetime,
                'path' => $cookiePath,
                'domain' => '', // Deixa vazio para usar o domínio atual
                'secure' => $isSecure,
                'httponly' => true,
                'samesite' => $sameSite
            ]);

            // Configurações adicionais via ini_set (para garantir)
            ini_set('session.cookie_lifetime', (string) $lifetime);
            ini_set('session.cookie_path', $cookiePath);
            ini_set('session.cookie_httponly', '1');
            ini_set('session.use_only_cookies', '1');
            ini_set('session.cookie_samesite', $sameSite);

            if ($isSecure) {
                ini_set('session.cookie_secure', '1');
            }

            // Gerar nome de sessão único baseado no path para evitar conflitos entre diferentes instalações
            $sessionName = $this->generateSessionName($config);
            session_name($sessionName);

            // Log para debug (sempre logar para diagnosticar problemas)
            $basePath = $this->detectBasePath();
            error_log("[Session::__construct] Configurando sessão:");
            error_log("  - Nome da sessão: {$sessionName}");
            error_log("  - Cookie path: {$cookiePath}");
            error_log("  - Base path detectado: {$basePath}");
            error_log("  - SCRIPT_NAME: " . ($_SERVER['SCRIPT_NAME'] ?? 'N/A'));
            error_log("  - REQUEST_URI: " . ($_SERVER['REQUEST_URI'] ?? 'N/A'));

            // Garantir que a sessão seja iniciada ANTES de qualquer processamento
            if (session_status() === PHP_SESSION_NONE) {
                @session_start();
            }

            $this->started = true;
        }
    }

    /**
     * Gera um nome de sessão único baseado no path da aplicação
     */
    private function generateSessionName(array $config): string
    {
        $baseName = $config['name'] ?? 'Erp_Aurion_SESSION';

        // Usa o UrlHelper para detectar o base path (já tem lógica robusta)
        $basePath = $this->detectBasePath();

        // Se o basePath for diferente de vazio, adiciona hash ao nome da sessão
        // Isso garante que cada instalação em subdiretório tenha sua própria sessão
        if ($basePath !== '' && $basePath !== '/') {
            $pathHash = md5($basePath);
            return $baseName . '_' . substr($pathHash, 0, 8);
        }

        return $baseName;
    }

    /**
     * Obtém o path do cookie baseado no diretório atual
     */
    private function getCookiePath(): string
    {
        // Detecta o base path
        $basePath = $this->detectBasePath();

        // Se há base path, usa ele como path do cookie (com barra final)
        if ($basePath !== '' && $basePath !== '/') {
            return rtrim($basePath, '/') . '/';
        }

        // Padrão: raiz
        return '/';
    }

    /**
     * Detecta o base path da aplicação
     */
    private function detectBasePath(): string
    {
        // Prioridade 1: Tenta usar o UrlHelper se disponível (melhor método)
        if (class_exists('\App\Helpers\UrlHelper')) {
            try {
                $basePath = UrlHelper::basePath();
                if ($basePath !== '') {
                    return $basePath;
                }
            } catch (\Exception $e) {
                error_log("[Session::detectBasePath] Erro ao usar UrlHelper: " . $e->getMessage());
            }
        }

        // Prioridade 2: Detecta manualmente pelo SCRIPT_NAME (mais confiável)
        $scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
        if ($scriptName) {
            $basePath = str_replace('\\', '/', dirname($scriptName));
            $basePath = rtrim($basePath, '/');
            // Se não for raiz e não estiver vazio, retorna
            if ($basePath !== '/' && $basePath !== '' && $basePath !== '.') {
                return $basePath;
            }
        }

        // Prioridade 3: Tenta extrair do REQUEST_URI
        $requestUri = $_SERVER['REQUEST_URI'] ?? '';
        if ($requestUri) {
            $uriPath = parse_url($requestUri, PHP_URL_PATH);
            if ($uriPath && $uriPath !== '/') {
                $parts = explode('/', trim($uriPath, '/'));
                if (!empty($parts[0]) && !preg_match('/\.(php|html|htm|js|css|jpg|png|gif)$/i', $parts[0])) {
                    return '/' . $parts[0];
                }
            }
        }

        // Fallback: retorna vazio (será tratado como raiz)
        return '';
    }

    /**
     * Define um valor na sessão
     */
    public function set(string $key, $value): void
    {
        $_SESSION[$key] = $value;
    }

    /**
     * Obtém um valor da sessão
     */
    public function get(string $key, $default = null)
    {
        return $_SESSION[$key] ?? $default;
    }

    /**
     * Verifica se uma chave existe na sessão
     */
    public function has(string $key): bool
    {
        return isset($_SESSION[$key]);
    }

    /**
     * Remove um valor da sessão
     */
    public function remove(string $key): void
    {
        unset($_SESSION[$key]);
    }

    /**
     * Limpa toda a sessão
     */
    public function clear(): void
    {
        $_SESSION = [];
    }

    /**
     * Destrói a sessão
     */
    public function destroy(): void
    {
        if ($this->started) {
            session_destroy();
            $_SESSION = [];
            $this->started = false;
        }
    }

    /**
     * Regenera o ID da sessão
     */
    public function regenerate(): void
    {
        if ($this->started) {
            session_regenerate_id(true);
        }
    }

    /**
     * Retorna o ID da sessão
     */
    public function getId(): string
    {
        return session_id();
    }

    /**
     * Define mensagem flash
     */
    public function flash(string $key, $value): void
    {
        $_SESSION['_flash'][$key] = $value;
    }

    /**
     * Obtém mensagem flash (é removida após leitura)
     */
    public function getFlash(string $key, $default = null)
    {
        $value = $_SESSION['_flash'][$key] ?? $default;
        unset($_SESSION['_flash'][$key]);
        return $value;
    }

    /**
     * Verifica se há mensagem flash
     */
    public function hasFlash(string $key): bool
    {
        return isset($_SESSION['_flash'][$key]);
    }

    /**
     * Define o usuário autenticado
     */
    public function setUser(array $user): void
    {
        $this->set('user', $user);
        $userId = $user['id'] ?? null;
        // Garantir que user_id seja sempre um inteiro
        $this->set('user_id', $userId !== null ? (int) $userId : null);
    }

    /**
     * Obtém o usuário autenticado
     */
    public function getUser(): ?array
    {
        return $this->get('user');
    }

    /**
     * Verifica se há usuário autenticado
     */
    public function isAuthenticated(): bool
    {
        return $this->has('user_id') && $this->get('user_id') !== null;
    }

    /**
     * Remove o usuário autenticado
     */
    public function logout(): void
    {
        $this->remove('user');
        $this->remove('user_id');
        $this->remove('tenant_id');
        $this->remove('company_id');
    }

    /**
     * Define o tenant atual
     */
    public function setTenant(int $tenantId): void
    {
        $this->set('tenant_id', $tenantId);
    }

    /**
     * Obtém o tenant atual
     */
    public function getTenantId(): ?int
    {
        return $this->get('tenant_id');
    }

    /**
     * Define a empresa atual
     */
    public function setCompany(int $companyId): void
    {
        $this->set('company_id', $companyId);
    }

    /**
     * Obtém a empresa atual
     */
    public function getCompanyId(): ?int
    {
        return $this->get('company_id');
    }
}

