<?php

namespace App\Repositories;

use App\Core\Cache;

class ClientesRepository
{
    /**
     * Lazy-init de VendasRepository para reaproveitar os filtros publicos
     * filtroVendaSql() / filtroEmpresaSql() — fonte unica do "o que conta
     * como venda" no sistema. Sem isso as queries de frequencia/ticketMedio
     * filtravam apenas TIPO_NATUREZA='V' e inflavam quando o cliente tinha
     * operacao com MOV_INTERNA='S' (Real Prime op 115).
     */
    private ?VendasRepository $vendasRepo = null;

    private function vendasRepo(): VendasRepository
    {
        return $this->vendasRepo ??= new VendasRepository($this->pdo);
    }

    public function __construct(private \PDO $pdo) {}

    /**
     * Converte campos de texto (nome/fantasia/cliente/bairro) WIN1252 → UTF-8.
     * Sem isso, um nome com acento faz o json_encode retornar false → corpo vazio
     * → "Unexpected end of JSON input" na tela.
     */
    private function utf8(array $rows): array
    {
        foreach ($rows as &$r) {
            if (!is_array($r)) continue;
            foreach (['NOME', 'FANTASIA', 'CLIENTE', 'BAIRRO_PRINC'] as $k) {
                if (isset($r[$k]) && $r[$k] !== '' && !mb_check_encoding((string)$r[$k], 'UTF-8')) {
                    $r[$k] = mb_convert_encoding((string)$r[$k], 'UTF-8', 'Windows-1252');
                }
            }
        }
        unset($r);
        return $rows;
    }

    public function kpis(): array
    {
        $sql = "SELECT
            COUNT(*) TOTAL_CLIENTES,
            COUNT(CASE WHEN ATIVO = 'S' THEN 1 END) ATIVOS,
            COUNT(CASE WHEN DATA_ULT_COMPRA >= DATEADD(DAY, -30, CURRENT_DATE) THEN 1 END) COMPRARAM_30D,
            SUM(TOTAL_COMPRA) VOLUME_TOTAL,
            AVG(CASE WHEN NUMERO_COMPRAS > 0 THEN VLR_ULT_COMPRA END) TICKET_MEDIO
        FROM PESSOAS
        WHERE ECLIENTE = 'S'";
        return $this->pdo->query($sql)->fetch(\PDO::FETCH_ASSOC) ?: [];
    }

    public function cadastro(string $busca = '', int $limite = 200): array
    {
        $where = $busca ? "AND UPPER(p.NOME) CONTAINING UPPER(?)" : '';
        $sql = "SELECT FIRST {$limite}
            p.COD_PESSOA, p.NOME, p.FANTASIA,
            p.CPF, p.CNPJ, p.FONE_PRINC, p.CEL_PRINC,
            p.BAIRRO_PRINC,
            p.DATA_CADASTRO, p.DATA_ULT_COMPRA,
            p.VLR_ULT_COMPRA, p.NUMERO_COMPRAS,
            p.TOTAL_COMPRA, p.ATIVO
        FROM PESSOAS p
        WHERE p.ECLIENTE = 'S' {$where}
        ORDER BY p.NOME";
        $st = $this->pdo->prepare($sql);
        $busca ? $st->execute([$busca]) : $st->execute();
        return $this->utf8($st->fetchAll(\PDO::FETCH_ASSOC));
    }

    public function frequencia(string $dataIni, string $dataFim, int $limite = 100): array
    {
        $ttl = ($dataFim >= date('Y-m-d')) ? 300 : 86400;
        return Cache::lembrar("clientes_frequencia_{$dataIni}_{$dataFim}_{$limite}", $ttl, function () use ($dataIni, $dataFim, $limite) {
            // v1.4.17: usa filtroVendaSql do VendasRepository — fonte unica de
            // "o que e venda real" (respeita extras/excluir do config). Antes
            // o filtro era so TIPO_NATUREZA='V', sem excluir MOV_INTERNA, e
            // op 115 do Real Prime entrava como visita/ticket.
            $filtroV = $this->vendasRepo()->filtroVendaSql('n');
            $fe      = $this->vendasRepo()->filtroEmpresaSql('n');
            $sql = "SELECT FIRST {$limite}
                p.COD_PESSOA, p.NOME,
                COUNT(DISTINCT n.NR_NOTA) QTD_VISITAS,
                SUM(n.TOTAL_NOTA) VALOR_TOTAL,
                AVG(n.TOTAL_NOTA) TICKET_MEDIO,
                MIN(n.DATA_EMISSAO) PRIMEIRA,
                MAX(n.DATA_EMISSAO) ULTIMA
            FROM NOTAS_CAB n
            JOIN PESSOAS p ON n.COD_PESSOA = p.COD_PESSOA
            WHERE n.DATA_EMISSAO BETWEEN ? AND ?
              AND n.COD_SITUACAO <> 3
              AND p.ECLIENTE = 'S'
              {$filtroV}
              {$fe}
            GROUP BY p.COD_PESSOA, p.NOME
            ORDER BY QTD_VISITAS DESC";
            $st = $this->pdo->prepare($sql);
            $st->execute([$dataIni, $dataFim]);
            return $this->utf8($st->fetchAll(\PDO::FETCH_ASSOC));
        });
    }

    public function inadimplentes(int $limite = 200): array
    {
        return Cache::lembrar("clientes_inadimplentes_{$limite}", 600, function () use ($limite) {
            // Inadimplencia = SALDO > 0 + VENCIMENTO passado. Nao filtra por
            // tipo de operacao — titulos legitimos (notas debito, parcelas
            // renegociadas, ajustes) podem nao estar na lista de venda e
            // mesmo assim sao devedores reais.
            $fe = $this->vendasRepo()->filtroEmpresaSql('rp');
            $sql = "SELECT FIRST {$limite}
                p.COD_PESSOA, p.NOME, p.FONE_PRINC, p.CEL_PRINC,
                COUNT(rp.PARCELA) QTD_TITULOS,
                SUM(rp.SALDO_TITULO) SALDO_TOTAL,
                MIN(rp.VENCIMENTO) VENC_MAIS_ANTIGO,
                MAX(rp.VENCIMENTO) VENC_MAIS_RECENTE,
                DATEDIFF(DAY FROM MIN(rp.VENCIMENTO) TO CURRENT_DATE) DIAS_ATRASO
            FROM RECEBER_PAGAR rp
            JOIN PESSOAS p ON rp.COD_PESSOA = p.COD_PESSOA
            WHERE rp.SALDO_TITULO > 0
              AND rp.VENCIMENTO < CURRENT_DATE
              AND p.ECLIENTE = 'S'
              {$fe}
            GROUP BY p.COD_PESSOA, p.NOME, p.FONE_PRINC, p.CEL_PRINC
            ORDER BY SALDO_TOTAL DESC";
            return $this->utf8($this->pdo->query($sql)->fetchAll(\PDO::FETCH_ASSOC));
        });
    }

    public function inadimplentesDetalhado(int $limite = 1000): array
    {
        return Cache::lembrar("clientes_inadimplentes_det_{$limite}", 600, function () use ($limite) {
            // Mesma razao de inadimplentes() — nao filtra por tipo de operacao.
            $fe = $this->vendasRepo()->filtroEmpresaSql('rp');
            $sql = "SELECT FIRST {$limite}
                p.NOME CLIENTE,
                rp.NR_NOTA,
                rp.PARCELA NR_PARCELA,
                (SELECT COUNT(*) FROM RECEBER_PAGAR rp2
                 WHERE rp2.NR_NOTA = rp.NR_NOTA AND rp2.SERIE = rp.SERIE) QT_PARCELAS,
                rp.VALOR_TITULO VALOR,
                rp.SALDO_TITULO SALDO,
                rp.VENCIMENTO DT_VENCIMENTO,
                CASE WHEN rp.VENCIMENTO < CURRENT_DATE
                     THEN DATEDIFF(DAY FROM rp.VENCIMENTO TO CURRENT_DATE)
                     ELSE 0 END DIAS_ATRASO
            FROM RECEBER_PAGAR rp
            JOIN PESSOAS p ON rp.COD_PESSOA = p.COD_PESSOA
            WHERE rp.SALDO_TITULO > 0
              AND p.ECLIENTE = 'S'
              {$fe}
            ORDER BY rp.VENCIMENTO";
            return $this->utf8($this->pdo->query($sql)->fetchAll(\PDO::FETCH_ASSOC));
        });
    }

    public function ticketMedio(string $dataIni, string $dataFim, int $limite = 100): array
    {
        $ttl = ($dataFim >= date('Y-m-d')) ? 300 : 86400;
        return Cache::lembrar("clientes_ticket_{$dataIni}_{$dataFim}_{$limite}", $ttl, function () use ($dataIni, $dataFim, $limite) {
            // v1.4.17: alinhado com VendasRepository::filtroVendaSql (mesmo
            // IN list que /vendas usa). Sem isto o ticket medio do Real Prime
            // inflava porque a op 115 (MOV_INTERNA='S') somava ao total.
            $filtroV = $this->vendasRepo()->filtroVendaSql('n');
            $fe      = $this->vendasRepo()->filtroEmpresaSql('n');
            $sql = "SELECT FIRST {$limite}
                p.COD_PESSOA, p.NOME,
                COUNT(DISTINCT n.NR_NOTA) QTD_NOTAS,
                SUM(n.TOTAL_NOTA) VALOR_TOTAL,
                AVG(n.TOTAL_NOTA) TICKET_MEDIO,
                MAX(n.DATA_EMISSAO) ULTIMA_COMPRA
            FROM NOTAS_CAB n
            JOIN PESSOAS p ON n.COD_PESSOA = p.COD_PESSOA
            WHERE n.DATA_EMISSAO BETWEEN ? AND ?
              AND n.COD_SITUACAO <> 3
              AND p.ECLIENTE = 'S'
              {$filtroV}
              {$fe}
            GROUP BY p.COD_PESSOA, p.NOME
            HAVING COUNT(DISTINCT n.NR_NOTA) >= 2
            ORDER BY TICKET_MEDIO DESC";
            $st = $this->pdo->prepare($sql);
            $st->execute([$dataIni, $dataFim]);
            return $this->utf8($st->fetchAll(\PDO::FETCH_ASSOC));
        });
    }

    public function ultimaData(): string
    {
        try {
            // v1.4.17: substitui o hardcode IN (115,231,297) — quebrava em
            // clientes cujos codigos de venda nao batiam com esse trio (MAX
            // vazio -> fallback hoje). Agora usa o mesmo IN dinamico de
            // VendasRepository (com extras/excluir do config).
            $filtroV = $this->vendasRepo()->filtroVendaSql('');
            $fe      = $this->vendasRepo()->filtroEmpresaSql('');
            // Janela recente (120d) — MAX sem limite custa ~45s no Firebird 2.5 sem índice descendente.
            $sql = "SELECT MAX(DATA_EMISSAO) D FROM NOTAS_CAB
                    WHERE COD_SITUACAO <> 3
                      AND DATA_EMISSAO >= DATEADD(DAY,-120,CURRENT_DATE)
                      AND DATA_EMISSAO <= CURRENT_DATE
                      {$filtroV}
                      {$fe}";
            $r = $this->pdo->query($sql)->fetchColumn();
            if (!$r) return date('Y-m-d');
            if ($r instanceof \DateTime) return $r->format('Y-m-d');
            return substr((string)$r, 0, 10);
        } catch (\Throwable) { return date('Y-m-d'); }
    }

    public function countInativos(int $dias = 60): int
    {
        return Cache::lembrar("cli_inativos_{$dias}", 600, function () use ($dias) {
            try {
                return (int)$this->pdo->query("
                    SELECT COUNT(*) FROM PESSOAS
                    WHERE ECLIENTE = 'S'
                      AND DATA_ULT_COMPRA IS NOT NULL
                      AND DATA_ULT_COMPRA < DATEADD(DAY, -{$dias}, CURRENT_DATE)
                ")->fetchColumn();
            } catch (\Throwable) { return 0; }
        });
    }

    public function inativos(int $dias = 60, int $limite = 300): array
    {
        return Cache::lembrar("clientes_inativos_{$dias}_{$limite}", 1800, function () use ($dias, $limite) {
            $sql = "SELECT FIRST {$limite}
                p.COD_PESSOA, p.NOME, p.FANTASIA,
                p.FONE_PRINC, p.CEL_PRINC,
                p.DATA_ULT_COMPRA,
                p.VLR_ULT_COMPRA,
                p.NUMERO_COMPRAS,
                p.TOTAL_COMPRA,
                DATEDIFF(DAY FROM p.DATA_ULT_COMPRA TO CURRENT_DATE) DIAS_SEM_COMPRA
            FROM PESSOAS p
            WHERE p.ECLIENTE = 'S'
              AND p.DATA_ULT_COMPRA IS NOT NULL
              AND p.DATA_ULT_COMPRA < DATEADD(DAY, -{$dias}, CURRENT_DATE)
            ORDER BY DIAS_SEM_COMPRA ASC";
            try {
                return $this->utf8($this->pdo->query($sql)->fetchAll(\PDO::FETCH_ASSOC));
            } catch (\Throwable) { return []; }
        });
    }
}
