"""
Data types representing files on disk.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, final, override

from betty.ancestry.description import HasDescription
from betty.ancestry.has_citations import HasCitations
from betty.ancestry.has_links import HasLinks
from betty.ancestry.has_notes import HasNotes
from betty.ancestry.media_type import HasMediaType
from betty.copyright_notice import CopyrightNoticeDefinition
from betty.license import LicenseDefinition
from betty.locale.localizable.gettext import _, ngettext
from betty.model import EntityDefinition
from betty.model.association import BidirectionalToManyMultipleTypes, ToManyAssociates
from betty.plugin.schema import PluginIdSchema
from betty.privacy import HasPrivacy, Privacy

if TYPE_CHECKING:
    from collections.abc import MutableSequence
    from pathlib import Path

    from betty.ancestry.citation import Citation
    from betty.ancestry.file_reference import FileReference  # noqa: F401
    from betty.ancestry.link import Link
    from betty.ancestry.note import Note
    from betty.copyright_notice import CopyrightNotice
    from betty.json.linked_data import JsonLdObject
    from betty.license import License
    from betty.locale.localizable import Localizable, ResolvableLocalizable
    from betty.media_type import MediaType
    from betty.portable import PortableMapping
    from betty.project import Project


@final
@EntityDefinition(
    "file",
    label=_("File"),
    label_plural=_("Files"),
    label_countable=ngettext("{count} file", "{count} files"),
)
class File(
    HasDescription,
    HasPrivacy,
    HasLinks,
    HasMediaType,
    HasNotes,
    HasCitations,
):
    """
    .. plugin:: entity:file.
    """

    referees = BidirectionalToManyMultipleTypes["File", "FileReference"](
        "betty.ancestry.file_reference:FileReference",
        "file",
        label=_("Referees"),
        description=_("The entities referencing this file"),
        linked_data_embedded=True,
    )
    """
    Other entities referencing this file.
    """

    copyright_notice: CopyrightNotice | None
    """
    The copyright notice for this file.
    """

    license: License | None
    """
    The license for this file.
    """

    def __init__(
        self,
        path: Path,
        *,
        id: str | None = None,  # noqa: A002
        name: str | None = None,
        media_type: MediaType | None = None,
        description: ResolvableLocalizable | None = None,
        notes: ToManyAssociates[Note] | None = None,
        citations: ToManyAssociates[Citation] | None = None,
        privacy: Privacy | None = None,
        links: MutableSequence[Link] | None = None,
        copyright_notice: CopyrightNotice | None = None,
        license: License | None = None,  # noqa: A002
    ):
        super().__init__(
            id,
            media_type=media_type,
            description=description,
            notes=notes,
            citations=citations,
            privacy=privacy,
            links=links,
        )
        self._path = path
        self._name = name
        self.copyright_notice = copyright_notice
        self.license = license

    @property
    def name(self) -> str:
        """
        The file name.
        """
        return self._name or self.path.name

    @property
    def path(self) -> Path:
        """
        The file's path on disk.
        """
        return self._path

    @override
    @property
    def label(self) -> Localizable:
        return self.description or super().label

    @override
    @classmethod
    async def linked_data_schema(cls, project: Project, /) -> JsonLdObject:
        schema = await super().linked_data_schema(project)
        schema.add_property(
            "copyrightNotice",
            PluginIdSchema(
                CopyrightNoticeDefinition.type(),
                await project.plugins.plugins(CopyrightNoticeDefinition),
            ),
            False,
        )
        schema.add_property(
            "license",
            PluginIdSchema(
                LicenseDefinition.type(),
                await project.plugins.plugins(LicenseDefinition),
            ),
            False,
        )
        return schema

    @override
    async def dump_linked_data(self, project: Project, /) -> PortableMapping:
        portable = await super().dump_linked_data(project)
        if self.copyright_notice:
            portable["copyrightNotice"] = self.copyright_notice.plugin().id
        if self.license:
            portable["license"] = self.license.plugin().id
        return portable
