package render

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"

	"leadb2b.org/internal/api"
)

// DisplayCNPJForInput formata CNPJ para exibição em campos de formulário (14 ou 8 dígitos).
func DisplayCNPJForInput(raw string) string {
	digits := strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, raw)
	switch len(digits) {
	case 14:
		return fmt.Sprintf("%s.%s.%s/%s-%s", digits[:2], digits[2:5], digits[5:8], digits[8:12], digits[12:14])
	case 8:
		return fmt.Sprintf("%s.%s.%s", digits[:2], digits[2:5], digits[5:8])
	default:
		return strings.TrimSpace(raw)
	}
}

func FmtCNPJ(c string) string {
	c = strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, c)
	c = fmt.Sprintf("%014s", c)
	if len(c) != 14 {
		return c
	}
	return fmt.Sprintf("%s.%s.%s/%s-%s", c[:2], c[2:5], c[5:8], c[8:12], c[12:14])
}

func FantasiaOrDash(s *string) string {
	if s == nil || *s == "" {
		return "—"
	}
	return *s
}

// EmpresaNomeExibicao retorna o nome em destaque na página: nome fantasia, se houver, senão razão social.
func EmpresaNomeExibicao(d *api.EstabelecimentoDetailOut) string {
	if d == nil {
		return ""
	}
	if HasText(d.NomeFantasia) {
		return strings.TrimSpace(*d.NomeFantasia)
	}
	return strings.TrimSpace(d.Empresa.RazaoSocial)
}

// SiteURLJoin monta URL absoluta a partir do site e do path.
func SiteURLJoin(siteURL, path string) string {
	return strings.TrimRight(siteURL, "/") + path
}

// EmpresaNomeExibicaoList mesmo critério da listagem de busca (fantasia ou razão social).
func EmpresaNomeExibicaoList(item api.EstabelecimentoOut) string {
	if HasText(item.NomeFantasia) {
		return strings.TrimSpace(*item.NomeFantasia)
	}
	return strings.TrimSpace(item.RazaoSocial)
}

// EmpresaDisplayAddress retorna o endereço legível na ficha.
func EmpresaDisplayAddress(d *api.EstabelecimentoDetailOut) string {
	q := EmpresaMapAddress(d)
	return strings.TrimSuffix(q, ", Brasil")
}

// EmpresaMapAddress monta o texto para geocodificação no OpenStreetMap.
func EmpresaMapAddress(d *api.EstabelecimentoDetailOut) string {
	if d == nil {
		return ""
	}
	if s := strings.TrimSpace(d.Endereco.EnderecoCompleto); s != "" {
		return s + ", Brasil"
	}
	var parts []string
	if d.Endereco.TipoLogradouro != "" || d.Endereco.Logradouro != "" {
		parts = append(parts, strings.TrimSpace(d.Endereco.TipoLogradouro+" "+d.Endereco.Logradouro))
	}
	if d.Endereco.Numero != "" {
		parts = append(parts, d.Endereco.Numero)
	}
	if d.Endereco.Bairro != "" {
		parts = append(parts, d.Endereco.Bairro)
	}
	if d.Endereco.Municipio.Descricao != "" {
		parts = append(parts, d.Endereco.Municipio.Descricao)
	}
	if d.Endereco.UF != "" {
		parts = append(parts, d.Endereco.UF)
	}
	if d.Endereco.CEP != "" {
		parts = append(parts, d.Endereco.CEP)
	}
	if len(parts) == 0 {
		return ""
	}
	return strings.Join(parts, ", ") + ", Brasil"
}

func HasText(s *string) bool {
	return s != nil && strings.TrimSpace(*s) != ""
}

func HasTextStr(s string) bool {
	return strings.TrimSpace(s) != ""
}

func ListBadge(item api.EstabelecimentoOut) string {
	var parts []string
	if item.DDD1 != nil && item.Telefone1 != nil && *item.DDD1 != "" && *item.Telefone1 != "" {
		parts = append(parts, "Telefone")
	}
	if item.CorreioEletronico != nil && *item.CorreioEletronico != "" {
		parts = append(parts, "E-mail")
	}
	if len(parts) == 0 {
		return ""
	}
	return strings.Join(parts, " · ")
}

func CNAEShort(desc string) string {
	if i := strings.Index(desc, ","); i > 0 {
		return desc[:i]
	}
	return desc
}

func parseDate(value any) *time.Time {
	switch v := value.(type) {
	case nil:
		return nil
	case string:
		if len(v) >= 10 && v[4] == '-' {
			t, err := time.Parse("2006-01-02", v[:10])
			if err == nil {
				return &t
			}
		}
		if len(v) == 8 {
			y, _ := strconv.Atoi(v[:4])
			m, _ := strconv.Atoi(v[4:6])
			d, _ := strconv.Atoi(v[6:8])
			t := time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC)
			return &t
		}
	case float64:
		s := fmt.Sprintf("%.0f", v)
		if len(s) == 8 {
			y, _ := strconv.Atoi(s[:4])
			m, _ := strconv.Atoi(s[4:6])
			d, _ := strconv.Atoi(s[6:8])
			t := time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC)
			return &t
		}
	}
	return nil
}

func dataAtividadeParts(detail *api.EstabelecimentoDetailOut) (string, string) {
	dt := parseDate(detail.DataInicioAtividadeFormatada)
	if dt == nil {
		dt = parseDate(detail.DataInicioAtividade)
	}
	if dt == nil {
		return "—", ""
	}
	fmtPt := dt.Format("02/01/2006")
	today := time.Now()
	years := today.Year() - dt.Year()
	if today.YearDay() < dt.YearDay() {
		years--
	}
	if years >= 1 {
		label := "anos"
		if years == 1 {
			label = "ano"
		}
		return fmtPt, fmt.Sprintf("%d %s", years, label)
	}
	months := (today.Year()-dt.Year())*12 + int(today.Month()-dt.Month())
	if today.Day() < dt.Day() {
		months--
	}
	if months >= 1 {
		label := "meses"
		if months == 1 {
			label = "mês"
		}
		return fmtPt, fmt.Sprintf("%d %s", months, label)
	}
	return fmtPt, "menos de 1 mês"
}

func DataAtividadeHTML(detail *api.EstabelecimentoDetailOut) string {
	fmtPt, tempo := dataAtividadeParts(detail)
	if tempo == "" {
		return fmtPt
	}
	return fmt.Sprintf(`%s <span class="twm-data-elapsed">· %s</span>`, fmtPt, tempo)
}

func DataAtividadeSidebar(detail *api.EstabelecimentoDetailOut) string {
	fmtPt, tempo := dataAtividadeParts(detail)
	if tempo == "" {
		return fmtPt
	}
	return fmt.Sprintf(`%s<br><span class="twm-data-elapsed">%s de atividade</span>`, fmtPt, tempo)
}

func FormatCapital(s string) string {
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return s
	}
	return fmt.Sprintf("R$ %s", formatBRFloat(f))
}

func formatBRFloat(f float64) string {
	intPart := int64(f)
	frac := int64((f - float64(intPart)) * 100)
	s := strconv.FormatInt(intPart, 10)
	var out []byte
	for i, c := range reverse(s) {
		if i > 0 && i%3 == 0 {
			out = append(out, '.')
		}
		out = append(out, byte(c))
	}
	return string(reverse(string(out))) + fmt.Sprintf(",%02d", frac)
}

func reverse(s string) string {
	r := []byte(s)
	for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

func SocioNome(s api.SocioOut) string {
	if n := strings.TrimSpace(s.NomeSocioRazaoSocial); n != "" {
		return n
	}
	return strings.TrimSpace(s.NomeSocio)
}

func SociosNomes(socios []api.SocioOut) string {
	if len(socios) == 0 {
		return ""
	}
	names := make([]string, 0, len(socios))
	for _, s := range socios {
		if n := SocioNome(s); n != "" {
			names = append(names, n)
		}
	}
	if len(names) == 0 {
		return ""
	}
	return strings.Join(names, ", ")
}

func HasSocios(socios []api.SocioOut) bool {
	for _, s := range socios {
		if SocioNome(s) != "" {
			return true
		}
	}
	return false
}

// BuscaSocioURL monta link para listar empresas ligadas ao sócio (busca textual q=).
func BuscaSocioURL(nome, uf string) string {
	nome = strings.TrimSpace(nome)
	if nome == "" {
		return "/busca"
	}
	q := url.Values{"q": {nome}}
	if uf = strings.ToUpper(strings.TrimSpace(uf)); len(uf) == 2 {
		q.Set("uf", uf)
	}
	return "/busca?" + q.Encode()
}

func HasTelefone(item api.EstabelecimentoOut) bool {
	return item.DDD1 != nil && item.Telefone1 != nil && *item.DDD1 != "" && *item.Telefone1 != ""
}

func HasEmail(item api.EstabelecimentoOut) bool {
	return item.CorreioEletronico != nil && *item.CorreioEletronico != ""
}

func HasTelefoneDetail(d *api.EstabelecimentoDetailOut) bool {
	if d == nil {
		return false
	}
	return (d.Contatos.Telefone1 != nil && d.Contatos.Telefone1.DDD != "") ||
		(d.Contatos.Telefone2 != nil && d.Contatos.Telefone2.DDD != "")
}

func HasEmailDetail(d *api.EstabelecimentoDetailOut) bool {
	if d == nil {
		return false
	}
	return d.Contatos.Email != nil && strings.TrimSpace(*d.Contatos.Email) != ""
}

func ExtractCNPJDigitsOnly(raw string) string {
	digits, _ := ExtractCNPJDigits(raw)
	return digits
}

func NormalizeCNPJ(raw string) (string, bool) {
	digits := strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, raw)
	if len(digits) != 14 {
		return "", false
	}
	return digits, true
}

func SanitizeUF(uf string) string {
	uf = strings.ToUpper(strings.TrimSpace(uf))
	if len(uf) == 2 {
		return uf
	}
	return ""
}

func ClampPageSize(n int) int {
	switch {
	case n <= 0:
		return 10
	case n > 50:
		return 50
	default:
		return n
	}
}

func StageLabel(stage string) string {
	labels := map[string]string{
		"novos":       "Novos",
		"contatando":  "Contatando",
		"respondeu":   "Respondeu",
		"negociacao":  "Negociação",
		"fechado":     "Fechado",
		"perdido":     "Perdido",
		"arquivado":   "Arquivado",
	}
	if label, ok := labels[stage]; ok {
		return label
	}
	return stage
}

func TemperatureLabel(t *int) string {
	if t == nil {
		return ""
	}
	switch {
	case *t <= 33:
		return "Frio"
	case *t <= 66:
		return "Morno"
	default:
		return "Quente"
	}
}

func TemperatureClass(t *int) string {
	if t == nil {
		return "temp-none"
	}
	switch {
	case *t <= 33:
		return "temp-cold"
	case *t <= 66:
		return "temp-warm"
	default:
		return "temp-hot"
	}
}

func LeadTagSelected(tags []api.LeadTagOut, id int64) bool {
	for _, tag := range tags {
		if tag.ID == id {
			return true
		}
	}
	return false
}

func DerefInt(p *int, fallback int) int {
	if p == nil {
		return fallback
	}
	return *p
}
