<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\TenantManager;
use App\Helpers\UrlHelper;
use Exception;

/**
 * Controller de Autenticação
 * Gerencia login, registro e logout
 */
class AuthController extends BaseController
{
    /**
     * Exibe formulário de login
     */
    public function showLogin(): void
    {
        if ($this->session->isAuthenticated()) {
            $this->redirect('/dashboard');
            return;
        }

        // Busca logo da empresa se houver tenant identificado
        $companyLogo = null;
        $companyName = null;

        if ($this->db) {
            try {
                // Verificar se a tabela empresas existe
                $stmt = $this->db->query("SHOW TABLES LIKE 'empresas'");
                $tabelaExiste = $stmt->fetch() !== false;

                if ($tabelaExiste) {
                    // Busca logo da empresa com id = 1 na tabela empresas
                    $stmt = $this->db->prepare("
                        SELECT logo, razao_social, nome_fantasia
                        FROM empresas
                        WHERE id = 1
                        LIMIT 1
                    ");
                    $stmt->execute();
                    $empresa = $stmt->fetch(\PDO::FETCH_ASSOC);

                    if ($empresa) {
                        // Verificar se logo não é NULL e não está vazio
                        $logoValue = $empresa['logo'] ?? null;

                        if (!empty($logoValue) && trim($logoValue) !== '' && strtolower($logoValue) !== 'null') {
                            $companyLogo = trim($logoValue);
                            $companyName = $empresa['razao_social'] ?? $empresa['nome_fantasia'] ?? null;

                            error_log("Logo da empresa encontrada: " . $companyLogo);
                        } else {
                            error_log("Logo da empresa está vazia ou NULL. Valor: " . var_export($logoValue, true));
                        }
                    } else {
                        error_log("Empresa com id = 1 não encontrada na tabela empresas");
                    }
                } else {
                    error_log("Tabela 'empresas' não existe no banco de dados");
                }
            } catch (\Exception $e) {
                // Log do erro para debug
                error_log("Erro ao buscar logo da empresa: " . $e->getMessage());
                error_log("Stack trace: " . $e->getTraceAsString());
            }
        } else {
            error_log("Conexão com banco de dados não disponível no showLogin");
        }

        $this->view('auth/login', [
            'companyLogo' => $companyLogo,
            'companyName' => $companyName
        ]);
    }

    /**
     * Processa login
     */
    public function login(): void
    {
        // Garantir que a sessão esteja iniciada antes de processar o login
        if (session_status() === PHP_SESSION_NONE) {
            @session_start();
        }

        // Garantir que os cookies sejam enviados em requisições AJAX
        if ($this->request->isAjax()) {
            // Configurar headers para garantir que cookies sejam aceitos
            $origin = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_HOST'] ?? '*';
            header('Access-Control-Allow-Credentials: true');
            if ($origin !== '*') {
                header('Access-Control-Allow-Origin: ' . $origin);
            }
        }

        $email = trim($this->request->post('email', ''));
        $password = $this->request->post('password', '');

        error_log("[AuthController::login] Tentativa de login - Email: " . substr($email, 0, 10) . "... | Senha recebida: " . (empty($password) ? 'VAZIA' : 'PRESENTE'));
        error_log("[AuthController::login] Session ID: " . session_id());
        error_log("[AuthController::login] Session status: " . session_status());

        $errors = $this->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        if (!empty($errors)) {
            error_log("[AuthController::login] Erros de validação: " . json_encode($errors));
            $this->error('Dados inválidos', $errors);
            return;
        }

        try {
            // Verificar se há conexão com banco
            if (!$this->db) {
                error_log("[AuthController::login] ERRO: Conexão com banco de dados não disponível!");
                $this->error('Erro de conexão com o banco de dados');
                return;
            }

            // MODO SIMPLES: Se não tem DB_TENANT_PREFIX, busca direto no banco único
            if (empty($_ENV['DB_TENANT_PREFIX'])) {
                error_log("[AuthController::login] Modo SIMPLES - DB_TENANT_PREFIX vazio");

                // Primeiro, busca SEM verificar is_active para diagnosticar
                $stmtCheck = $this->db->prepare("
                    SELECT u.id, u.email, u.is_active, u.password, LENGTH(u.password) as password_length
                    FROM users u
                    WHERE u.email = :email
                    LIMIT 1
                ");
                $stmtCheck->execute(['email' => $email]);
                $userCheck = $stmtCheck->fetch();

                if ($userCheck) {
                    error_log("[AuthController::login] Usuário encontrado no banco:");
                    error_log("  - ID: {$userCheck['id']}");
                    error_log("  - Email: {$userCheck['email']}");
                    error_log("  - is_active: {$userCheck['is_active']}");
                    error_log("  - Tamanho do hash: {$userCheck['password_length']}");
                    error_log("  - Hash (primeiros 20 chars): " . substr($userCheck['password'], 0, 20) . "...");
                } else {
                    error_log("[AuthController::login] NENHUM usuário encontrado com email: {$email}");
                }

                // Busca usuário direto no banco único
                $stmt = $this->db->prepare("
                    SELECT u.*, c.id as company_id, c.name as company_name
                    FROM users u
                    LEFT JOIN companies c ON u.company_id = c.id
                    WHERE u.email = :email AND u.is_active = 1
                    LIMIT 1
                ");

                $stmt->execute(['email' => $email]);
                $userFound = $stmt->fetch();

                if (!$userFound) {
                    error_log("[AuthController::login] Usuário não encontrado OU inativo para email: {$email}");
                    $this->error('Email ou senha inválidos');
                    return;
                }

                error_log("[AuthController::login] Usuário encontrado e ativo. Verificando senha...");
                error_log("[AuthController::login] Hash no banco: " . substr($userFound['password'], 0, 30) . "...");

                $passwordVerified = password_verify($password, $userFound['password']);
                error_log("[AuthController::login] Resultado password_verify: " . ($passwordVerified ? 'SUCESSO' : 'FALHOU'));

                if (!$passwordVerified) {
                    error_log("[AuthController::login] Senha incorreta para email: {$email}");
                    error_log("[AuthController::login] Tentando verificar com hash direto (para debug)...");
                    $this->error('Email ou senha inválidos');
                    return;
                }

                // Regenerar ID da sessão ANTES de definir dados (para evitar problemas)
                $this->session->regenerate();

                // Define sessão
                $this->session->setUser([
                    'id' => (int) $userFound['id'],
                    'name' => $userFound['name'],
                    'email' => $userFound['email'],
                    'role' => $userFound['role'],
                    'company_id' => $userFound['company_id'] ? (int) $userFound['company_id'] : null
                ]);

                if ($userFound['company_id']) {
                    $this->session->setCompany((int) $userFound['company_id']);
                }

                // Log para debug - verificar se sessão foi definida corretamente
                error_log("[AuthController::login] Sessão definida - user_id: " . $this->session->get('user_id') . " | isAuthenticated: " . ($this->session->isAuthenticated() ? 'SIM' : 'NÃO'));
                error_log("[AuthController::login] Session ID após regenerate: " . session_id());

                // Salvar usuário logado (pode falhar silenciosamente se a tabela não existir)
                try {
                    $this->salvarUsuarioLogado((int) $userFound['id'], $userFound['company_id'] ? (int) $userFound['company_id'] : null);
                } catch (\Exception $e) {
                    // Logar mas não interromper o login
                    error_log("[AuthController::login] Erro ao salvar usuário logado (não crítico): " . $e->getMessage());
                }

                $this->success('Login realizado com sucesso', [
                    'redirect' => UrlHelper::url('/dashboard')
                ]);
                return;
            }

            // MODO MULTI-TENANT: Busca qual tenant pertence este email
            $masterDb = $this->tenantManager->getMasterConnection();

            // Primeiro, busca em qual tenant esse email está cadastrado
            $stmt = $masterDb->prepare("
                SELECT id, database_name, subdomain FROM tenants WHERE status = 'active'
            ");
            $stmt->execute();
            $tenants = $stmt->fetchAll();

            $userFound = null;
            $tenantFound = null;

            // Procura o usuário em cada tenant
            foreach ($tenants as $tenant) {
                try {
                    $this->tenantManager->setCurrentTenant($tenant);
                    $tenantDb = $this->tenantManager->getTenantConnection();

                    $stmt = $tenantDb->prepare("
                        SELECT u.*, c.id as company_id, c.name as company_name
                        FROM users u
                        LEFT JOIN companies c ON u.company_id = c.id
                        WHERE u.email = :email AND u.is_active = 1
                        LIMIT 1
                    ");

                    $stmt->execute(['email' => $email]);
                    $user = $stmt->fetch();

                    if ($user) {
                        $userFound = $user;
                        $tenantFound = $tenant;
                        break;
                    }
                } catch (\Exception $e) {
                    continue;
                }
            }

            if (!$userFound) {
                // Se é um tenant novo sem usuários, sugere configuração inicial
                $this->error('Nenhum usuário cadastrado neste tenant. Acesse /setup para configurar.');
                return;
            }

            // Conecta ao banco do tenant correto
            $this->tenantManager->setCurrentTenant($tenantFound);
            $this->db = $this->tenantManager->getTenantConnection();

            if (!password_verify($password, $userFound['password'])) {
                $this->error('Email ou senha inválidos');
                return;
            }

            // Atualiza último login
            $updateStmt = $this->db->prepare("
                UPDATE users SET last_login = NOW() WHERE id = :id
            ");
            $updateStmt->execute(['id' => $userFound['id']]);

            // Regenerar ID da sessão ANTES de definir dados (para evitar problemas)
            $this->session->regenerate();

            // Define sessão
            $this->session->setUser([
                'id' => (int) $userFound['id'],
                'name' => $userFound['name'],
                'email' => $userFound['email'],
                'role' => $userFound['role'],
                'company_id' => $userFound['company_id'] ? (int) $userFound['company_id'] : null
            ]);

            if ($userFound['company_id']) {
                $this->session->setCompany((int) $userFound['company_id']);
            }

            // Armazena tenant na sessão
            $this->session->setTenant((int) $tenantFound['id']);

            // Log para debug
            error_log("[AuthController::login] Sessão definida (multi-tenant) - user_id: " . $this->session->get('user_id') . " | isAuthenticated: " . ($this->session->isAuthenticated() ? 'SIM' : 'NÃO'));

            // Salvar usuário logado (pode falhar silenciosamente se a tabela não existir)
            try {
                $this->salvarUsuarioLogado((int) $userFound['id'], $userFound['company_id'] ? (int) $userFound['company_id'] : null);
            } catch (\Exception $e) {
                // Logar mas não interromper o login
                error_log("[AuthController::login] Erro ao salvar usuário logado (não crítico): " . $e->getMessage());
            }

            // Logar atividade (pode falhar silenciosamente se a tabela não existir)
            try {
                $this->logActivity('login', 'users', $userFound['id']);
            } catch (\Exception $e) {
                // Logar mas não interromper o login
                error_log("[AuthController::login] Erro ao logar atividade (não crítico): " . $e->getMessage());
            }

            $this->success('Login realizado com sucesso', [
                'redirect' => UrlHelper::url('/dashboard')
            ]);

        } catch (Exception $e) {
            error_log("==========================================");
            error_log("[AuthController::login] ERRO CAPTURADO:");
            error_log("Mensagem: " . $e->getMessage());
            error_log("Arquivo: " . $e->getFile() . ":" . $e->getLine());
            error_log("Stack trace: " . $e->getTraceAsString());
            error_log("==========================================");

            // Se for erro de conexão com banco, mensagem mais específica
            if (strpos($e->getMessage(), 'SQLSTATE') !== false || strpos($e->getMessage(), 'PDO') !== false) {
                error_log("[AuthController::login] Erro de conexão com banco de dados");
                $this->error('Erro de conexão com o banco de dados. Verifique as configurações.');
            } else {
                $this->error('Erro ao realizar login: ' . $e->getMessage());
            }
        }
    }

    /**
     * Exibe formulário de registro
     */
    public function showRegister(): void
    {
        if ($this->session->isAuthenticated()) {
            $this->redirect('/dashboard');
            return;
        }

        $this->view('auth/register');
    }

    /**
     * Processa registro de novo tenant
     */
    public function register(): void
    {
        $data = [
            'company_name' => $this->request->post('company_name'),
            'subdomain' => $this->request->post('subdomain'),
            'name' => $this->request->post('name'),
            'email' => $this->request->post('email'),
            'password' => $this->request->post('password'),
            'password_confirmation' => $this->request->post('password_confirmation'),
            'plan' => $this->request->post('plan', 'free'),
        ];

        $errors = $this->validate([
            'company_name' => 'required|min:3',
            'subdomain' => 'required|min:3',
            'name' => 'required|min:3',
            'email' => 'required|email',
            'password' => 'required|min:8',
        ]);

        if (!empty($errors)) {
            $this->error('Dados inválidos', $errors);
            return;
        }

        if ($data['password'] !== $data['password_confirmation']) {
            $this->error('As senhas não coincidem');
            return;
        }

        // Valida subdomínio (apenas letras, números e hífen)
        if (!preg_match('/^[a-z0-9-]+$/', $data['subdomain'])) {
            $this->error('Subdomínio inválido. Use apenas letras minúsculas, números e hífen');
            return;
        }

        try {
            // Verifica se subdomínio já existe
            if ($this->subdomainExists($data['subdomain'])) {
                $this->error('Este subdomínio já está em uso');
                return;
            }

            // Cria o tenant
            $tenant = $this->tenantManager->createTenant([
                'name' => $data['company_name'],
                'subdomain' => $data['subdomain'],
                'plan' => $data['plan']
            ]);

            // Criar subdomínio automaticamente no servidor
            try {
                require_once ROOT_PATH . '/src/Integrations/SubdomainManager.php';
                $subdomainManager = new \App\Integrations\SubdomainManager();
                $subdomainResult = $subdomainManager->createSubdomain($data['subdomain']);

                if (!$subdomainResult['success']) {
                    error_log("Aviso: Subdomínio não foi criado automaticamente: " . $subdomainResult['message']);
                    // Não falha o registro, apenas registra o aviso
                } else {
                    error_log("Subdomínio criado com sucesso: {$data['subdomain']} via método {$subdomainResult['method']}");
                }
            } catch (Exception $e) {
                error_log("Erro ao criar subdomínio (não crítico): " . $e->getMessage());
                // Continua o processo mesmo se falhar
            }

            // Conecta ao banco do tenant
            $this->tenantManager->setCurrentTenant($tenant);
            $tenantDb = $this->tenantManager->getTenantConnection();

            // Cria a empresa padrão
            $stmt = $tenantDb->prepare("
                INSERT INTO companies (name, is_default)
                VALUES (:name, 1)
            ");
            $stmt->execute(['name' => $data['company_name']]);
            $companyId = $tenantDb->lastInsertId();

            // Cria o usuário administrador
            $stmt = $tenantDb->prepare("
                INSERT INTO users (company_id, name, email, password, role, is_active)
                VALUES (:company_id, :name, :email, :password, 'admin', 1)
            ");

            $stmt->execute([
                'company_id' => $companyId,
                'name' => $data['name'],
                'email' => $data['email'],
                'password' => password_hash($data['password'], PASSWORD_DEFAULT)
            ]);

            // Registra log no banco master
            $this->logRegistration((int) $tenant['id'], $data['email']);

            // Monta URL de redirecionamento para o subdomínio
            $baseDomain = $_ENV['BASE_DOMAIN'] ?? 'localhost';
            $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
            $redirectUrl = $protocol . '://' . $data['subdomain'] . '.' . $baseDomain . '/login';

            $this->success('Cadastro realizado com sucesso!', [
                'redirect' => $redirectUrl,
                'subdomain' => $data['subdomain'],
                'message' => 'Conta criada! Redirecionando para seu sistema...'
            ]);

        } catch (Exception $e) {
            error_log("Erro no registro: " . $e->getMessage());
            $this->error('Erro ao criar conta: ' . $e->getMessage());
        }
    }

    /**
     * Verifica se subdomínio existe
     */
    public function checkSubdomain(): void
    {
        try {
            $subdomain = $this->request->post('subdomain');

            if (empty($subdomain)) {
                $this->error('Subdomínio não fornecido');
                return;
            }

            $exists = $this->subdomainExists($subdomain);

            $this->response->json([
                'success' => true,
                'data' => [
                    'available' => !$exists
                ]
            ]);

        } catch (\Exception $e) {
            error_log("Erro ao verificar subdomínio: " . $e->getMessage());
            $this->error('Erro ao verificar disponibilidade');
        }
    }

    /**
     * Verifica se subdomínio já existe
     */
    private function subdomainExists(string $subdomain): bool
    {
        try {
            $db = $this->tenantManager->getMasterConnection();

            $stmt = $db->prepare("
                SELECT COUNT(*) as count FROM tenants WHERE subdomain = :subdomain
            ");
            $stmt->execute(['subdomain' => $subdomain]);
            $result = $stmt->fetch();

            return $result['count'] > 0;

        } catch (Exception $e) {
            error_log("Erro ao verificar subdomínio: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Registra log de registro no banco master
     */
    private function logRegistration(int $tenantId, string $email): void
    {
        try {
            $db = $this->tenantManager->getMasterConnection();

            $stmt = $db->prepare("
                INSERT INTO system_logs (tenant_id, log_type, severity, message, ip_address, user_agent)
                VALUES (:tenant_id, 'auth', 'info', :message, :ip, :user_agent)
            ");

            $stmt->execute([
                'tenant_id' => $tenantId,
                'message' => "Novo tenant registrado: {$email}",
                'ip' => $this->request->ip(),
                'user_agent' => $this->request->userAgent()
            ]);
        } catch (Exception $e) {
            error_log("Erro ao registrar log: " . $e->getMessage());
        }
    }

    /**
     * Logout
     */
    public function logout(): void
    {
        $userId = $this->getUserId();

        // Remover usuário logado antes de destruir a sessão
        if ($userId) {
            $this->removerUsuarioLogado($userId);
            $this->logActivity('logout', 'users', $userId);
        }

        $this->session->logout();
        $this->session->destroy();

        $this->redirect('/login');
    }

    /**
     * Salva registro de usuário logado na tabela logged_users
     */
    private function salvarUsuarioLogado(int $userId, ?int $companyId = null): void
    {
        try {
            // Verificar se a tabela existe
            $stmt = $this->db->query("SHOW TABLES LIKE 'logged_users'");
            if (!$stmt->fetch()) {
                // Tabela não existe, não faz nada (silenciosamente)
                return;
            }

            $sessionId = session_id();
            $ipAddress = $this->request->ip();
            $userAgent = $this->request->userAgent();

            // Verificar se já existe registro para este usuário e sessão
            $stmt = $this->db->prepare("
                SELECT id FROM logged_users
                WHERE user_id = :user_id AND session_id = :session_id
                LIMIT 1
            ");
            $stmt->execute([
                'user_id' => $userId,
                'session_id' => $sessionId
            ]);
            $existing = $stmt->fetch();

            if ($existing) {
                // Atualizar registro existente
                $stmt = $this->db->prepare("
                    UPDATE logged_users
                    SET company_id = :company_id,
                        ip_address = :ip_address,
                        user_agent = :user_agent,
                        logged_in_at = NOW(),
                        last_activity = NOW()
                    WHERE id = :id
                ");
                $stmt->execute([
                    'id' => $existing['id'],
                    'company_id' => $companyId,
                    'ip_address' => $ipAddress,
                    'user_agent' => $userAgent
                ]);
            } else {
                // Inserir novo registro
                $stmt = $this->db->prepare("
                    INSERT INTO logged_users
                    (user_id, company_id, session_id, ip_address, user_agent, logged_in_at, last_activity)
                    VALUES
                    (:user_id, :company_id, :session_id, :ip_address, :user_agent, NOW(), NOW())
                ");
                $stmt->execute([
                    'user_id' => $userId,
                    'company_id' => $companyId,
                    'session_id' => $sessionId,
                    'ip_address' => $ipAddress,
                    'user_agent' => $userAgent
                ]);
            }
        } catch (Exception $e) {
            error_log("Erro ao salvar usuário logado: " . $e->getMessage());
            // Não quebra o processo, apenas loga o erro
        }
    }

    /**
     * Remove registro de usuário logado da tabela logged_users
     */
    private function removerUsuarioLogado(int $userId): void
    {
        try {
            // Verificar se a tabela existe
            $stmt = $this->db->query("SHOW TABLES LIKE 'logged_users'");
            if (!$stmt->fetch()) {
                // Tabela não existe, não faz nada (silenciosamente)
                return;
            }

            $sessionId = session_id();

            // Remover por user_id e session_id (mais seguro)
            $stmt = $this->db->prepare("
                DELETE FROM logged_users
                WHERE user_id = :user_id AND session_id = :session_id
            ");
            $stmt->execute([
                'user_id' => $userId,
                'session_id' => $sessionId
            ]);
        } catch (Exception $e) {
            error_log("Erro ao remover usuário logado: " . $e->getMessage());
            // Não quebra o processo, apenas loga o erro
        }
    }
}

