from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone

from core.models import TimeStampedModel


class TemplateCategory(TimeStampedModel):
    class Tone(models.TextChoices):
        YELLOW = "yellow", "Amarelo Kreate4Web"
        BLUE = "blue", "Azul"
        GREEN = "green", "Verde"
        VIOLET = "violet", "Violeta"
        ORANGE = "orange", "Laranja"
        GREY = "grey", "Cinzento"

    name = models.CharField("Nome", max_length=120, unique=True)
    code = models.SlugField("Código", max_length=80, unique=True)
    description = models.TextField("Descrição", blank=True)
    icon = models.CharField("Ícone Material", max_length=80, default="folder_copy")
    tone = models.CharField("Cor", max_length=20, choices=Tone.choices, default=Tone.YELLOW)
    sort_order = models.PositiveSmallIntegerField("Ordem", default=100)
    is_active = models.BooleanField("Ativa", default=True, db_index=True)

    class Meta:
        verbose_name = "Categoria de templates"
        verbose_name_plural = "Categorias de templates"
        ordering = ["sort_order", "name"]

    def __str__(self):
        return self.name


class ReusableTemplate(TimeStampedModel):
    class Kind(models.TextChoices):
        MESSAGE = "message", "Mensagem geral"
        TICKET_REPLY = "ticket_reply", "Resposta de ticket"
        PORTAL_NOTICE = "portal_notice", "Aviso no portal"
        DOCUMENT = "document", "Documento"
        CONTRACT = "contract", "Contrato"
        REPORT = "report", "Relatório"
        CHECKLIST = "checklist", "Checklist"
        QUOTE = "quote", "Orçamento"
        INVOICE_NOTE = "invoice_note", "Nota de faturação"
        TECHNICAL = "technical", "Conteúdo técnico"

    category = models.ForeignKey(
        TemplateCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="content_templates",
        verbose_name="Categoria",
    )
    name = models.CharField("Nome", max_length=180)
    code = models.SlugField("Código", max_length=100, unique=True)
    kind = models.CharField("Tipo", max_length=30, choices=Kind.choices, default=Kind.MESSAGE, db_index=True)
    description = models.TextField("Descrição", blank=True)
    subject_template = models.CharField("Título / assunto", max_length=250, blank=True)
    body_template = models.TextField("Conteúdo em texto")
    html_template = models.TextField("Conteúdo HTML", blank=True)
    variables = models.TextField(
        "Variáveis disponíveis",
        blank=True,
        help_text="Uma variável por linha, por exemplo: {cliente} — nome do cliente.",
    )
    example_context = models.JSONField(
        "Dados de exemplo",
        default=dict,
        blank=True,
        help_text="Valores usados na pré-visualização. Ex.: {\"cliente\": \"Acácia Studio\"}.",
    )
    is_active = models.BooleanField("Ativo", default=True, db_index=True)
    is_default = models.BooleanField("Modelo predefinido", default=False)
    visible_to_clients = models.BooleanField("Pode ser usado no portal do cliente", default=False)
    usage_count = models.PositiveIntegerField("Utilizações", default=0, editable=False)
    last_used_at = models.DateTimeField("Última utilização", null=True, blank=True, editable=False)

    class Meta:
        verbose_name = "Template reutilizável"
        verbose_name_plural = "Templates reutilizáveis"
        ordering = ["kind", "name"]
        constraints = [
            models.UniqueConstraint(
                fields=["kind"],
                condition=models.Q(is_default=True),
                name="one_default_reusable_template_per_kind",
            )
        ]
        indexes = [models.Index(fields=["kind", "is_active"])]

    def clean(self):
        if self.is_default and not self.is_active:
            raise ValidationError({"is_active": "Um modelo predefinido tem de estar ativo."})

    def render_subject(self, context=None):
        return self._safe_render(self.subject_template, context)

    def render_body(self, context=None):
        return self._safe_render(self.body_template, context)

    def render_html(self, context=None):
        return self._safe_render(self.html_template, context)

    def mark_used(self):
        type(self).objects.filter(pk=self.pk).update(
            usage_count=models.F("usage_count") + 1,
            last_used_at=timezone.now(),
        )

    def _safe_render(self, value, context=None):
        if not value:
            return ""
        data = dict(self.example_context or {})
        data.update(context or {})
        try:
            return value.format_map(_SafeFormatDict(data))
        except (ValueError, KeyError):
            return value

    def __str__(self):
        return self.name


class _SafeFormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"


class FormTemplate(TimeStampedModel):
    class Purpose(models.TextChoices):
        CLIENT_ONBOARDING = "client_onboarding", "Adesão / dados do cliente"
        SUPPORT = "support", "Pedido de suporte"
        DOMAIN = "domain", "Pedido de domínio"
        HOSTING = "hosting", "Pedido de alojamento"
        WEBSITE = "website", "Briefing de website"
        PAYMENT = "payment", "Informação de pagamento"
        MAINTENANCE = "maintenance", "Pedido de manutenção"
        SATISFACTION = "satisfaction", "Satisfação / feedback"
        GENERAL = "general", "Formulário geral"

    name = models.CharField("Nome", max_length=180)
    code = models.SlugField("Código", max_length=100, unique=True)
    category = models.ForeignKey(
        TemplateCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="form_templates",
        verbose_name="Categoria",
    )
    purpose = models.CharField("Finalidade", max_length=30, choices=Purpose.choices, default=Purpose.GENERAL, db_index=True)
    description = models.TextField("Descrição", blank=True)
    intro_text = models.TextField("Texto introdutório", blank=True)
    success_message = models.TextField("Mensagem de sucesso", default="Formulário recebido com sucesso.")
    version = models.PositiveSmallIntegerField("Versão", default=1)
    is_active = models.BooleanField("Ativo", default=True, db_index=True)
    visible_to_clients = models.BooleanField("Disponível no portal do cliente", default=False)
    requires_authentication = models.BooleanField("Exige autenticação", default=True)
    sort_order = models.PositiveSmallIntegerField("Ordem", default=100)

    class Meta:
        verbose_name = "Template de formulário"
        verbose_name_plural = "Templates de formulários"
        ordering = ["sort_order", "name"]
        indexes = [models.Index(fields=["purpose", "is_active"])]

    @property
    def active_fields_count(self):
        return self.fields.filter(is_active=True).count()

    def __str__(self):
        return self.name


class FormTemplateField(TimeStampedModel):
    class FieldType(models.TextChoices):
        TEXT = "text", "Texto curto"
        TEXTAREA = "textarea", "Texto longo"
        EMAIL = "email", "Email"
        PHONE = "phone", "Telefone"
        NUMBER = "number", "Número"
        DATE = "date", "Data"
        URL = "url", "URL"
        SELECT = "select", "Lista de opções"
        RADIO = "radio", "Escolha única"
        CHECKBOX = "checkbox", "Caixa de seleção"
        FILE = "file", "Ficheiro"

    form_template = models.ForeignKey(
        FormTemplate,
        on_delete=models.CASCADE,
        related_name="fields",
        verbose_name="Formulário",
    )
    label = models.CharField("Etiqueta", max_length=180)
    key = models.SlugField("Chave", max_length=100)
    field_type = models.CharField("Tipo de campo", max_length=20, choices=FieldType.choices, default=FieldType.TEXT)
    help_text = models.CharField("Ajuda", max_length=300, blank=True)
    placeholder = models.CharField("Placeholder", max_length=250, blank=True)
    required = models.BooleanField("Obrigatório", default=False)
    options = models.TextField("Opções", blank=True, help_text="Uma opção por linha para listas e escolhas.")
    default_value = models.CharField("Valor predefinido", max_length=250, blank=True)
    sort_order = models.PositiveSmallIntegerField("Ordem", default=100)
    is_active = models.BooleanField("Ativo", default=True)

    class Meta:
        verbose_name = "Campo de formulário"
        verbose_name_plural = "Campos de formulário"
        ordering = ["sort_order", "id"]
        constraints = [
            models.UniqueConstraint(fields=["form_template", "key"], name="unique_form_template_field_key")
        ]

    def clean(self):
        if self.field_type in {self.FieldType.SELECT, self.FieldType.RADIO} and not self.options.strip():
            raise ValidationError({"options": "Indique pelo menos uma opção para este tipo de campo."})

    @property
    def option_list(self):
        return [item.strip() for item in self.options.splitlines() if item.strip()]

    def __str__(self):
        return f"{self.form_template} — {self.label}"


class TemplateUsage(models.Model):
    class Action(models.TextChoices):
        PREVIEW = "preview", "Pré-visualização"
        COPY = "copy", "Copiado"
        GENERATED = "generated", "Documento gerado"
        SENT = "sent", "Usado em comunicação"

    reusable_template = models.ForeignKey(
        ReusableTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="usage_logs",
        verbose_name="Template reutilizável",
    )
    form_template = models.ForeignKey(
        FormTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="usage_logs",
        verbose_name="Formulário",
    )
    action = models.CharField("Ação", max_length=20, choices=Action.choices, default=Action.PREVIEW)
    used_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="template_usages",
        verbose_name="Utilizador",
    )
    context = models.JSONField("Contexto", default=dict, blank=True)
    used_at = models.DateTimeField("Utilizado em", auto_now_add=True, db_index=True)

    class Meta:
        verbose_name = "Utilização de template"
        verbose_name_plural = "Utilizações de templates"
        ordering = ["-used_at"]

    def clean(self):
        if bool(self.reusable_template_id) == bool(self.form_template_id):
            raise ValidationError("Associe exatamente um template reutilizável ou um formulário.")

    def __str__(self):
        target = self.reusable_template or self.form_template
        return f"{target} — {self.get_action_display()}"
