package render

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"leadb2b.org/internal/api"
)

type SearchForm struct {
	UF                        string
	Municipio                 string
	CNAEFiscalPrincipal       []string
	SituacaoCadastral         []string
	IdentificadorMatrizFilial string
	TemTelefone               string
	TemEmail                  string
	TemSocios                 string
	TemWhatsApp               string
	CNAESecundario            []string
	Q                         string
	CNPJ                      string
	CEP                       string
	Bairro                    string
	Logradouro                string
	MEI                       string
	Simples                   string
	Porte                     string
	NaturezaJuridica          string
	DataInicioAno             string
	DataInicioMes             string
	PageSize                  int
	Cursor                    string
}

type PorteOption struct {
	Codigo    string
	Descricao string
}

func PorteOptions() []PorteOption {
	return []PorteOption{
		{Codigo: "01", Descricao: "Microempresa (ME)"},
		{Codigo: "02", Descricao: "Empresa de pequeno porte (EPP)"},
		{Codigo: "03", Descricao: "Demais (médio/grande porte)"},
		{Codigo: "05", Descricao: "Demais"},
	}
}

func ParseSearchForm(q url.Values) SearchForm {
	ps, _ := strconv.Atoi(q.Get("page_size"))
	cnaes := q["cnae_fiscal_principal"]
	if len(cnaes) > 1 {
		cnaes = cnaes[:1]
	}
	return SearchForm{
		UF:                        q.Get("uf"),
		Municipio:                 q.Get("municipio"),
		CNAEFiscalPrincipal:       cnaes,
		SituacaoCadastral:         q["situacao_cadastral"],
		IdentificadorMatrizFilial: q.Get("identificador_matriz_filial"),
		TemTelefone:               parseOptionalBoolParam(q, "tem_telefone"),
		TemEmail:                  parseOptionalBoolParam(q, "tem_email"),
		TemSocios:                 parseOptionalBoolParam(q, "tem_socios"),
		TemWhatsApp:               parseOptionalBoolParam(q, "tem_whatsapp"),
		CNAESecundario:            q["cnae_secundario"],
		Q:                         strings.TrimSpace(q.Get("q")),
		CNPJ:                      strings.TrimSpace(q.Get("cnpj")),
		CEP:                       strings.TrimSpace(q.Get("cep")),
		Bairro:                    strings.TrimSpace(q.Get("bairro")),
		Logradouro:                strings.TrimSpace(q.Get("logradouro")),
		MEI:                       parseOptionalBoolParam(q, "mei"),
		Simples:                   parseOptionalBoolParam(q, "simples"),
		Porte:                     strings.TrimSpace(q.Get("porte")),
		NaturezaJuridica:          strings.TrimSpace(q.Get("natureza_juridica")),
		DataInicioAno:             q.Get("data_inicio_ano"),
		DataInicioMes:             q.Get("data_inicio_mes"),
		PageSize:                  ps,
		Cursor:                    q.Get("cursor"),
	}
}

// ApplySearchDefaults não impõe filtros — apenas normaliza o que o usuário escolheu.
func ApplySearchDefaults(p SearchForm) SearchForm {
	return p
}

// DefaultAppBuscaParams estado inicial do formulário após limpar ou primeira visita.
func DefaultAppBuscaParams() SearchForm {
	return SearchForm{
		SituacaoCadastral: []string{"2"},
		PageSize:          10,
	}
}

// ApplyAppSearchDefaults aplica situação Ativa quando a busca ainda não foi enviada.
func ApplyAppSearchDefaults(p SearchForm, q url.Values) SearchForm {
	if q.Get("busca_submitted") == "1" {
		return p
	}
	if queryHasUserFilters(q) {
		return p
	}
	if len(p.SituacaoCadastral) == 0 {
		p.SituacaoCadastral = []string{"2"}
	}
	return p
}

func EffectiveQ(p SearchForm) string {
	q := strings.TrimSpace(p.Q)
	cnpj := strings.TrimSpace(p.CNPJ)
	if cnpj != "" && q == "" {
		return cnpj
	}
	return q
}

func ExtractCNPJDigits(raw string) (string, bool) {
	digits := strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, raw)
	if len(digits) == 8 || len(digits) == 14 {
		return digits, true
	}
	return "", false
}

func IsCNPJQuery(s string) bool {
	_, ok := ExtractCNPJDigits(s)
	return ok
}

// RequiresUFOrMunicipio indica se a busca textual exige UF ou município (API).
func RequiresUFOrMunicipio(p SearchForm) bool {
	q := EffectiveQ(p)
	if q == "" || IsCNPJQuery(q) {
		return false
	}
	return p.UF == "" && p.Municipio == ""
}

// AppBuscaLocationRequiredNotice mensagem quando falta escopo regional no painel.
const AppBuscaLocationRequiredNotice = "Selecione UF e município para buscar. Buscas por cidade são mais rápidas."

// RequiresUFAndMunicipio bloqueia buscas amplas sem UF e município no painel.
// Exceção: consulta exclusiva por CNPJ (o CEP pode preencher UF/município antes desta checagem).
func RequiresUFAndMunicipio(p SearchForm) bool {
	if IsCNPJLookup(p) {
		return false
	}
	return p.UF == "" || p.Municipio == ""
}

// IsCNPJLookup indica busca exclusiva por CNPJ sem outros filtros.
func IsCNPJLookup(p SearchForm) bool {
	if p.CNPJ == "" || p.Q != "" {
		return false
	}
	return !hasExtraSearchFilters(p)
}

func hasExtraSearchFilters(p SearchForm) bool {
	return p.UF != "" ||
		p.Municipio != "" ||
		len(p.CNAEFiscalPrincipal) > 0 ||
		len(p.SituacaoCadastral) > 0 ||
		p.IdentificadorMatrizFilial != "" ||
		p.TemTelefone != "" ||
		p.TemEmail != "" ||
		p.TemSocios != "" ||
		p.TemWhatsApp != "" ||
		len(p.CNAESecundario) > 0 ||
		p.CEP != "" ||
		p.Bairro != "" ||
		p.Logradouro != "" ||
		p.MEI != "" ||
		p.Simples != "" ||
		p.Porte != "" ||
		p.NaturezaJuridica != "" ||
		p.DataInicioAno != "" ||
		p.DataInicioMes != "" ||
		p.Cursor != ""
}

func ToAPISearchParams(p SearchForm) api.SearchParams {
	return api.SearchParams{
		UF:                        SanitizeUF(p.UF),
		Municipio:                 p.Municipio,
		CNAEFiscalPrincipal:       p.CNAEFiscalPrincipal,
		SituacaoCadastral:         p.SituacaoCadastral,
		IdentificadorMatrizFilial: p.IdentificadorMatrizFilial,
		TemTelefone:               p.TemTelefone,
		TemEmail:                  p.TemEmail,
		TemSocios:                 p.TemSocios,
		TemWhatsApp:               p.TemWhatsApp,
		CNAESecundario:            p.CNAESecundario,
		Q:                         p.Q,
		CNPJ:                      p.CNPJ,
		CEP:                       p.CEP,
		Bairro:                    p.Bairro,
		Logradouro:                p.Logradouro,
		MEI:                       p.MEI,
		Simples:                   p.Simples,
		Porte:                     p.Porte,
		NaturezaJuridica:          p.NaturezaJuridica,
		DataInicioAno:             p.DataInicioAno,
		DataInicioMes:             p.DataInicioMes,
		PageSize:                  ClampPageSize(p.PageSize),
		Cursor:                    p.Cursor,
	}
}

func ResultSummary(count int, p SearchForm) string {
	loc := "Brasil"
	if p.UF != "" {
		loc = p.UF
	}
	if p.Municipio != "" {
		loc = "município selecionado"
	}
	return fmt.Sprintf("%d empresas encontradas em %s", count, loc)
}

func (p SearchForm) QueryValues() url.Values {
	q := url.Values{}
	if p.UF != "" {
		q.Set("uf", p.UF)
	}
	if p.Municipio != "" {
		q.Set("municipio", p.Municipio)
	}
	for _, c := range p.CNAEFiscalPrincipal {
		q.Add("cnae_fiscal_principal", c)
	}
	for _, s := range p.SituacaoCadastral {
		q.Add("situacao_cadastral", s)
	}
	if p.IdentificadorMatrizFilial != "" {
		q.Set("identificador_matriz_filial", p.IdentificadorMatrizFilial)
	}
	setBoolFilterParam(q, "tem_telefone", p.TemTelefone)
	setBoolFilterParam(q, "tem_email", p.TemEmail)
	setBoolFilterParam(q, "tem_socios", p.TemSocios)
	setBoolFilterParam(q, "mei", p.MEI)
	setBoolFilterParam(q, "simples", p.Simples)
	if p.Q != "" {
		q.Set("q", p.Q)
	}
	if p.CNPJ != "" {
		q.Set("cnpj", p.CNPJ)
	}
	if p.CEP != "" {
		q.Set("cep", p.CEP)
	}
	if p.Bairro != "" {
		q.Set("bairro", p.Bairro)
	}
	if p.Logradouro != "" {
		q.Set("logradouro", p.Logradouro)
	}
	if p.Porte != "" {
		q.Set("porte", p.Porte)
	}
	if p.NaturezaJuridica != "" {
		q.Set("natureza_juridica", p.NaturezaJuridica)
	}
	if p.DataInicioAno != "" {
		q.Set("data_inicio_ano", p.DataInicioAno)
	}
	if p.DataInicioMes != "" {
		q.Set("data_inicio_mes", p.DataInicioMes)
	}
	if p.PageSize > 0 {
		q.Set("page_size", strconv.Itoa(p.PageSize))
	}
	return q
}

func NextPageURL(r *url.URL, cursor string) string {
	q := r.Query()
	q.Set("cursor", cursor)
	return "/busca?" + q.Encode()
}

func parseBoolQuery(v string) bool {
	switch strings.ToLower(strings.TrimSpace(v)) {
	case "1", "true", "yes", "sim", "on":
		return true
	default:
		return false
	}
}

func parseOptionalBoolParam(q url.Values, key string) string {
	v := strings.TrimSpace(q.Get(key))
	if v == "" {
		return ""
	}
	if parseBoolQuery(v) {
		return "true"
	}
	return "false"
}

// ToProductSearchQuery mapeia o formulário para /product/search/companies (paridade com /leads/estabelecimentos).
func ToProductSearchQuery(p SearchForm) url.Values {
	q := url.Values{}
	if p.UF != "" {
		q.Set("uf", SanitizeUF(p.UF))
	}
	if p.Municipio != "" {
		q.Set("municipio", p.Municipio)
	}
	for _, c := range p.CNAEFiscalPrincipal {
		if c != "" {
			q.Add("cnae_fiscal_principal", c)
		}
	}
	for _, c := range p.CNAESecundario {
		if c != "" {
			q.Add("cnae_secundario", c)
		}
	}
	for _, s := range p.SituacaoCadastral {
		if s != "" {
			q.Add("situacao_cadastral", s)
		}
	}
	if p.IdentificadorMatrizFilial != "" {
		q.Set("identificador_matriz_filial", p.IdentificadorMatrizFilial)
	}
	if p.DataInicioAno != "" {
		q.Set("data_inicio_ano", p.DataInicioAno)
	}
	if p.DataInicioMes != "" {
		q.Set("data_inicio_mes", p.DataInicioMes)
	}
	setBoolFilterParam(q, "tem_telefone", p.TemTelefone)
	setBoolFilterParam(q, "tem_email", p.TemEmail)
	setBoolFilterParam(q, "tem_socios", p.TemSocios)
	setBoolFilterParam(q, "tem_whatsapp", p.TemWhatsApp)
	setBoolFilterParam(q, "opcao_mei", p.MEI)
	setBoolFilterParam(q, "opcao_pelo_simples", p.Simples)
	if cep := NormalizeCEPDigits(p.CEP); cep != "" {
		q.Set("cep", cep)
	}
	if p.Bairro != "" {
		q.Set("bairro", p.Bairro)
	}
	if p.Logradouro != "" {
		q.Set("logradouro", p.Logradouro)
	}
	if p.Porte != "" {
		q.Set("porte_empresa", p.Porte)
	}
	if p.NaturezaJuridica != "" {
		q.Set("natureza_juridica", p.NaturezaJuridica)
	}
	if v := effectiveProductSearchQ(p.Q, p.CNPJ); v != "" {
		q.Set("q", v)
	}
	if p.PageSize > 0 {
		q.Set("page_size", strconv.Itoa(p.PageSize))
	}
	if p.Cursor != "" {
		q.Set("cursor", p.Cursor)
	}
	return q
}

func effectiveProductSearchQ(q, cnpj string) string {
	q = strings.TrimSpace(q)
	cnpj = strings.TrimSpace(cnpj)
	if q != "" {
		return q
	}
	if cnpj != "" {
		return cnpj
	}
	return ""
}

// ToLeadsSearchQuery repassa os mesmos filtros da busca product para /leads/estabelecimentos.
func ToLeadsSearchQuery(p SearchForm) url.Values {
	q := ToProductSearchQuery(p)
	if v := q.Get("porte_empresa"); v != "" {
		q.Del("porte_empresa")
		q.Set("porte", v)
	}
	if v := q.Get("opcao_mei"); v != "" {
		q.Del("opcao_mei")
		q.Set("mei", v)
	}
	if v := q.Get("opcao_pelo_simples"); v != "" {
		q.Del("opcao_pelo_simples")
		q.Set("simples", v)
	}
	return q
}

func AppBuscaNextPageURL(params SearchForm, cursor string) string {
	q := ToProductSearchQuery(params)
	q.Set("cursor", cursor)
	return "/app/busca?" + q.Encode()
}

func setBoolFilterParam(q url.Values, key, value string) {
	switch value {
	case "true", "false":
		q.Set(key, value)
	}
}

func BuildQueryFromMap(m map[string]string, extras map[string][]string) string {
	params := make(map[string]string, len(m))
	for k, v := range m {
		if v != "" && v != "0" {
			params[k] = v
		}
	}
	return BuildQueryString(params, extras)
}
