<?php

namespace App\Core;

class FbAuth
{
    private const SESSION_KEY  = 'pbi_fb_user';
    private const PERMS_FILE   = '/config/user_permissions.json';

    /** Última falha de CONEXÃO no login (driver/Firebird/WireCrypt) — p/ mensagem clara. */
    private static ?string $erroConexao = null;
    public static function erroConexao(): ?string { return self::$erroConexao; }

    // Lista de todos os modulos do sistema
    public const MODULOS = [
        'dashboard'  => 'Dashboard',
        'executivo'  => 'Painel Executivo',
        'alertas'    => 'Alertas',
        'vendas'     => 'Vendas',
        'compras'    => 'Compras',
        'estoque'    => 'Estoque',
        'financeiro' => 'Financeiro',
        'caixa'      => 'Fechamento de Caixa',
        'clientes'   => 'Clientes',
        'pessoal'    => 'Pessoal',
        'produtos'   => 'Produtos',
        'relatorios' => 'Relatórios',
        'devolucoes' => 'Devoluções',
        'previsao'   => 'Previsão de Vendas',
        'mesas'      => 'Mesas',
        'cozinha'    => 'Cozinha (KDS)',
    ];

    // Segmentos que possuem modulo de mesas e cozinha (KDS)
    private const SEGMENTOS_MESAS = [
        'restaurante', 'bar', 'lanchonete', 'sushi', 'pizzaria', 'churrascaria',
    ];

    // Retorna os modulos aplicaveis ao segmento atual
    public static function modulosPorSegmento(string $segmento = ''): array
    {
        $todos = self::MODULOS;
        if (!in_array($segmento, self::SEGMENTOS_MESAS, true)) {
            unset($todos['mesas']);
            unset($todos['cozinha']);
        }
        return $todos;
    }

    // Mapeamento URI prefix -> modulo
    private const URI_MAP = [
        '/vendas'     => 'vendas',
        '/compras'    => 'compras',
        '/financeiro' => 'financeiro',
        '/estoque'    => 'estoque',
        '/clientes'   => 'clientes',
        '/pessoal'    => 'pessoal',
        '/produtos'   => 'produtos',
        '/mesas'      => 'mesas',
        '/cozinha'    => 'cozinha',
        '/caixa'      => 'caixa',
        '/relatorios' => 'relatorios',
        '/executivo'  => 'executivo',
        '/alertas'    => 'alertas',
        '/devolucoes' => 'devolucoes',
        '/previsao'   => 'previsao',
        '/dashboard'  => 'dashboard',
    ];

    // Autentica contra tabela USUARIOS do ERP (mesmo mecanismo do Leitor)
    /** Traduz o erro técnico do Firebird em dica acionável p/ instalação nova. */
    private static function traduzErroConexao(string $msg): string
    {
        $m = strtolower($msg);
        if (str_contains($m, 'could not find driver') || str_contains($m, 'pdo_firebird') || str_contains($m, 'find driver')) {
            // Diagnóstico técnico embutido — em 1 print no celular dá pra ver o que falta
            $drivers = class_exists('PDO') ? implode(',', \PDO::getAvailableDrivers()) : 'PDO nao existe';
            if ($drivers === '') $drivers = '(vazio)';
            $iniPath = php_ini_loaded_file() ?: '(nenhum)';
            $extDir  = ini_get('extension_dir') ?: '(vazio)';
            $pdoFb   = extension_loaded('pdo_firebird') ? 'SIM' : 'NAO';
            $intf    = extension_loaded('interbase') ? 'SIM' : 'NAO';
            $diag = " | PHP=" . PHP_VERSION
                  . " | extension_loaded(pdo_firebird)=$pdoFb"
                  . " | extension_loaded(interbase)=$intf"
                  . " | PDO drivers=[$drivers]"
                  . " | php.ini=$iniPath"
                  . " | extension_dir=$extDir";
            return 'O driver do Firebird não carregou. ' . $msg . $diag;
        }
        if (str_contains($m, 'wire encryption') || str_contains($m, '335545064') || str_contains($m, 'encryption')) {
            return 'Criptografia de conexão incompatível (Firebird 2.5/antigo). Ajuste WireCrypt=Disabled no firebird.conf do cliente. Detalhe: ' . $msg;
        }
        if (str_contains($m, 'i/o error') || str_contains($m, 'createfile') || str_contains($m, '335544721') || str_contains($m, 'unavailable') || str_contains($m, 'network')) {
            return 'Não foi possível abrir o banco (verifique o caminho do .FDB, o host/porta e se o Firebird está rodando). Detalhe: ' . $msg;
        }
        if (str_contains($m, 'login') || str_contains($m, 'password') || str_contains($m, 'sysdba')) {
            return 'Usuário/senha do BANCO (SYSDBA) incorretos no Configurar Sistema. Detalhe: ' . $msg;
        }
        return 'Falha ao conectar ao banco. Detalhe: ' . $msg;
    }

    public static function login(string $usuario, string $senha): bool
    {
        $usuario = strtoupper(trim($usuario));
        if ($usuario === '' || $senha === '') return false;

        // O LOGIN valida SEMPRE contra a loja PRINCIPAL (central), nunca a loja ativa
        // da sessão. Sem isso, se a sessão estiver apontando p/ outra loja (ex.: uma
        // filial via VPN, com cod_empresa diferente), o login procuraria o usuário no
        // banco/empresa errados e falharia. O cod_empresa tem de casar com o banco
        // realmente conectado.
        self::$erroConexao = null;
        try {
            if (!\App\Core\Database::estaConfigurado()) {
                self::$erroConexao = 'Banco de dados não configurado. Abra Configurar Sistema.';
                return false;
            }

            if (\App\Core\Lojas::multi()) {
                $idPrincipal = \App\Core\Lojas::padraoId();
                $pdo     = \App\Core\Database::conectarLoja($idPrincipal);
                $empresa = (int) (\App\Core\Lojas::get($idPrincipal)['cod_empresa'] ?? 0);
            } else {
                $pdo     = \App\Core\Database::conectar();
                $cfg     = self::dbConfig();
                $empresa = (int) ($cfg['cod_empresa'] ?? 5);
            }
        } catch (\Throwable $e) {
            // Conexão/driver falhou → NÃO é "senha errada". Guarda causa amigável.
            self::$erroConexao = self::traduzErroConexao($e->getMessage());
            return false;
        }

        // Tentativas de senha: MD5, MD5 upper, strtoupper(MD5), plain, upper
        $tentativas = [
            md5($senha),
            md5(strtoupper($senha)),
            strtoupper(md5($senha)),
            $senha,
            strtoupper($senha),
        ];
        $colunas = ['PASS', 'SENHA'];

        // Busca o usuário; tenta COM a empresa configurada e, se não achar, SEM o filtro
        // de empresa (o nº de empresa do wizard costuma vir errado em cliente novo).
        $buscar = function (\PDO $pdo, ?int $emp, bool $comSenha) use ($usuario, $tentativas, $colunas) {
            $empW = $emp !== null ? " AND u.COD_EMPRESA = {$emp}" : '';
            if ($comSenha) {
                foreach ($colunas as $col) {
                    foreach ($tentativas as $sc) {
                        try {
                            $st = $pdo->prepare(
                                "SELECT FIRST 1 TRIM(u.COD_USUARIO) AS COD_USUARIO,
                                        TRIM(COALESCE(u.NOME, u.COD_USUARIO)) AS NOME,
                                        COALESCE(TRIM(u.SUPERVISOR), '') AS SUPERVISOR
                                 FROM USUARIOS u
                                 WHERE UPPER(TRIM(u.COD_USUARIO)) = ?{$empW} AND u.{$col} = ?"
                            );
                            $st->execute([$usuario, $sc]);
                            $r = $st->fetch(\PDO::FETCH_ASSOC);
                            if ($r && !empty($r['COD_USUARIO'])) return $r;
                        } catch (\Throwable) { break; } // coluna não existe → próxima
                    }
                }
                return null;
            }
            try {
                $st = $pdo->prepare(
                    "SELECT FIRST 1 TRIM(u.COD_USUARIO) AS COD_USUARIO,
                            TRIM(COALESCE(u.NOME, u.COD_USUARIO)) AS NOME,
                            COALESCE(TRIM(u.SUPERVISOR), '') AS SUPERVISOR
                     FROM USUARIOS u
                     WHERE UPPER(TRIM(u.COD_USUARIO)) = ?{$empW}"
                );
                $st->execute([$usuario]);
                return $st->fetch(\PDO::FETCH_ASSOC) ?: null;
            } catch (\Throwable) { return null; }
        };

        $row = $buscar($pdo, $empresa ?: null, true)        // 1) empresa + senha
            ?? $buscar($pdo, null, true)                    // 2) qualquer empresa + senha
            ?? $buscar($pdo, $empresa ?: null, false)       // 3) empresa, só existência
            ?? $buscar($pdo, null, false);                  // 4) qualquer empresa, só existência

        if (!$row || empty($row['COD_USUARIO'])) return false;

        $user = (string)$row['COD_USUARIO'];
        $_SESSION[self::SESSION_KEY]   = $user;
        $_SESSION['pbi_fb_nome']       = mb_convert_encoding(
            (string)($row['NOME'] ?? $user), 'UTF-8', 'Windows-1252'
        );
        $_SESSION['pbi_fb_supervisor'] = ($row['SUPERVISOR'] ?? '') === 'S';

        self::registrarSeNovo($user);
        return true;
    }

    public static function logout(): void
    {
        unset(
            $_SESSION[self::SESSION_KEY],
            $_SESSION['pbi_fb_nome'],
            $_SESSION['pbi_fb_supervisor']
        );
    }

    public static function estaLogado(): bool
    {
        return !empty($_SESSION[self::SESSION_KEY]);
    }

    public static function usuario(): string
    {
        return $_SESSION[self::SESSION_KEY] ?? '';
    }

    public static function nome(): string
    {
        return $_SESSION['pbi_fb_nome'] ?? self::usuario();
    }

    // Admin = supervisor no ERP ou usuario SYSDBA com acesso total
    public static function isAdmin(): bool
    {
        if (!self::estaLogado()) return false;
        if (!empty($_SESSION['pbi_fb_supervisor'])) return true;
        $perms = self::permissoesUsuario(self::usuario());
        return in_array('*', $perms['modulos'] ?? [], true);
    }

    // Verifica se usuario pode acessar um modulo
    public static function pode(string $modulo): bool
    {
        if (!self::estaLogado()) return false;
        if (self::isAdmin()) return true;

        $perms = self::permissoesUsuario(self::usuario());
        if (in_array('*', $perms['modulos'] ?? [], true)) return true;
        return in_array($modulo, $perms['modulos'] ?? [], true);
    }

    // Detecta modulo a partir do URI
    public static function moduloDaUri(string $uri): ?string
    {
        // Rotas de API retornam o modulo base
        foreach (self::URI_MAP as $prefix => $modulo) {
            if ($uri === $prefix || str_starts_with($uri, $prefix . '/')) {
                return $modulo;
            }
        }
        return null;
    }

    // Redireciona para login se nao autenticado
    public static function guard(): void
    {
        if (self::estaLogado()) return;
        $redirect = urlencode($_SERVER['REQUEST_URI'] ?? '/');
        header('Location: /login?redirect=' . $redirect);
        exit;
    }

    // Redireciona para 403 se nao tem permissao no modulo
    public static function guardModulo(string $modulo): void
    {
        if (self::pode($modulo)) return;
        header('HTTP/1.1 403 Forbidden');
        header('Location: /sem-permissao');
        exit;
    }

    // Carrega permissoes de um usuario
    public static function permissoesUsuario(string $usuario): array
    {
        $todos = self::carregarTodos();
        $user  = strtoupper($usuario);
        return $todos[$user] ?? ['modulos' => [], 'ativo' => true];
    }

    // Salva permissoes de um usuario
    public static function salvarPermissoes(string $usuario, array $modulos): void
    {
        $todos = self::carregarTodos();
        $user  = strtoupper($usuario);
        if (!isset($todos[$user])) {
            $todos[$user] = ['modulos' => [], 'ativo' => true];
        }
        $todos[$user]['modulos'] = $modulos;
        self::salvarTodos($todos);
    }

    // ── Controle de LOJAS por usuário (multi-loja) ───────────────────────────
    // 'lojas' vazio/ausente = SEM restrição (vê todas). Lista = só essas lojas.

    /** Salva as lojas que o usuário pode acessar (ids de config/lojas.php). */
    public static function salvarLojasUsuario(string $usuario, array $lojas): void
    {
        $todos = self::carregarTodos();
        $user  = strtoupper($usuario);
        if (!isset($todos[$user])) $todos[$user] = ['modulos' => [], 'ativo' => true];
        $todos[$user]['lojas'] = array_values(array_filter(array_map('strval', $lojas)));
        self::salvarTodos($todos);
    }

    /** Lojas permitidas ao usuário (apenas as que existem). [] = todas (sem restrição). */
    public static function lojasPermitidas(string $usuario = ''): array
    {
        if ($usuario === '') $usuario = self::usuario();
        if ($usuario === '') return [];
        if (self::isAdmin()) return [];               // admin/supervisor vê todas
        $perms = self::permissoesUsuario($usuario);
        $lojas = is_array($perms['lojas'] ?? null) ? $perms['lojas'] : [];
        // mantém só as que existem na config atual
        $lojas = array_values(array_filter(array_map('strval', $lojas), fn($id) => \App\Core\Lojas::existe($id)));
        return $lojas;
    }

    /** O usuário pode acessar a loja $id? (sem restrição = sim) */
    public static function podeLoja(string $id): bool
    {
        $perm = self::lojasPermitidas();
        return empty($perm) || in_array((string)$id, $perm, true);
    }

    /** Pode usar a visão consolidada "Todas as lojas"? (precisa de acesso a 2+ lojas) */
    public static function podeConsolidado(): bool
    {
        $perm = self::lojasPermitidas();
        return empty($perm) || count($perm) >= 2;
    }

    /** Lojas visíveis no seletor para o usuário atual (filtradas). */
    public static function lojasVisiveis(): array
    {
        $todas = \App\Core\Lojas::todas();
        $perm  = self::lojasPermitidas();
        if (empty($perm)) return $todas;
        $out = [];
        foreach ($todas as $id => $l) { if (in_array((string)$id, $perm, true)) $out[$id] = $l; }
        return $out;
    }

    /** Garante que a loja ativa/consolidado respeite a restrição do usuário. */
    public static function enforcarLojaAtiva(): void
    {
        if (!self::estaLogado() || !\App\Core\Lojas::multi()) return;
        $perm = self::lojasPermitidas();
        if (empty($perm)) return; // sem restrição
        if (\App\Core\Lojas::consolidado() && !self::podeConsolidado()) {
            \App\Core\Lojas::setConsolidado(false);
        }
        $ativa = \App\Core\Lojas::idAtiva();
        if ($ativa === null || !in_array((string)$ativa, $perm, true)) {
            \App\Core\Lojas::setAtiva($perm[0]); // primeira loja permitida
        }
    }

    // Lista todos os usuarios com permissoes cadastradas (JSON)
    public static function listarUsuarios(): array
    {
        return self::carregarTodos();
    }

    // Lista usuarios ativos do ERP (tabela USUARIOS) mesclados com permissoes do JSON
    public static function listarUsuariosERP(): array
    {
        $todos = self::carregarTodos();

        try {
            if (!\App\Core\Database::estaConfigurado()) return $todos;

            // Escolhe banco + cod_empresa DA LOJA ATIVA (consistente com autenticar()).
            // Em multi-loja, conectar() vai pra loja ativa da sessão, mas o cod_empresa
            // do config/database.php pode ser de OUTRA loja → query retorna 0 rows.
            if (\App\Core\Lojas::multi()) {
                $idLoja  = \App\Core\Lojas::idAtiva() ?? \App\Core\Lojas::padraoId();
                $pdo     = \App\Core\Database::conectarLoja($idLoja);
                $empresa = (int) (\App\Core\Lojas::get($idLoja)['cod_empresa'] ?? 0);
            } else {
                $pdo     = \App\Core\Database::conectar();
                $cfg     = self::dbConfig();
                $empresa = (int) ($cfg['cod_empresa'] ?? 0);
            }
            if ($empresa <= 0) return $todos; // sem empresa válida = evita listar nada errado

            $st = $pdo->prepare(
                "SELECT TRIM(u.COD_USUARIO) AS COD_USUARIO,
                        TRIM(COALESCE(u.NOME, u.COD_USUARIO)) AS NOME,
                        COALESCE(TRIM(u.SUPERVISOR), '') AS SUPERVISOR,
                        u.COD_SITUACAO
                 FROM USUARIOS u
                 WHERE u.COD_EMPRESA = ?
                 ORDER BY u.COD_USUARIO"
            );
            $st->execute([$empresa]);

            $resultado = [];
            foreach ($st->fetchAll(\PDO::FETCH_ASSOC) as $r) {
                $user = strtoupper((string)$r['COD_USUARIO']);
                $nome = mb_convert_encoding((string)($r['NOME'] ?? $user), 'UTF-8', 'Windows-1252');
                $resultado[$user] = array_merge(
                    $todos[$user] ?? ['modulos' => [], 'ativo' => (int)($r['COD_SITUACAO'] ?? 1) === 1],
                    [
                        'nome'       => $nome,
                        'supervisor' => ($r['SUPERVISOR'] ?? '') === 'S',
                        'situacao'   => (int)($r['COD_SITUACAO'] ?? 1),
                    ]
                );
            }
            return $resultado;
        } catch (\Throwable $e) {
            error_log('listarUsuariosERP falhou: ' . $e->getMessage());
            return $todos;
        }
    }

    // Retorna array de modulos permitidos para exibicao no menu
    public static function modulosPermitidos(): array
    {
        if (self::isAdmin()) return array_keys(self::MODULOS);
        $perms = self::permissoesUsuario(self::usuario());
        if (in_array('*', $perms['modulos'] ?? [], true)) return array_keys(self::MODULOS);
        return $perms['modulos'] ?? [];
    }

    // ── Privados ────────────────────────────────────────────────────────────────

    private static function registrarSeNovo(string $usuario): void
    {
        $todos = self::carregarTodos();
        if (!isset($todos[$usuario])) {
            // Supervisores e SYSDBA recebem acesso total; outros ficam sem modulos
            $isSupervisor = !empty($_SESSION['pbi_fb_supervisor']);
            $todos[$usuario] = [
                'modulos' => ($isSupervisor || $usuario === 'SYSDBA') ? ['*'] : [],
                'ativo'   => true,
            ];
            self::salvarTodos($todos);
        }
    }

    private static function carregarTodos(): array
    {
        $path = dirname(__DIR__, 2) . self::PERMS_FILE;
        if (!file_exists($path)) return [];
        $json = file_get_contents($path);
        return json_decode($json ?: '{}', true) ?: [];
    }

    private static function salvarTodos(array $dados): void
    {
        $path = dirname(__DIR__, 2) . self::PERMS_FILE;
        file_put_contents($path, json_encode($dados, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
    }

    private static function dbConfig(): array
    {
        $path = dirname(__DIR__, 2) . '/config/database.php';
        $cfg  = file_exists($path) ? (require $path) : [];
        return [
            'host'        => $cfg['host']        ?? 'localhost',
            'port'        => $cfg['port']         ?? 3050,
            'database'    => $cfg['database']     ?? '',
            'charset'     => $cfg['charset']      ?? 'WIN1252',
            'cod_empresa' => $cfg['cod_empresa']  ?? 5,
        ];
    }
}
