# This file is part of django-ca (https://github.com/mathiasertl/django-ca).
#
# django-ca is free software: you can redistribute it and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# django-ca is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along with django-ca. If not, see
# <http://www.gnu.org/licenses/>.

"""Pydantic models representing messages exchanged between various parts of the system."""

from datetime import datetime, timezone as tz
from typing import List, Optional

from pydantic import BaseModel, Field

from cryptography import x509

from django_ca import ca_settings
from django_ca.constants import HASH_ALGORITHM_TYPES
from django_ca.pydantic.base import DATETIME_EXAMPLE
from django_ca.pydantic.extensions import SignCertificateExtensions
from django_ca.pydantic.name import NameModel
from django_ca.typehints import AllowedHashTypes, HashAlgorithms


class SignCertificateMessage(BaseModel):
    """Schema for signing certificates."""

    algorithm: Optional[HashAlgorithms] = Field(
        default=None,
        description="Hash algorithm used for signing (default: same as in the certificate authority).",
        # TODO: check if this is necessary since we now have a 'literal'
        # json_schema_extra={"enum": list(sorted(HASH_ALGORITHM_TYPES))},
    )
    autogenerated: bool = Field(
        default=False, description="If the certificate should be marked as auto-generated."
    )
    csr: bytes = Field(
        title="CSR",
        description="The certificate signing request (CSR) in PEM format",
        json_schema_extra={
            "example": "-----BEGIN CERTIFICATE REQUEST-----\n...\n-----END CERTIFICATE REQUEST-----\n"
        },
    )
    expires: Optional[datetime] = Field(
        description="When the certificate is due to expire, defaults to the CA_DEFAULT_EXPIRES setting.",
        default_factory=lambda: datetime.now(tz=tz.utc) + ca_settings.CA_DEFAULT_EXPIRES,
        json_schema_extra={"example": DATETIME_EXAMPLE},
    )
    extensions: Optional[List[SignCertificateExtensions]] = Field(
        default_factory=list,
        description="**Optional** additional extensions to add to the certificate.",
    )
    profile: str = Field(
        description="Issue the certificate with the given profile.",
        default=ca_settings.CA_DEFAULT_PROFILE,
        json_schema_extra={"enum": list(sorted(ca_settings.CA_PROFILES))},
    )
    subject: NameModel = Field(description="The subject as list of name attributes.")

    def get_algorithm(self) -> Optional[AllowedHashTypes]:
        """Get algorithm class if set."""
        if self.algorithm is not None:
            return HASH_ALGORITHM_TYPES[self.algorithm]()
        return None

    def get_csr(self) -> x509.CertificateSigningRequest:
        """Get CSR encoded in this message."""
        return x509.load_pem_x509_csr(self.csr)

    def get_extensions(self) -> List[x509.Extension[x509.ExtensionType]]:
        """Get extensions encoded in this message."""
        if self.extensions is None:
            return []
        return [ext.cryptography for ext in self.extensions]
