package elinps

import (
	"crypto/subtle"
	"encoding/csv"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"e-li.nps/internal/contratos"
	"e-li.nps/internal/db"
	"github.com/jackc/pgx/v5"
)

// Proteção simples do painel administrativo.
//
// Objetivo: bloquear acesso ao painel com uma senha definida no .env.
// Implementação: cookie simples com token aleatório por boot.
//
// Observação: propositalmente simples (sem banco) para manter o projeto leve.
type AuthPainel struct {
	Senha string
	Token string
}

// Regex pré-compilada (evita recompilar a cada request).
var naoDigitosRe = regexp.MustCompile(`\D`)

func (a AuthPainel) handlerLoginPost(w http.ResponseWriter, r *http.Request) {
	if !a.habilitado() {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte("painel desabilitado"))
		return
	}
	if err := r.ParseForm(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	senha := r.FormValue("senha")
	if subtle.ConstantTimeCompare([]byte(senha), []byte(a.Senha)) != 1 {
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte("senha invalida"))
		return
	}

	http.SetCookie(w, &http.Cookie{
		Name:     a.cookieName(),
		Value:    a.Token,
		Path:     "/",
		HttpOnly: true,
		SameSite: http.SameSiteLaxMode,
		// Secure deve ser true em produção com HTTPS.
		Secure: false,
		// Expira em 24h (relogin simples).
		Expires: time.Now().Add(24 * time.Hour),
	})

	http.Redirect(w, r, "/painel", http.StatusFound)
}

func (a AuthPainel) handlerLoginGet(w http.ResponseWriter, r *http.Request) {
	// HTML mínimo para evitar dependências.
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte(`<!doctype html>
<html lang="pt-br">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>e-li.nps • Painel</title>
  <style>
    body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;padding:24px;}
    .card{max-width:420px;margin:0 auto;border:1px solid #e5e5e5;border-radius:12px;padding:16px;background:#fff;}
    label{display:block;font-size:12px;color:#444;margin-bottom:6px;}
    input{width:100%;padding:10px;border:1px solid #ddd;border-radius:10px;}
    button{margin-top:12px;width:100%;padding:10px 14px;border-radius:10px;border:1px solid #111;background:#111;color:#fff;cursor:pointer;}
    .muted{color:#555;font-size:13px;}
  </style>
</head>
<body>
  <div class="card">
    <h1>e-li.nps • Painel</h1>
    <p class="muted">Acesso protegido por senha (SENHA_PAINEL).</p>
    <form method="POST" action="/painel/login">
      <label>Senha</label>
      <input type="password" name="senha" autocomplete="current-password" />
      <button type="submit">Entrar</button>
    </form>
  </div>
</body>
</html>`))
}

func (a AuthPainel) habilitado() bool   { return a.Senha != "" && a.Token != "" }
func (a AuthPainel) cookieName() string { return "eli_nps_painel" }

func (a AuthPainel) isAutenticado(r *http.Request) bool {
	c, err := r.Cookie(a.cookieName())
	if err != nil {
		return false
	}
	return subtle.ConstantTimeCompare([]byte(c.Value), []byte(a.Token)) == 1
}

func (a AuthPainel) middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !a.habilitado() {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("painel desabilitado"))
			return
		}
		if a.isAutenticado(r) {
			next.ServeHTTP(w, r)
			return
		}
		http.Redirect(w, r, "/painel/login", http.StatusFound)
	})
}

type NPSMensal = contratos.NPSMensal
type RespostaPainel = contratos.RespostaPainel
type PainelDados = contratos.PainelDados

func (a AuthPainel) handlerPainel(w http.ResponseWriter, r *http.Request, store *Store) {
	ctx := r.Context()

	// Query params
	produto := r.URL.Query().Get("produto")
	pagina := 1
	if p := r.URL.Query().Get("pagina"); p != "" {
		// best-effort parse
		_, _ = fmt.Sscanf(p, "%d", &pagina)
		if pagina <= 0 {
			pagina = 1
		}
	}
	somenteBaixas := r.URL.Query().Get("baixas") == "1"

	produtos, err := store.ListarProdutos(ctx)
	if err != nil {
		a.renderPainelHTML(w, PainelDados{MsgErro: "erro ao listar produtos"})
		return
	}
	if produto == "" && len(produtos) > 0 {
		produto = produtos[0]
	}

	dados := PainelDados{Produto: produto, Produtos: produtos, Pagina: pagina, SomenteBaixas: somenteBaixas}
	if produto == "" {
		a.renderPainelHTML(w, dados)
		return
	}

	// tabela segura
	prodNorm, err := db.NormalizeProduto(produto)
	if err != nil {
		dados.MsgErro = "produto inválido"
		a.renderPainelHTML(w, dados)
		return
	}
	tabela := db.TableNameForProduto(prodNorm)
	if err := db.EnsureNPSTable(ctx, store.poolRef(), tabela); err != nil {
		dados.MsgErro = "erro ao garantir tabela"
		a.renderPainelHTML(w, dados)
		return
	}

	meses, err := store.NPSMesAMes(ctx, tabela, 12)
	if err != nil {
		dados.MsgErro = "erro ao calcular NPS"
		a.renderPainelHTML(w, dados)
		return
	}
	dados.Meses = meses

	respostas, err := store.ListarRespostas(ctx, tabela, ListarRespostasFiltro{SomenteNotasBaixas: somenteBaixas, Pagina: pagina, PorPagina: 50})
	if err != nil {
		if err == pgx.ErrNoRows {
			respostas = []contratos.RespostaPainel{}
		} else {
			dados.MsgErro = "erro ao listar respostas"
		}
	}
	dados.Respostas = respostas

	a.renderPainelHTML(w, dados)
}

func (a AuthPainel) handlerExportCSV(w http.ResponseWriter, r *http.Request, store *Store) {
	ctx := r.Context()

	produto := r.URL.Query().Get("produto")
	somenteBaixas := r.URL.Query().Get("baixas") == "1"
	if strings.TrimSpace(produto) == "" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("produto obrigatorio"))
		return
	}

	prodNorm, err := db.NormalizeProduto(produto)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("produto invalido"))
		return
	}
	tabela := db.TableNameForProduto(prodNorm)
	if err := db.EnsureNPSTable(ctx, store.poolRef(), tabela); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("erro ao garantir tabela"))
		return
	}

	rows, err := store.ExportarRespostas(ctx, tabela, ExportarRespostasFiltro{SomenteNotasBaixas: somenteBaixas})
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("erro ao listar respostas"))
		return
	}
	defer rows.Close()

	agora := time.Now().Format("20060102_150405")
	nome := "respostas_" + prodNorm
	if somenteBaixas {
		nome += "_baixas"
	}
	nome += "_" + agora + ".csv"

	w.Header().Set("Content-Type", "text/csv; charset=utf-8")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", nome))

	// BOM UTF-8 para Excel/PT-BR abrir corretamente.
	_, _ = w.Write([]byte("\xEF\xBB\xBF"))

	cw := csv.NewWriter(w)
	cw.Comma = ';'
	defer cw.Flush()

	formatarDataBR := func(t time.Time) string {
		loc, err := time.LoadLocation("America/Sao_Paulo")
		if err != nil {
			loc = time.FixedZone("America/Sao_Paulo", -3*60*60)
		}
		return t.In(loc).Format("02/01/2006 15:04")
	}

	_ = cw.Write([]string{
		"data",
		"nota",
		"usuario_nome",
		"usuario_codigo",
		"inquilino_nome",
		"inquilino_codigo",
		"email",
		"telefone",
		"comentario",
	})

	for rows.Next() {
		var rr RespostaPainel
		if err := rows.Scan(
			&rr.ID,
			&rr.RespondidoEm,
			&rr.PedidoCriadoEm,
			&rr.InquilinoCodigo,
			&rr.InquilinoNome,
			&rr.UsuarioCodigo,
			&rr.UsuarioNome,
			&rr.UsuarioEmail,
			&rr.UsuarioTelefone,
			&rr.Nota,
			&rr.Justificativa,
		); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		data := ""
		if rr.RespondidoEm != nil {
			data = formatarDataBR(*rr.RespondidoEm)
		} else {
			data = formatarDataBR(rr.PedidoCriadoEm)
		}

		nota := ""
		if rr.Nota != nil {
			nota = strconv.Itoa(*rr.Nota)
		}

		usuarioCod := ""
		if rr.UsuarioCodigo != nil {
			usuarioCod = *rr.UsuarioCodigo
		}
		email := ""
		if rr.UsuarioEmail != nil {
			email = *rr.UsuarioEmail
		}
		tel := ""
		if rr.UsuarioTelefone != nil {
			tel = *rr.UsuarioTelefone
		}
		coment := ""
		if rr.Justificativa != nil {
			coment = *rr.Justificativa
		}

		_ = cw.Write([]string{
			data,
			nota,
			rr.UsuarioNome,
			usuarioCod,
			rr.InquilinoNome,
			rr.InquilinoCodigo,
			email,
			tel,
			coment,
		})

		// Flush incremental para streaming.
		cw.Flush()
		if err := cw.Error(); err != nil {
			return
		}
		if f, ok := w.(http.Flusher); ok {
			f.Flush()
		}
	}
}

func (a AuthPainel) renderPainelHTML(w http.ResponseWriter, d PainelDados) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	formatarDataPainel := func(t time.Time) string {
		loc, err := time.LoadLocation("America/Sao_Paulo")
		if err != nil {
			loc = time.FixedZone("America/Sao_Paulo", -3*60*60)
		}
		return t.In(loc).Format("02/01/2006 15:04")
	}

	soDigitos := func(s string) string {
		return naoDigitosRe.ReplaceAllString(s, "")
	}

	linkEmail := func(email string) string {
		email = strings.TrimSpace(email)
		if email == "" {
			return "-"
		}
		href := "mailto:" + url.PathEscape(email)
		txt := template.HTMLEscapeString(email)
		return "<a href=\"" + href + "\">" + txt + "</a>"
	}

	linkTelefone := func(telOriginal string) (hrefTel string, hrefWA string, htmlTel string) {
		telOriginal = strings.TrimSpace(telOriginal)
		if telOriginal == "" {
			return "", "", "-"
		}
		dig := soDigitos(telOriginal)
		if dig == "" {
			return "", "", template.HTMLEscapeString(telOriginal)
		}
		hrefTel = "tel:+" + dig
		waNum := dig
		if (len(waNum) == 10 || len(waNum) == 11) && !strings.HasPrefix(waNum, "55") {
			waNum = "55" + waNum
		}
		hrefWA = "https://wa.me/" + waNum
		htmlTel = "<a href=\"" + hrefTel + "\">" + template.HTMLEscapeString(telOriginal) + "</a>"
		return hrefTel, hrefWA, htmlTel
	}

	var b strings.Builder
	b.WriteString("<!doctype html><html lang=\"pt-br\"><head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>")
	b.WriteString("<title>e-li.nps • Painel</title>")
	b.WriteString(`<style>
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:0;padding:18px;background:#fafafa;color:#111;}
.top{display:flex;gap:12px;flex-wrap:wrap;align-items:center;}
.card{background:#fff;border:1px solid #e5e5e5;border-radius:12px;padding:14px;}
select,input{padding:10px;border:1px solid #ddd;border-radius:10px;}
a{color:#111}
table{width:100%;border-collapse:collapse;font-size:13px;}
th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;vertical-align:top;}
.muted{color:#666;font-size:12px;}
.badge{display:inline-block;padding:2px 8px;border-radius:999px;background:#f2f2f2;border:1px solid #e5e5e5;font-size:12px;}
.iconbtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:10px;border:1px solid #e5e5e5;background:#fff;margin-right:6px;text-decoration:none;}
.iconbtn:hover{border-color:#bbb;background:#fafafa;}
.icon{width:16px;height:16px;display:block;}

/* Responsivo */
.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch;}
.tablewrap table{min-width:900px;}

@media (max-width: 640px){
  body{padding:12px;}
  .card{padding:12px;}
  h1{font-size:18px;}
}
</style></head><body>`)

	b.WriteString("<div class=\"top\">")
	b.WriteString("<div class=\"card\"><h1 style=\"margin:0 0 8px\">e-li.nps • Painel</h1>")
	if d.MsgErro != "" {
		b.WriteString("<p class=\"badge\">" + template.HTMLEscapeString(d.MsgErro) + "</p>")
	}
	b.WriteString("<form method=\"GET\" action=\"/painel\" style=\"display:flex;gap:10px;flex-wrap:wrap;align-items:center\">")
	b.WriteString("<label class=\"muted\">Produto</label>")
	b.WriteString("<select name=\"produto\">")
	for _, p := range d.Produtos {
		sel := ""
		if p == d.Produto {
			sel = " selected"
		}
		b.WriteString("<option value=\"" + template.HTMLEscapeString(p) + "\"" + sel + ">" + template.HTMLEscapeString(p) + "</option>")
	}
	b.WriteString("</select>")
	chk := ""
	if d.SomenteBaixas {
		chk = "checked"
	}
	b.WriteString("<label class=\"muted\"><input type=\"checkbox\" name=\"baixas\" value=\"1\" " + chk + "/> notas baixas (<=6)</label>")
	b.WriteString("<button type=\"submit\" style=\"padding:10px 14px;border-radius:10px;border:1px solid #111;background:#111;color:#fff;cursor:pointer\">Aplicar</button>")

	// Export CSV (mantém filtros atuais)
	exportURL := "/painel/export.csv?produto=" + url.QueryEscape(d.Produto)
	if d.SomenteBaixas {
		exportURL += "&baixas=1"
	}
	b.WriteString("<a class=\"badge\" style=\"text-decoration:none;display:inline-block;padding:10px 14px\" href=\"" + exportURL + "\">Exportar CSV</a>")

	b.WriteString("</form></div>")
	b.WriteString("</div>")

	// NPS mês a mês
	b.WriteString("<div class=\"card\" style=\"margin-top:12px\"><h2 style=\"margin:0 0 8px\">NPS mês a mês</h2>")
	b.WriteString("<div class=\"tablewrap\"><table><thead><tr><th>Mês</th><th>Detratores</th><th>Neutros</th><th>Promotores</th><th>Total</th><th>NPS</th></tr></thead><tbody>")
	for _, m := range d.Meses {
		b.WriteString(fmt.Sprintf("<tr><td>%s</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td><b>%d</b></td></tr>",
			template.HTMLEscapeString(m.Mes), m.Detratores, m.Neutros, m.Promotores, m.Total, m.NPS))
	}
	b.WriteString("</tbody></table></div></div>")

	// Respostas
	b.WriteString("<div class=\"card\" style=\"margin-top:12px\"><h2 style=\"margin:0 0 8px\">Respostas</h2>")
	b.WriteString("<div class=\"tablewrap\"><table><thead><tr><th>Data</th><th>Nota</th><th>Usuário</th><th>Inquilino</th><th>Email</th><th>Telefone</th><th>Comentário</th><th>Ações</th></tr></thead><tbody>")
	for _, r := range d.Respostas {
		data := "-"
		if r.RespondidoEm != nil {
			data = formatarDataPainel(*r.RespondidoEm)
		} else {
			data = formatarDataPainel(r.PedidoCriadoEm)
		}
		nota := "-"
		if r.Nota != nil {
			nota = fmt.Sprintf("%d", *r.Nota)
		}
		usuario := template.HTMLEscapeString(r.UsuarioNome)
		if r.UsuarioCodigo != nil {
			usuario += " <span class=\"muted\">(" + template.HTMLEscapeString(*r.UsuarioCodigo) + ")</span>"
		}
		inquilino := template.HTMLEscapeString(r.InquilinoNome) + " <span class=\"muted\">(" + template.HTMLEscapeString(r.InquilinoCodigo) + ")</span>"

		emailHTML := "-"
		emailHref := ""
		if r.UsuarioEmail != nil && strings.TrimSpace(*r.UsuarioEmail) != "" {
			emailHTML = linkEmail(*r.UsuarioEmail)
			emailHref = "mailto:" + url.PathEscape(strings.TrimSpace(*r.UsuarioEmail))
		}

		telHref := ""
		waHref := ""
		telefoneHTML := "-"
		if r.UsuarioTelefone != nil && strings.TrimSpace(*r.UsuarioTelefone) != "" {
			telHref, waHref, telefoneHTML = linkTelefone(*r.UsuarioTelefone)
		}

		iconMail := `<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6h16v12H4V6Z" stroke="currentColor" stroke-width="2"/><path d="m4 7 8 6 8-6" stroke="currentColor" stroke-width="2"/></svg>`
		iconPhone := `<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 3h4l2 6-3 2c1.5 3 4 5.5 7 7l2-3 6 2v4c0 1-1 2-2 2C11 23 1 13 2 5c0-1 1-2 2-2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>`
		iconWA := `<svg class="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20 11.5c0 4.142-3.582 7.5-8 7.5-1.403 0-2.722-.339-3.87-.937L4 19l1.05-3.322A7.08 7.08 0 0 1 4 11.5C4 7.358 7.582 4 12 4s8 3.358 8 7.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>`

		acoes := ""
		if emailHref != "" {
			acoes += "<a class=\"iconbtn\" href=\"" + emailHref + "\" title=\"Enviar email\">" + iconMail + "</a>"
		}
		if waHref != "" {
			acoes += "<a class=\"iconbtn\" href=\"" + waHref + "\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Abrir WhatsApp\">" + iconWA + "</a>"
		}
		if telHref != "" {
			acoes += "<a class=\"iconbtn\" href=\"" + telHref + "\" title=\"Ligar\">" + iconPhone + "</a>"
		}
		if acoes == "" {
			acoes = "-"
		}

		coment := ""
		if r.Justificativa != nil {
			coment = template.HTMLEscapeString(*r.Justificativa)
		}
		b.WriteString("<tr><td>" + template.HTMLEscapeString(data) + "</td><td><b>" + template.HTMLEscapeString(nota) + "</b></td><td>" + usuario + "</td><td>" + inquilino + "</td><td>" + emailHTML + "</td><td>" + telefoneHTML + "</td><td>" + coment + "</td><td>" + acoes + "</td></tr>")
	}
	b.WriteString("</tbody></table></div>")

	// Navegação
	base := "/painel?produto=" + url.QueryEscape(d.Produto)
	if d.SomenteBaixas {
		base += "&baixas=1"
	}
	prev := d.Pagina - 1
	if prev < 1 {
		prev = 1
	}
	next := d.Pagina + 1
	b.WriteString("<div style=\"display:flex;gap:10px;justify-content:flex-end;margin-top:10px\">")
	b.WriteString("<a class=\"badge\" href=\"" + base + "&pagina=" + fmt.Sprintf("%d", prev) + "\">Anterior</a>")
	b.WriteString("<span class=\"muted\">Página " + fmt.Sprintf("%d", d.Pagina) + "</span>")
	b.WriteString("<a class=\"badge\" href=\"" + base + "&pagina=" + fmt.Sprintf("%d", next) + "\">Próxima</a>")
	b.WriteString("</div>")

	b.WriteString("</div>")

	// JS do painel: bootstrap do WASM.
	b.WriteString(`<script src="/static/wasm_exec.js"></script><script src="/static/painel.js"></script>`)

	b.WriteString("</body></html>")
	w.Write([]byte(b.String()))
}
