import math

from django.conf import settings
from django.db import models
from markdownx.models import MarkdownxField

from shared.models import NameStrMixin
from users.models import User


class RepresentativeMixin:
    @property
    def name(self):
        raise NotImplementedError

    @property
    def function(self):
        raise NotImplementedError

    def __str__(self) -> str:
        result = self.name

        if self.function is not None:
            result += f", {self.function}"

        return result


class Signee(models.Model):
    name = models.CharField(
        max_length=256,
        verbose_name="Jméno",
    )

    class EntityTypes(models.TextChoices):
        NATURAL_PERSON = "natural_person", "Fyzická osoba"
        LEGAL_ENTITY = "legal_entity", "Právnická osoba"
        BUSINESS_NATURAL_PERSON = "business_natural_person", "Podnikající fyzická osoba"
        OTHER = "other", "Jiné"

    entity_type = models.CharField(
        max_length=23,
        choices=EntityTypes.choices,
        verbose_name="Typ",
        help_text="Důležité označit správně! Fyzickým osobám nepublikujeme adresu.",
    )

    address_street_with_number = models.CharField(
        max_length=256,
        verbose_name="Ulice, č.p.",
        help_text="Veřejné pouze, když typ není nastaven na fyzickou osobu.",
    )  # WARNING: Legal entity status dependent!

    address_district = models.CharField(
        max_length=256,
        verbose_name="Obec",
    )

    address_zip = models.CharField(
        max_length=16,
        verbose_name="PSČ",
        help_text="Veřejné pouze, když typ není nastaven na fyzickou osobu.",
    )  # WARNING: Legal entity status dependent!

    address_country = models.CharField(
        max_length=256,
        verbose_name="Země",
        default=settings.DEFAULT_COUNTRY,
    )

    ico_number = models.CharField(
        max_length=16,
        blank=True,
        null=True,
        verbose_name="IČO",
    )  # WARNING: Legal entity status dependent!

    date_of_birth = models.DateField(
        blank=True,
        null=True,
        verbose_name="Datum narození",
    )  # WARNING: Legal entity status dependent!

    department = models.CharField(
        max_length=128,
        blank=True,
        null=True,
        verbose_name="Organizační složka",
    )

    role = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Role",
    )

    class Meta:
        verbose_name = "Jiná smluvní strana"
        verbose_name_plural = "Ostatní smluvní strany"

    def __str__(self) -> str:
        result = self.name

        if self.ico_number is not None:
            result += f" ({self.ico_number})"

        if self.date_of_birth is not None:
            result += f" ({self.date_of_birth})"

        if self.department is not None:
            result += f", {self.department}"

        representative_names = []

        for representative in self.representatives.all():
            representative_names.append(representative.name)

        if len(representative_names) != 0:
            result += " - zástupci " + ", ".join(representative_names)

        return result


class SigneeRepresentative(RepresentativeMixin, models.Model):
    signee = models.ForeignKey(
        Signee,
        on_delete=models.CASCADE,
        related_name="representatives",
        verbose_name="Smluvní strana",
    )

    name = models.CharField(
        max_length=256,
        verbose_name="Jméno",
    )

    function = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Funkce",
    )

    class Meta:
        verbose_name = "Zástupce"
        verbose_name_plural = "Zástupci"


class Contractee(models.Model):
    name = models.CharField(
        max_length=256,
        default=settings.DEFAULT_CONTRACTEE_NAME,
        verbose_name="Jméno",
    )

    address_street_with_number = models.CharField(
        max_length=256,
        default=settings.DEFAULT_CONTRACTEE_STREET,
        verbose_name="Ulice, č.p.",
    )

    address_district = models.CharField(
        max_length=256,
        default=settings.DEFAULT_CONTRACTEE_DISTRICT,
        verbose_name="Obec",
    )

    address_zip = models.CharField(
        max_length=16,
        default=settings.DEFAULT_CONTRACTEE_ZIP,
        verbose_name="PSČ",
    )

    address_country = models.CharField(
        max_length=256,
        verbose_name="Země",
        default=settings.DEFAULT_COUNTRY,
    )

    ico_number = models.CharField(
        max_length=16,
        blank=True,
        null=True,
        default=settings.DEFAULT_CONTRACTEE_ICO_NUMBER,
        verbose_name="IČO",
    )

    department = models.CharField(
        max_length=128,
        blank=True,
        null=True,
        verbose_name="Organizační složka",
    )

    role = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Role",
    )

    class Meta:
        verbose_name = "Naše smluvní strana"
        verbose_name_plural = "Naše smluvní strany"

    def __str__(self) -> str:
        result = self.name

        if self.department is not None:
            result += f", {self.department}"

        representative_names = []

        for representative in self.representatives.all():
            representative_names.append(representative.name)

        if len(representative_names) != 0:
            result += " - zástupci " + ", ".join(representative_names)

        return result


class ContracteeRepresentative(RepresentativeMixin, models.Model):
    contractee = models.ForeignKey(
        Contractee,
        on_delete=models.CASCADE,
        related_name="representatives",
        verbose_name="Smluvní strana",
    )

    name = models.CharField(
        max_length=256,
        verbose_name="Jméno",
    )

    function = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Funkce",
    )

    class Meta:
        verbose_name = "Zástupce"
        verbose_name_plural = "Zástupci"


class ContractType(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=32,
        verbose_name="Jméno",
    )

    class Meta:
        verbose_name = "Typ smlouvy"
        verbose_name_plural = "Typy smlouvy"


class ContractIssue(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=32,
        verbose_name="Jméno",
    )

    class Meta:
        verbose_name = "Problém se smlouvou"
        verbose_name_plural = "Problémy se smlouvami"


class ContractFilingArea(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=32,
        verbose_name="Jméno",
    )

    person_responsible = models.CharField(
        max_length=256,
        verbose_name="Odpovědná osoba",
    )

    class Meta:
        verbose_name = "Spisovna"
        verbose_name_plural = "Spisovny"


class Contract(models.Model):
    # BEGIN Automatically set fields

    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name="uploaded_contracts",
        verbose_name="Vytvořena uživatelem",
        help_text="Informace není veřejně přístupná. Pokud vytváříš novou smlouvu, budeš to ty.",
    )  # WARNING: exclude in admin

    public_status_set_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name="public_status_altered_contracts",
        verbose_name="Zveřejněno / nezveřejněno uživatelem",
        help_text="Obsah není veřejně přístupný.",
    )  # WARNING: exclude in admin

    all_parties_sign_date = models.DateField(
        verbose_name="Datum podpisu všech stran",
        blank=True,
        null=True,
    )  # WARNING: Exclude in admin, autofill

    # END Automatically set fields

    # BEGIN Approval fields

    class ApprovalStates(models.TextChoices):
        NO = "no", "Neschválená"
        YES = "yes", "Schválená"

    approval_state = models.CharField(
        max_length=7,
        choices=ApprovalStates.choices,
        blank=True,
        null=True,
        verbose_name="Stav schválení",
        help_text=(
            "Může měnit jen schvalovatel. Pokud je smlouva "
            "veřejná, se stavem \"Přijatá\" se vypustí ven."
        ),
    )

    # END Approval fields

    name = models.CharField(
        max_length=128,
        verbose_name="Název",
    )

    id_number = models.CharField(
        max_length=128,
        blank=True,
        null=True,
        verbose_name="Identifikační číslo",
    )

    types = models.ManyToManyField(
        ContractType,
        verbose_name="Typ",
    )

    summary = models.TextField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Sumarizace obsahu smlouvy",
    )

    valid_start_date = models.DateField(
        blank=True,
        null=True,
        verbose_name="Začátek účinnosti",
    )
    valid_end_date = models.DateField(
        blank=True,
        null=True,
        verbose_name="Konec účinnosti",
    )

    class LegalStates(models.TextChoices):
        VALID = "valid", "Platná"
        INVALID = "invalid", "Neplatná"

    class PublicStates(models.TextChoices):
        YES = "yes", "Veřejná"
        NO = "no", "Neveřejná"

    class PaperFormStates(models.TextChoices):
        SENT = "sent", "Odeslaný"
        STORED = "stored", "Uložený"
        TO_SHRED = "to_shred", "Ke skartaci"
        SHREDDED = "shredded", "Skartovaný"
        LOST = "lost", "Ztracený"

    legal_state = models.CharField(
        max_length=13,
        choices=LegalStates.choices,
        verbose_name="Stav právního ujednání",
    )

    public_state = models.CharField(
        max_length=7,
        choices=PublicStates.choices,
        verbose_name="Veřejnost smlouvy",
    )

    paper_form_state = models.CharField(
        max_length=8,
        choices=PaperFormStates.choices,
        verbose_name="Stav fyzického dokumentu",
    )

    publishing_rejection_comment = models.CharField(
        max_length=65536,
        blank=True,
        null=True,
        verbose_name="Důvod nezveřejnění",
        help_text="Obsah není veřejně přístupný.",
    )  # WARNING: public status dependent

    tender_url = models.URLField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Odkaz na výběrové řízení",
    )

    agreement_url = models.URLField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Odkaz na schválení",
    )  # WARNING: Dependent on the type!

    issues = models.ManyToManyField(
        ContractIssue,
        blank=True,
        verbose_name="Problémy",
        help_text='Veřejně nazváno "Poznámky".',
    )

    class CostUnits(models.TextChoices):
        HOUR = "hour", "Hodina"
        MONTH = "month", "Měsíc"
        YEAR = "year", "Rok"
        TOTAL = "total", "Celkem"

    cost_amount = models.PositiveIntegerField(
        blank=True,
        null=True,
        verbose_name="Náklady (Kč)"
    )

    cost_unit = models.CharField(
        max_length=5,
        choices=CostUnits.choices,
        blank=True,
        null=True,
        verbose_name="Jednotka nákladů"
    )

    filing_area = models.ForeignKey(
        ContractFilingArea,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name="filed_contracts",
        verbose_name="Spisovna",
        help_text="Obsah není veřejně přístupný.",
    )

    primary_contract = models.ForeignKey(
        "Contract",
        on_delete=models.SET_NULL,  # TODO: Figure out if we want this behavior
        blank=True,
        null=True,
        related_name="subcontracts",
        verbose_name="Primární smlouva",
        help_text="Např. pro dodatky nebo objednávky u rámcových smluv.",
    )  # WARNING: Dependent on the type!

    notes = MarkdownxField(
        blank=True,
        null=True,
        verbose_name="Poznámky",
        help_text="Poznámky jsou viditelné pro všechny, kteří mohou smlouvu spravovat.",
    )

    class Meta:
        verbose_name = "Smlouva"
        verbose_name_plural = "Smlouvy"

        permissions = (("approve", "Schválit / zrušit schválení"),)

    def get_public_files(self):
        return ContractFile.objects.filter(
            contract=self,
            is_public=True,
        ).all()

    def __str__(self) -> str:
        return self.name


class ContractFile(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=128,
        blank=True,
        null=True,
        verbose_name="Jméno",
    )

    is_public = models.BooleanField(
        verbose_name="Veřejně dostupný",
        default=False,
    )

    file = models.FileField(
        verbose_name="Soubor",
    )

    contract = models.ForeignKey(
        Contract,
        on_delete=models.CASCADE,
        related_name="files",
        verbose_name="Soubory",
    )

    class Meta:
        verbose_name = "Soubor"
        verbose_name_plural = "Soubory"


class ContracteeSignature(models.Model):
    contractee = models.ForeignKey(
        Contractee,
        on_delete=models.CASCADE,
        related_name="signatures",
        verbose_name="Smluvní strana",
    )

    contract = models.ForeignKey(
        Contract,
        on_delete=models.CASCADE,
        related_name="contractee_signatures",
        verbose_name="Podpisy našich smluvních stran",
    )

    date = models.DateField(
        verbose_name="Datum podpisu",
    )

    class Meta:
        verbose_name = "Podpis naší smluvní strany"
        verbose_name_plural = "Podpisy našich smluvních stran"

    def __str__(self) -> str:
        representative_names = []

        for representative in self.contractee.representatives.all():
            representative_names.append(representative.name)

        representatives = ", ".join(representative_names)
        result = self.contractee.name

        if len(representatives) != 0:
            result += f" - zastoupena {representatives}"

        result += f", {self.date}"

        return result


class SigneeSignature(models.Model):
    signee = models.ForeignKey(
        Signee,
        on_delete=models.CASCADE,
        related_name="signatures",
        verbose_name="Smluvní strana",
    )

    contract = models.ForeignKey(
        Contract,
        on_delete=models.CASCADE,
        related_name="signee_signatures",
        verbose_name="Podpisy jiných smluvních stran",
    )

    date = models.DateField(
        verbose_name="Datum podpisu",
    )

    class Meta:
        verbose_name = "Podpis jiné smluvní strany"
        verbose_name_plural = "Podpisy ostatních smluvních stran"

    def __str__(self) -> str:
        representative_names = []

        for representative in self.signee.representatives.all():
            representative_names.append(representative.name)

        representatives = ", ".join(representative_names)
        result = self.signee.name

        if len(representatives) != 0:
            result += f" - zastoupena {representatives}"

        result += f", {self.date}"

        return result


class ContractIntent(NameStrMixin, models.Model):
    name = models.CharField(
        max_length=128,
        verbose_name="Jméno",
    )

    url = models.URLField(
        max_length=256,
        verbose_name="Odkaz",
    )

    contract = models.ForeignKey(
        Contract,
        on_delete=models.CASCADE,
        related_name="intents",
        verbose_name="Smlouva",
    )

    class Meta:
        verbose_name = "Záměr"
        verbose_name_plural = "Záměry"