package render

import (
	"fmt"
	"strings"

	"leadb2b.org/internal/api"
)

type ContactBadge struct {
	Icon   string
	Label  string
	Active bool
}

func phoneSlotFilled(ddd, tel *string) bool {
	return ddd != nil && tel != nil && *ddd != "" && *tel != ""
}

// TelefoneCount retorna quantos telefones o item da listagem possui (0–2).
func TelefoneCount(item api.EstabelecimentoOut) int {
	n := 0
	if phoneSlotFilled(item.DDD1, item.Telefone1) {
		n++
	}
	if phoneSlotFilled(item.DDD2, item.Telefone2) {
		n++
	}
	return n
}

// SociosCountForItem consulta o mapa de enriquecimento por CNPJ básico.
func SociosCountForItem(item api.EstabelecimentoOut, counts map[string]int) int {
	if counts == nil {
		return 0
	}
	return counts[item.CNPJBasico]
}

func pluralLabel(n int, singular, plural string) string {
	if n == 1 {
		return fmt.Sprintf("1 %s", singular)
	}
	return fmt.Sprintf("%d %s", n, plural)
}

// ListContactBadges monta os indicadores visuais de contato para a listagem.
func ListContactBadges(item api.EstabelecimentoOut, sociosCounts map[string]int) []ContactBadge {
	var badges []ContactBadge

	if tel := TelefoneCount(item); tel > 0 {
		badges = append(badges, ContactBadge{
			Icon:   "fa-phone",
			Label:  pluralLabel(tel, "telefone", "telefones"),
			Active: true,
		})
	}
	if HasEmail(item) {
		badges = append(badges, ContactBadge{
			Icon:   "fa-envelope",
			Label:  "1 e-mail",
			Active: true,
		})
	}
	if socios := SociosCountForItem(item, sociosCounts); socios > 0 {
		badges = append(badges, ContactBadge{
			Icon:   "fa-users",
			Label:  pluralLabel(socios, "sócio", "sócios"),
			Active: true,
		})
	}
	return badges
}

// FullCNPJDigits retorna 14 dígitos ou string vazia.
func FullCNPJDigits(raw string) string {
	d := cnpjDigitsLoose(raw)
	if len(d) == 14 {
		return d
	}
	return ""
}

func cnpjDigitsLoose(raw string) string {
	return strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, raw)
}

// PartialCNPJLookupKey extrai os dígitos visíveis do CNPJ mascarado (**.XXX.XXX/XXXX-**).
func PartialCNPJLookupKey(cnpjOrMasked string) string {
	d := cnpjDigitsLoose(cnpjOrMasked)
	if len(d) == 14 {
		return d[2:12]
	}
	if len(d) >= 4 {
		return d
	}
	return ""
}

// MaskedItemsNeedCNPJ indica se algum item ainda não tem 14 dígitos para kanban/export.
func MaskedItemsNeedCNPJ(items []api.MaskedCompanyOut) bool {
	for _, item := range items {
		if MaskedCompanyCNPJ(item) == "" {
			return true
		}
	}
	return false
}

func partialCNPJMatchesFull(full14, masked string) bool {
	fd := cnpjDigitsLoose(full14)
	md := cnpjDigitsLoose(masked)
	if len(fd) != 14 || len(md) < 4 {
		return false
	}
	if strings.Contains(fd, md) {
		return true
	}
	mid := fd[2:12]
	if strings.Contains(mid, md) || strings.Contains(md, mid) {
		return true
	}
	branch := fd[8:12]
	return md == branch || strings.Contains(md, branch)
}

func normalizeCompanyNameKey(s string) string {
	s = strings.ToLower(strings.TrimSpace(s))
	if s == "" {
		return ""
	}
	var b strings.Builder
	for _, r := range s {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
			b.WriteRune(r)
		}
	}
	return b.String()
}

func maskedCompanyNameKey(m api.MaskedCompanyOut) string {
	return normalizeCompanyNameKey(derefStr(m.CompanyName))
}

func establishmentNameKey(e api.EstabelecimentoOut) string {
	name := normalizeCompanyNameKey(e.RazaoSocial)
	if e.NomeFantasia != nil {
		if nf := normalizeCompanyNameKey(*e.NomeFantasia); nf != "" {
			return nf
		}
	}
	return name
}

func maskedSamePlace(m api.MaskedCompanyOut, e api.EstabelecimentoOut) bool {
	uf := strings.TrimSpace(derefStr(m.UF))
	if uf != "" && !strings.EqualFold(uf, strings.TrimSpace(e.UF)) {
		return false
	}
	city := normalizeCompanyNameKey(derefStr(m.City))
	estCity := normalizeCompanyNameKey(e.MunicipioNome)
	if city != "" && estCity != "" && city != estCity {
		return false
	}
	return true
}

func maskedMatchesEstablishment(m api.MaskedCompanyOut, e api.EstabelecimentoOut) bool {
	d := cnpjDigitsLoose(e.CNPJ)
	if len(d) != 14 {
		return false
	}
	if partialCNPJMatchesFull(d, m.CNPJMasked) {
		return true
	}
	if !maskedSamePlace(m, e) {
		return false
	}
	mn := maskedCompanyNameKey(m)
	en := establishmentNameKey(e)
	if mn == "" || en == "" {
		return false
	}
	return mn == en || strings.Contains(en, mn) || strings.Contains(mn, en)
}

func assignMaskedCNPJ(item *api.MaskedCompanyOut, digits14 string) {
	if item == nil || len(digits14) != 14 {
		return
	}
	c := digits14
	item.CNPJ = &c
}

// EnrichMaskedSearchCNPJs preenche CNPJ completo a partir da busca Global API (servidor).
func EnrichMaskedSearchCNPJs(items []api.MaskedCompanyOut, full []api.EstabelecimentoOut) []api.MaskedCompanyOut {
	if len(items) == 0 || len(full) == 0 || !MaskedItemsNeedCNPJ(items) {
		return items
	}
	byKey := make(map[string]string, len(full)*2)
	for _, e := range full {
		d := cnpjDigitsLoose(e.CNPJ)
		if len(d) != 14 {
			continue
		}
		byKey[companyLookupKeyFromCNPJ(d)] = d
		byKey[establishmentLookupKey(e)] = d
	}
	out := make([]api.MaskedCompanyOut, len(items))
	copy(out, items)
	for i := range out {
		if MaskedCompanyCNPJ(out[i]) != "" {
			continue
		}
		if i < len(full) && maskedMatchesEstablishment(out[i], full[i]) {
			assignMaskedCNPJ(&out[i], cnpjDigitsLoose(full[i].CNPJ))
			if MaskedCompanyCNPJ(out[i]) != "" {
				continue
			}
		}
		for _, key := range maskedItemLookupKeys(out[i]) {
			if cnpj, ok := byKey[key]; ok {
				assignMaskedCNPJ(&out[i], cnpj)
				break
			}
		}
	}
	// Mesma consulta/página: product e leads retornam a mesma ordem — alinha por índice.
	if len(out) == len(full) {
		for i := range out {
			if MaskedCompanyCNPJ(out[i]) != "" {
				continue
			}
			d := cnpjDigitsLoose(full[i].CNPJ)
			if len(d) == 14 && maskedSamePlace(out[i], full[i]) {
				assignMaskedCNPJ(&out[i], d)
			}
		}
	}
	return out
}

func derefStr(s *string) string {
	if s == nil {
		return ""
	}
	return *s
}

func companyLookupKeyFromCNPJ(digits14 string) string {
	if len(digits14) != 14 {
		return ""
	}
	return "p:" + PartialCNPJLookupKey(digits14)
}

func maskedItemLookupKeys(m api.MaskedCompanyOut) []string {
	var keys []string
	if k := PartialCNPJLookupKey(m.CNPJMasked); k != "" {
		keys = append(keys, "p:"+k)
	}
	name := strings.ToLower(strings.TrimSpace(derefStr(m.CompanyName)))
	uf := strings.TrimSpace(derefStr(m.UF))
	city := strings.ToLower(strings.TrimSpace(derefStr(m.City)))
	if name != "" {
		keys = append(keys, "n:"+uf+":"+city+":"+name)
	}
	return keys
}

func establishmentLookupKey(e api.EstabelecimentoOut) string {
	name := strings.ToLower(strings.TrimSpace(e.RazaoSocial))
	if e.NomeFantasia != nil {
		if nf := strings.TrimSpace(*e.NomeFantasia); nf != "" {
			name = strings.ToLower(nf)
		}
	}
	city := strings.ToLower(strings.TrimSpace(e.MunicipioNome))
	return "n:" + strings.TrimSpace(e.UF) + ":" + city + ":" + name
}

// MaskedCompanyCNPJ retorna 14 dígitos para ações do painel (kanban/export).
func MaskedCompanyCNPJ(m api.MaskedCompanyOut) string {
	if m.CNPJ != nil {
		if d := ExtractCNPJDigitsOnly(*m.CNPJ); len(d) == 14 {
			return d
		}
	}
	if d := ExtractCNPJDigitsOnly(m.CNPJMasked); len(d) == 14 {
		return d
	}
	return ""
}

// MaskedContactBadges monta indicadores para resultados mascarados do painel.
func MaskedContactBadges(m api.MaskedCompanyOut) []ContactBadge {
	var badges []ContactBadge
	if m.HasPhone {
		badges = append(badges, ContactBadge{Icon: "fa-phone", Label: "Telefone", Active: true})
	}
	if m.HasEmail {
		badges = append(badges, ContactBadge{Icon: "fa-envelope", Label: "E-mail", Active: true})
	}
	if m.HasWhatsApp {
		badges = append(badges, ContactBadge{Icon: "fa-whatsapp", Label: "WhatsApp", Active: true})
	}
	return badges
}
