package elinps

import (
	"context"
	"fmt"
	"strings"
	"time"

	"e-li.nps/internal/contratos"
	"e-li.nps/internal/db"

	"github.com/jackc/pgx/v5"
)

// ListarProdutos retorna os produtos existentes a partir das tabelas `nps_*`.
//
// Importante: este painel é para exploração interna. Mesmo assim, mantemos uma
// sanitização mínima no nome (prefixo nps_ removido).
func (s *Store) ListarProdutos(ctx context.Context) ([]string, error) {
	rows, err := s.pool.Query(ctx, `
SELECT tablename
FROM pg_catalog.pg_tables
WHERE schemaname='public' AND tablename LIKE 'nps_%'
ORDER BY tablename`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	produtos := []string{}
	for rows.Next() {
		var t string
		if err := rows.Scan(&t); err != nil {
			return nil, err
		}
		produtos = append(produtos, strings.TrimPrefix(t, "nps_"))
	}
	return produtos, rows.Err()
}

// NPSMesAMes calcula o NPS por mês para um produto (tabela `nps_{produto}`).
//
// Regra NPS (1–10):
// - 1–6 detratores
// - 7–8 neutros
// - 9–10 promotores
func (s *Store) NPSMesAMes(ctx context.Context, tabela string, meses int) ([]contratos.NPSMensal, error) {
	// Segurança: a tabela é um identificador interpolado. Validamos sempre.
	if !db.TableNameValido(tabela) {
		return nil, fmt.Errorf("tabela invalida")
	}
	q := fmt.Sprintf(`
WITH base AS (
  SELECT
    date_trunc('month', respondido_em) AS mes,
    nota
  FROM %s
  WHERE status='respondido'
    AND valida=true
    AND respondido_em IS NOT NULL
    AND respondido_em >= date_trunc('month', now()) - ($1::int * interval '1 month')
)
SELECT
  to_char(mes, 'YYYY-MM') AS mes,
  SUM(CASE WHEN nota BETWEEN 1 AND 6 THEN 1 ELSE 0 END)::int AS detratores,
  SUM(CASE WHEN nota BETWEEN 7 AND 8 THEN 1 ELSE 0 END)::int AS neutros,
  SUM(CASE WHEN nota BETWEEN 9 AND 10 THEN 1 ELSE 0 END)::int AS promotores,
  COUNT(*)::int AS total
FROM base
GROUP BY mes
ORDER BY mes ASC`, tabela)

	rows, err := s.pool.Query(ctx, q, meses)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := []contratos.NPSMensal{}
	for rows.Next() {
		var m contratos.NPSMensal
		if err := rows.Scan(&m.Mes, &m.Detratores, &m.Neutros, &m.Promotores, &m.Total); err != nil {
			return nil, err
		}
		if m.Total > 0 {
			pctProm := float64(m.Promotores) / float64(m.Total) * 100
			pctDet := float64(m.Detratores) / float64(m.Total) * 100
			m.NPS = int((pctProm - pctDet) + 0.5) // arredonda para inteiro
		}
		out = append(out, m)
	}
	return out, rows.Err()
}

type ListarRespostasFiltro struct {
	SomenteNotasBaixas bool
	Pagina             int
	PorPagina          int
}

func (f *ListarRespostasFiltro) normalizar() { (*contratos.ListarRespostasFiltro)(f).Normalizar() }

type ExportarRespostasFiltro struct {
	SomenteNotasBaixas bool
}

// ListarRespostas retorna respostas respondidas, com paginação e filtro.
func (s *Store) ListarRespostas(ctx context.Context, tabela string, filtro ListarRespostasFiltro) ([]contratos.RespostaPainel, error) {
	// Segurança: a tabela é um identificador interpolado. Validamos sempre.
	if !db.TableNameValido(tabela) {
		return nil, fmt.Errorf("tabela invalida")
	}

	filtro.normalizar()
	offset := (filtro.Pagina - 1) * filtro.PorPagina

	cond := "status='respondido' AND valida=true"
	if filtro.SomenteNotasBaixas {
		cond += " AND nota BETWEEN 1 AND 6"
	}

	// Importante (segurança): apesar do cond ser construído em string, ele NÃO usa
	// entrada do usuário diretamente. O filtro "SomenteNotasBaixas" é booleano.
	// A tabela também é validada por regex (TableNameValido).
	q := fmt.Sprintf(`
SELECT
  id,
  respondido_em,
  pedido_criado_em,
	inquilino_codigo,
	inquilino_nome,
  usuario_codigo,
  usuario_nome,
  usuario_email,
	usuario_telefone,
  nota,
  justificativa
FROM %s
WHERE %s
ORDER BY respondido_em DESC NULLS LAST
LIMIT $1 OFFSET $2`, tabela, cond)

	rows, err := s.pool.Query(ctx, q, filtro.PorPagina, offset)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	respostas := []contratos.RespostaPainel{}
	for rows.Next() {
		var r contratos.RespostaPainel
		if err := rows.Scan(
			&r.ID,
			&r.RespondidoEm,
			&r.PedidoCriadoEm,
			&r.InquilinoCodigo,
			&r.InquilinoNome,
			&r.UsuarioCodigo,
			&r.UsuarioNome,
			&r.UsuarioEmail,
			&r.UsuarioTelefone,
			&r.Nota,
			&r.Justificativa,
		); err != nil {
			return nil, err
		}
		respostas = append(respostas, r)
	}
	return respostas, rows.Err()
}

// ExportarRespostas abre um cursor/stream de respostas (sem paginação) para export.
//
// Importante: o caller deve SEMPRE fechar o rows.
func (s *Store) ExportarRespostas(ctx context.Context, tabela string, filtro ExportarRespostasFiltro) (pgx.Rows, error) {
	// Segurança: a tabela é um identificador interpolado. Validamos sempre.
	if !db.TableNameValido(tabela) {
		return nil, fmt.Errorf("tabela invalida")
	}

	cond := "status='respondido' AND valida=true"
	if filtro.SomenteNotasBaixas {
		cond += " AND nota BETWEEN 1 AND 6"
	}

	// Sem LIMIT/OFFSET (export completo).
	q := fmt.Sprintf(`
SELECT
  id,
  respondido_em,
  pedido_criado_em,
	inquilino_codigo,
	inquilino_nome,
  usuario_codigo,
  usuario_nome,
  usuario_email,
	usuario_telefone,
  nota,
  justificativa
FROM %s
WHERE %s
ORDER BY respondido_em DESC NULLS LAST`, tabela, cond)

	rows, err := s.pool.Query(ctx, q)
	if err != nil {
		return nil, err
	}
	return rows, nil
}

// ensure interface imports
var _ = pgx.ErrNoRows
var _ = time.Second
