import datetime
import mimetypes
import os
import typing
import uuid

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator, URLValidator
from django.db import models
from django.db.models.fields.files import FieldFile
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils.safestring import mark_safe
from markdownx.models import MarkdownxField

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

from . import settings as app_settings


class SubdomainValidatedURLField(models.URLField):
    validators = [
        URLValidator(schemes=("https",)),
        RegexValidator(regex=r"https:\/\/(.*\.|)pirati.cz(\/|$).*"),
    ]


class OwnPermissionsMixin(models.Model):
    class Meta:
        abstract = True

        permissions = [
            ("edit_others", "Upravit cizí"),
            ("delete_others", "Odstranit cizí"),
        ]


class ContractCountMixin(models.Model):
    def get_contract_count(self, user) -> None:
        filter = {"is_approved": True}

        if not user.has_perm("contract.view_confidential"):
            filter["is_public"] = True

        return self.contracts.filter(**filter).count()

    class Meta:
        abstract = True


class SignatureCountMixin(models.Model):
    def get_signature_count(self, user) -> None:
        filter = {"contract__is_approved": True}

        if not user.has_perm("contract.view_confidential"):
            filter["contract__is_approved"] = True

        return self.signatures.filter(**filter).count()

    class Meta:
        abstract = True


class InlineNameMixin(models.Model):
    @property
    def inline_name(self) -> str:
        result = self.name

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

        return result

    class Meta:
        abstract = True


class CreatedByMixin(models.Model):
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name="+",
        verbose_name="Vytvořeno uživatelem",
    )  # WARNING: exclude in admin

    class Meta:
        abstract = True


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

    @property
    def function(self):
        raise NotImplementedError

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

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

        return result


def get_default_country():
    return settings.DEFAULT_COUNTRY


class Signee(
    CreatedByMixin,
    OwnPermissionsMixin,
    SignatureCountMixin,
    InlineNameMixin,
    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,
        default=EntityTypes.LEGAL_ENTITY,
        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,
        blank=True,
        null=True,
        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,
        blank=True,
        null=True,
        verbose_name="Obec",
    )

    address_zip = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        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,
        blank=True,
        null=True,
        verbose_name="Země",
        default=get_default_country,
    )

    ico_number = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="IČO",
        help_text=(
            "U právnických a podnikajících fyzických osob musí být vyplněno. "
            "Vyplněním můžeš automaticky načíst data z ARES."
        ),
    )  # WARNING: Legal entity status dependent!

    date_of_birth = models.DateField(
        blank=True,
        null=True,
        verbose_name="Datum narození",
        help_text="U fyzických osob musí být vyplněno.",
    )  # WARNING: Legal entity status dependent!

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

    @property
    def url(self) -> str:
        return reverse("contracts:view_signee", args=(self.id,))

    @property
    def entity_has_public_address(self) -> bool:
        return self.entity_type in (
            self.EntityTypes.LEGAL_ENTITY,
            self.EntityTypes.OTHER,
        )

    @property
    def has_any_address_information(self) -> bool:
        for field in (
            "address_street_with_number",
            "address_district",
            "address_zip",
            "address_country",
        ):
            if getattr(self, field):
                return True

        return False

    def clean(self):
        if (
            self.entity_type == self.EntityTypes.NATURAL_PERSON
            and not self.date_of_birth
        ):
            raise ValidationError(
                {"date_of_birth": "U fyzických osob musí být definováno."}
            )

        if (
            self.entity_type
            in (self.EntityTypes.LEGAL_ENTITY, self.EntityTypes.BUSINESS_NATURAL_PERSON)
            and not self.ico_number
        ):
            raise ValidationError(
                {
                    "ico_number": "U právnických a podnikajících fyzických osob musí být definováno."
                }
            )

        if self.entity_type != self.EntityTypes.OTHER:
            for field in (
                "address_street_with_number",
                "address_district",
                "address_zip",
                "address_country",
            ):
                if not getattr(self, field):
                    raise ValidationError(
                        {field: 'Pokud není vybrán typ "Jiné", musí být definováno.'}
                    )

        return super().clean()

    def __str__(self) -> str:
        result = str(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}"

        return result

    class Meta:
        app_label = "contracts"

        verbose_name = "Jiná smluvní strana"
        verbose_name_plural = "Ostatní smluvní strany"

        ordering = ["name", "department"]

        permissions = OwnPermissionsMixin.Meta.permissions


def get_default_contractee_name() -> str:
    return settings.DEFAULT_CONTRACTEE_NAME


def get_default_contractee_street() -> str:
    return settings.DEFAULT_CONTRACTEE_STREET


def get_default_contractee_district() -> str:
    return settings.DEFAULT_CONTRACTEE_DISTRICT


def get_default_contractee_zip() -> str:
    return settings.DEFAULT_CONTRACTEE_ZIP


def get_default_contractee_ico_number() -> str:
    return settings.DEFAULT_CONTRACTEE_ICO_NUMBER


class Contractee(
    CreatedByMixin,
    OwnPermissionsMixin,
    SignatureCountMixin,
    InlineNameMixin,
    models.Model,
):
    name = models.CharField(
        max_length=256,
        default=get_default_contractee_name,
        verbose_name="Jméno",
    )

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

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

    address_zip = models.CharField(
        max_length=256,
        default=get_default_contractee_zip,
        verbose_name="PSČ",
    )

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

    ico_number = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        default=get_default_contractee_ico_number,
        verbose_name="IČO",
    )

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

    @property
    def url(self) -> str:
        return reverse("contracts:view_contractee", args=(self.id,))

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

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

        return result

    class Meta:
        app_label = "contracts"

        verbose_name = "Naše smluvní strana"
        verbose_name_plural = "Naše smluvní strany"

        ordering = ["name", "department"]

        permissions = OwnPermissionsMixin.Meta.permissions


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

    @property
    def url(self) -> str:
        return reverse("contracts:view_contract_type", args=(self.id,))

    class Meta:
        app_label = "contracts"

        ordering = ["name"]

        verbose_name = "Typ smlouvy"
        verbose_name_plural = "Typy smluv"


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

    @property
    def url(self) -> str:
        return reverse("contracts:view_contract_issue", args=(self.id,))

    class Meta:
        app_label = "contracts"

        ordering = ["name"]

        verbose_name = "Problém se smlouvou"
        verbose_name_plural = "Problémy se smlouvami"


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

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

    @property
    def url(self) -> str:
        return reverse("contracts:view_contract_filing_area", args=(self.id,))

    class Meta:
        app_label = "contracts"

        ordering = ["name"]

        verbose_name = "Spisovna"
        verbose_name_plural = "Spisovny"


def get_current_utc_timestamp():
    return datetime.datetime.now(datetime.timezone.utc)


# Pre-squash migration compatibility
get_created_on_timestamp = get_current_utc_timestamp


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

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

    created_on = models.DateTimeField(
        blank=False,
        null=False,
        default=get_current_utc_timestamp,
        verbose_name="Čas vytvoření",
    )
    updated_on = models.DateTimeField(
        blank=False,
        null=False,
        default=get_current_utc_timestamp,
        verbose_name="Čas poslední aktualizace",
    )

    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

    is_approved = models.BooleanField(
        verbose_name="Je schválená",
        default=False,
        help_text=(
            "Mohou měnit jen schvalovatelé. Pokud je "
            "smlouva veřejná, schválením se vypustí ven."
        ),
    )

    # END Approval fields

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

    id_number = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Identifikační číslo",
        help_text=mark_safe(
            "<strong>Není IČO!</strong> Používá se pro identifikaci smluv "
            "s velkými organizacemi. Např. <code>MF-8687/2022/15-3</code>."
        ),
    )

    types = models.ManyToManyField(
        ContractType,
        related_name="contracts",
        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",
        help_text=mark_safe(
            "<strong>Pokud není začátek účinnosti zadán, vypočítá se automaticky "
            "podle posledního data podpisu.</strong>"
        ),
    )
    valid_end_date = models.DateField(
        blank=True,
        null=True,
        verbose_name="Konec účinnosti",
        help_text=mark_safe(
            "<strong>Pokud není konec účinnosti zadán, smlouva je evidována jako "
            "na dobu neurčitou.</strong>"
        ),
    )

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

    class PaperFormStates(models.TextChoices):
        ON_THE_WAY = "on_the_way", "Na cestě"
        SENT = "sent", "Odeslaný"
        MAILBOX = "mailbox", "Ve schránce"

        TO_SHRED = "to_shred", "Ke skartaci"
        SHREDDED = "shredded", "Skartovaný"

        STORED = "stored", "Uložený"

        LOST = "lost", "Ztracený"

    is_valid = models.BooleanField(
        default=False,
        verbose_name="Je právně platná",
        help_text="Právní vztah vyplývající ze smlouvy je aktuálně účinný a platný",
    )

    is_public = models.BooleanField(
        default=True,
        verbose_name="Je veřejná",
        help_text=(
            "Neveřejné smlouvy nejsou vidět bez přihlášení jako min. tajný čtenář."
        ),
    )
    publishing_rejection_comment = models.TextField(
        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

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

    paper_form_person_responsible = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Osoba zodpovědná za doručení",
    )

    tender_url = SubdomainValidatedURLField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Odkaz na výběrové řízení",
        help_text=mark_safe(
            'Běžně odkaz na <a href="https://forum.pirati.cz/viewforum.php?f=572">VŘ sekci</a> '
            "fóra. Musí začínat <i>https</i> a být pod doménou <i>pirati.cz</i>."
        ),
    )

    issues = models.ManyToManyField(
        ContractIssue,
        blank=True,
        related_name="contracts",
        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"
        OTHER = "other", "Jiné"

    cost_amount = models.PositiveIntegerField(
        blank=True,
        null=True,
        verbose_name="Náklady (Kč)",
        help_text="Pokud se smlouvu nejsou spojené náklady, nevyplňuj vůbec.",
    )

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

    cost_unit_other = models.CharField(
        max_length=256,
        verbose_name="Jednotka nákladů (jiné)",
        help_text='Je nutno vyplnit v případě, že máš vybranou možnost "jiné" v jednotce nákladů.',
        blank=True,
        null=True,
    )

    filing_area = models.ForeignKey(
        ContractFilingArea,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name="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 a pro tajné čtenáře.",
    )

    @property
    def primary_contract_url(self) -> typing.Union[None, str]:
        if self.primary_contract is None:
            return

        return reverse(
            "contracts:view_contract",
            args=(self.primary_contract.id,),
        )

    @property
    def url(self) -> str:
        return reverse(
            "contracts:view_contract",
            args=(self.id,),
        )

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

    def get_private_files(self):
        return ContractFile.objects.filter(
            contract=self,
            is_public=False,
        ).all()

    def calculate_signing_parties_sign_date(self) -> None:
        contractees_latest_sign_date = None
        signees_latest_sign_date = None

        for contractee_signature in self.contractee_signatures.all():
            if (
                contractees_latest_sign_date is None
                or contractee_signature.date > contractees_latest_sign_date
            ):
                contractees_latest_sign_date = contractee_signature.date

        for signee_signature in self.signee_signatures.all():
            if (
                signees_latest_sign_date is None
                or signee_signature.date > signees_latest_sign_date
            ):
                signees_latest_sign_date = signee_signature.date

        if (
            contractees_latest_sign_date is not None
            and signees_latest_sign_date is not None
        ):
            self.all_parties_sign_date = max(
                (contractees_latest_sign_date, signees_latest_sign_date)
            )

        self.save()

    def clean(self):
        if not self.is_public and not self.publishing_rejection_comment:
            raise ValidationError(
                {
                    "publishing_rejection_comment": "Pokud smlouva není veřejná, toto pole musí být vyplněné."
                }
            )
        elif self.is_public and self.publishing_rejection_comment:
            raise ValidationError(
                {
                    "publishing_rejection_comment": "Nemůže být definováno, pokud je smlouva veřejná."
                }
            )

        if (
            self.paper_form_state not in (
                self.PaperFormStates.STORED,
                self.PaperFormStates.SHREDDED,
                self.PaperFormStates.LOST
            )
            and not self.paper_form_person_responsible
        ):
            raise ValidationError(
                {
                    "paper_form_person_responsible": (
                        "Musí být vyplněno, pokud smlouva není uložená / skartovaná / ztracená."
                    )
                }
            )

        if (
            self.valid_start_date
            and self.valid_end_date
            and self.valid_start_date > self.valid_end_date
        ):
            raise ValidationError(
                {
                    "valid_end_date": "Konec platnosti nemůže být definován dříve, než začátek."
                }
            )

        if self.cost_amount is None and self.cost_unit:  # 0 is falsy, but a value
            raise ValidationError(
                {"cost_unit": "Nemůže být definováno, pokud nejsou zadány náklady."}
            )

        if (
            self.cost_amount is not None  # 0 is falsy, but a value
            and not self.cost_unit
        ):
            raise ValidationError(
                {"cost_amount": "Nemůže být definováno bez jednoty nákladů."}
            )

        if self.cost_unit == self.CostUnits.OTHER and not self.cost_unit_other:
            raise ValidationError(
                {
                    "cost_unit_other": 'Musí být definováno, pokud je vybrána jednotka nákladů "jiné".'
                }
            )
        elif self.cost_unit != self.CostUnits.OTHER and self.cost_unit_other:
            raise ValidationError(
                {
                    "cost_unit_other": 'Nemůže být definováno, pokud není vybrána jednotka nákladů "jiné".'
                }
            )

        if (
            self.primary_contract is not None
            and self.is_public
            and not self.primary_contract.is_public
        ):
            raise ValidationError(
                {
                    "is_public": "Primární smlouva je neveřejná, tato smlouva nemůže být veřejná."
                }
            )

        return super().clean()

    def save(self, *args, **kwargs):
        self.updated_on = get_current_utc_timestamp()

        return super().save(*args, **kwargs)

    # Settings

    settings = app_settings.ContractSettings("Nastavení")

    class Meta:
        app_label = "contracts"

        verbose_name = "Smlouva"
        verbose_name_plural = "Smlouvy"

        ordering = (
            "-created_on",
            "-updated_on",
            "name",
        )

        permissions = [
            ("approve", "Schválit / zrušit schválení"),
            ("view_confidential", "Zobrazit tajné informace"),
            ("edit_when_approved", "Upravit schválené"),
            ("delete_when_approved", "Odstranit schválené"),
        ] + OwnPermissionsMixin.Meta.permissions


def get_contract_file_loaction(instance, filename):
    mimetypes_instance = mimetypes.MimeTypes()

    current_time = datetime.datetime.today()
    guessed_type = mimetypes_instance.guess_type(filename, strict=False)[0]

    extension = ""

    if guessed_type is not None:
        for mapper in mimetypes_instance.types_map_inv:
            if guessed_type not in mapper:
                continue

            extension = mapper[guessed_type]

            if isinstance(extension, list):
                extension = extension[0]

            break

    return (
        "_private/"
        f"{current_time.year}/{current_time.month}/{current_time.day}/"
        f"{uuid.uuid4()}{extension}"
    )


class ContractFileProxy(FieldFile):
    @property
    def url(self) -> str:
        return reverse(
            "contracts:download_contract_file", args=(str(self.instance.id),)
        )


class ContractFileField(models.FileField):
    attr_class = ContractFileProxy


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

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

    file = ContractFileField(
        verbose_name="Soubor",
        upload_to=get_contract_file_loaction,
    )

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

    @property
    def protected_url(self) -> str:
        return reverse(
            "contracts:download_contract_file",
            args=(self.id,),
        )

    class Meta:
        app_label = "contracts"

        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",
    )

    role = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Role",
        help_text='Např. "nájemník"',
    )

    def __str__(self) -> str:
        return f"{str(self.contractee)} - {self.date}"

    class Meta:
        app_label = "contracts"

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


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",
    )

    role = models.CharField(
        max_length=256,
        blank=True,
        null=True,
        verbose_name="Role",
        help_text='Např. "pronajímatel"',
    )

    def __str__(self) -> str:
        return f"{str(self.signee)} - {self.date}"

    class Meta:
        app_label = "contracts"

        verbose_name = "Podpis jiné smluvní strany"
        verbose_name_plural = "Podpisy jiných smluvních stran"


class ContracteeSignatureRepresentative(RepresentativeMixin, models.Model):
    signature = models.ForeignKey(
        ContracteeSignature,
        on_delete=models.CASCADE,
        related_name="representatives",
        verbose_name="Podpis naši smluvní strany",
    )

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

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

    class Meta:
        app_label = "contracts"

        verbose_name = "Zástupce naší smluvní strany"
        verbose_name_plural = "Zástupci naší smluvní strany"


class SigneeSignatureRepresentative(RepresentativeMixin, models.Model):
    signature = models.ForeignKey(
        SigneeSignature,
        on_delete=models.CASCADE,
        related_name="representatives",
        verbose_name="Podpis jiné smluvní strany",
    )

    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:
        app_label = "contracts"

        verbose_name = "Zástupce jiné smluvní strany"
        verbose_name_plural = "Zástupci jiné smluvní strany"


@receiver(post_save, sender=ContracteeSignature)
@receiver(post_save, sender=SigneeSignature)
def signing_parties_post_save_update_dates(sender, instance, *args, **kwargs) -> None:
    instance.contract.calculate_signing_parties_sign_date()


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

    url = SubdomainValidatedURLField(
        max_length=256,
        verbose_name="Odkaz",
        help_text=mark_safe(
            "Využívá se např. u koaličních smluv. Musí začínat <i>https</i> "
            "a být pod doménou <i>pirati.cz</i>."
        ),
    )

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

    class Meta:
        app_label = "contracts"

        verbose_name = "Odkaz na schválení na Fóru"
        verbose_name_plural = "Odkazy na schválení na Fóru"


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

    url = SubdomainValidatedURLField(
        max_length=256,
        verbose_name="Odkaz",
        help_text=mark_safe(
            "Musí začínat <i>https</i> a být pod doménou <i>pirati.cz</i>."
        ),
    )

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

    class Meta:
        app_label = "contracts"

        verbose_name = "Odkaz na záměr v Piroplácení"
        verbose_name_plural = "Odkazy na záměry v Piroplácení"