package render

import (
	"encoding/json"
	"html/template"
	"strings"
	"time"

	"leadb2b.org/internal/api"
)

const (
	brandName        = "LeadB2B"
	brandDescription = "Plataforma brasileira de prospecção ativa com Kanban e funil de vendas. Organiza o fluxo diário de vendas com dados oficiais da Receita Federal."
	canonicalDesc    = "LeadB2B (leadb2b.org) é uma plataforma de prospecção ativa que transforma empresas em clientes através de um sistema diário de vendas guiado, com Kanban, funil ativo e dados da Receita Federal."
)

func OGImageURL(siteURL string) string {
	return strings.TrimRight(siteURL, "/") + "/static/images/logo-dark.png"
}

func marshalJSONLD(v any) template.HTML {
	b, err := json.Marshal(v)
	if err != nil {
		return ""
	}
	return template.HTML(b)
}

func leadB2BOrganization(siteURL string) map[string]any {
	base := strings.TrimRight(siteURL, "/")
	return map[string]any{
		"@type":       "Organization",
		"@id":         base + "/#organization",
		"name":        brandName,
		"url":         base + "/",
		"logo":        base + "/static/images/logo-dark.png",
		"description": brandDescription,
		"sameAs":      []string{base + "/"},
	}
}

func HomeStructuredData(siteURL string) template.HTML {
	base := strings.TrimRight(siteURL, "/")
	graph := []any{
		leadB2BOrganization(siteURL),
		map[string]any{
			"@type":           "WebSite",
			"@id":             base + "/#website",
			"name":            brandName,
			"url":             base + "/",
			"description":     brandDescription,
			"publisher":       map[string]any{"@id": base + "/#organization"},
			"inLanguage":      "pt-BR",
			"potentialAction": map[string]any{
				"@type":       "SearchAction",
				"target":      base + "/busca?q={search_term_string}",
				"query-input": "required name=search_term_string",
			},
		},
		map[string]any{
			"@type": "SoftwareApplication",
			"name":  brandName,
			"url":   base + "/",
			"applicationCategory": "BusinessApplication",
			"operatingSystem":     "Web",
			"description":         brandDescription,
			"offers": map[string]any{
				"@type":         "Offer",
				"price":           "0",
				"priceCurrency":   "BRL",
				"description":     "Teste grátis disponível",
				"url":             base + "/cadastro",
			},
		},
		FAQPageFromItems(HomeFAQItems()),
		map[string]any{
			"@type": "SpeakableSpecification",
			"cssSelector": []string{
				".geo-summary",
				".twm-bnr-discription",
			},
		},
	}
	return graphJSONLD(graph)
}

func HomeFAQItems() []FAQItem {
	return []FAQItem{
		{
			Question: "O que é o LeadB2B?",
			Answer:   canonicalDesc,
		},
		{
			Question: "Como funciona a prospecção ativa no LeadB2B?",
			Answer:   "Você busca empresas por CNAE, UF e município, adiciona ao funil ativo, trabalha diariamente no Kanban e acompanha cada negociação até o fechamento. O produto é um sistema de vendas guiado, não uma lista para download.",
		},
		{
			Question: "De onde vêm os dados das empresas?",
			Answer:   "Os dados provêm de registros públicos da Receita Federal do Brasil (CNPJ), atualizados periodicamente via base oficial de estabelecimentos.",
		},
	}
}

func SiteFAQStructuredData(siteURL string) template.HTML {
	return FAQPageJSONLD(SiteFAQItems(), strings.TrimRight(siteURL, "/")+"/faq")
}

func SiteFAQItems() []FAQItem {
	return []FAQItem{
		{Question: "Como funciona a plataforma LeadB2B?", Answer: "Você busca empresas por CNAE, UF e município, adiciona ao funil ativo, trabalha diariamente no Kanban e acompanha cada negociação. É um sistema de prospecção ativa, não uma lista para baixar."},
		{Question: "O que são leads ativos?", Answer: "Leads ativos são empresas em andamento no seu Kanban. Quando você arquiva, conclui ou marca como perdido, libera espaço para novos leads. Esse é o principal mecanismo dos planos Free (20 ativos) e Pro (100 ativos)."},
		{Question: "De onde vêm os dados?", Answer: "De registros públicos da Receita Federal do Brasil (CNPJ), tratados e disponibilizados para prospecção B2B conforme a legislação vigente."},
		{Question: "O LeadB2B está em conformidade com a LGPD?", Answer: "Sim. Tratamos apenas dados de pessoa jurídica disponibilizados publicamente pela Receita Federal, com finalidade de prospecção B2B e política de privacidade publicada no site."},
		{Question: "Existe plano gratuito?", Answer: "Sim. O plano Free inclui Kanban básico e até 20 leads ativos simultâneos para você testar o fluxo de vendas."},
	}
}

func FAQPageFromItems(items []FAQItem) map[string]any {
	entities := make([]map[string]any, 0, len(items))
	for _, item := range items {
		entities = append(entities, map[string]any{
			"@type": "Question",
			"name":  item.Question,
			"acceptedAnswer": map[string]any{
				"@type": "Answer",
				"text":  item.Answer,
			},
		})
	}
	return map[string]any{
		"@type":      "FAQPage",
		"mainEntity": entities,
	}
}

func FAQPageJSONLD(items []FAQItem, pageURL string) template.HTML {
	payload := FAQPageFromItems(items)
	payload["@context"] = "https://schema.org"
	payload["url"] = pageURL
	return marshalJSONLD(payload)
}

func BlogPostingStructuredData(siteURL, path, headline, description, datePublished string) template.HTML {
	base := strings.TrimRight(siteURL, "/")
	payload := map[string]any{
		"@context":      "https://schema.org",
		"@type":         "BlogPosting",
		"headline":      headline,
		"description":   description,
		"url":           base + path,
		"datePublished": datePublished,
		"dateModified":  datePublished,
		"inLanguage":    "pt-BR",
		"author": map[string]any{
			"@type": "Organization",
			"name":  "Equipe LeadB2B",
		},
		"publisher": leadB2BOrganization(siteURL),
		"image":     OGImageURL(siteURL),
		"mainEntityOfPage": map[string]any{
			"@type": "WebPage",
			"@id":   base + path,
		},
	}
	return marshalJSONLD(payload)
}

func BreadcrumbStructuredData(siteURL string, items []BreadcrumbItem) template.HTML {
	if len(items) == 0 {
		return ""
	}
	base := strings.TrimRight(siteURL, "/")
	list := make([]map[string]any, 0, len(items))
	for i, item := range items {
		entry := map[string]any{
			"@type":    "ListItem",
			"position": i + 1,
			"name":     item.Name,
		}
		if item.Path != "" {
			entry["item"] = base + item.Path
		}
		list = append(list, entry)
	}
	return marshalJSONLD(map[string]any{
		"@context":        "https://schema.org",
		"@type":           "BreadcrumbList",
		"itemListElement": list,
	})
}

type BreadcrumbItem struct {
	Name string
	Path string
}

func DatasetStructuredData(siteURL string) template.HTML {
	base := strings.TrimRight(siteURL, "/")
	return marshalJSONLD(map[string]any{
		"@context":    "https://schema.org",
		"@type":       "Dataset",
		"name":        "Base de empresas LeadB2B — dados Receita Federal",
		"description": "Estabelecimentos comerciais brasileiros com CNPJ, CNAE, endereço e situação cadastral derivados de registros públicos da Receita Federal.",
		"url":         base + "/sobre-dados",
		"creator":     leadB2BOrganization(siteURL),
		"license":     "https://www.gov.br/receitafederal/pt-br",
		"inLanguage":  "pt-BR",
		"distribution": map[string]any{
			"@type": "DataDownload",
			"contentUrl": base + "/empresa/",
			"encodingFormat": "text/html",
		},
	})
}

func graphJSONLD(graph []any) template.HTML {
	return marshalJSONLD(map[string]any{
		"@context": "https://schema.org",
		"@graph":   graph,
	})
}

func MergeJSONLD(parts ...template.HTML) template.HTML {
	var graphs []any
	for _, part := range parts {
		if part == "" {
			continue
		}
		var m map[string]any
		if err := json.Unmarshal([]byte(part), &m); err != nil {
			continue
		}
		if g, ok := m["@graph"].([]any); ok {
			graphs = append(graphs, g...)
			continue
		}
		delete(m, "@context")
		graphs = append(graphs, m)
	}
	if len(graphs) == 0 {
		return ""
	}
	return graphJSONLD(graphs)
}

func EmpresaPageStructuredData(d *api.EstabelecimentoDetailOut, siteURL string, locked bool) template.HTML {
	if d == nil {
		return ""
	}
	name := d.Empresa.RazaoSocial
	addr := d.Endereco.EnderecoCompleto
	city := d.Endereco.Municipio.Descricao
	uf := d.Endereco.UF
	cep := d.Endereco.CEP
	cnpj := FmtCNPJ(d.CNPJ)
	url := strings.TrimRight(siteURL, "/") + EmpresaPathFromDetail(d, false)

	orgType := "Organization"
	if addr != "" && city != "" {
		orgType = "LocalBusiness"
	}

	org := map[string]any{
		"@type": orgType,
		"name":  name,
		"url":   url,
		"taxID": cnpj,
		"address": map[string]any{
			"@type":           "PostalAddress",
			"streetAddress":   addr,
			"addressLocality": city,
			"addressRegion":   uf,
			"postalCode":      cep,
			"addressCountry":  "BR",
		},
		"description": EmpresaGeoSummary(d),
		"dateModified":  time.Now().Format("2006-01-02"),
	}
	if d.CNAE.Principal.Descricao != "" {
		org["knowsAbout"] = d.CNAE.Principal.Descricao
	}

	graph := []any{org}
	faqItems := BuildEmpresaFAQ(d, locked)
	if len(faqItems) > 0 {
		graph = append(graph, FAQPageFromItems(faqItems))
	}
	graph = append(graph, map[string]any{
		"@type": "BreadcrumbList",
		"itemListElement": []map[string]any{
			{"@type": "ListItem", "position": 1, "name": "Início", "item": strings.TrimRight(siteURL, "/") + "/"},
			{"@type": "ListItem", "position": 2, "name": "Buscar", "item": strings.TrimRight(siteURL, "/") + "/busca"},
			{"@type": "ListItem", "position": 3, "name": EmpresaNomeExibicao(d), "item": url},
		},
	})

	return graphJSONLD(graph)
}
