# Copyright 2024 The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Implementation of external file backends."""

from pathlib import Path
from typing import Optional

from debusine.db.models import File, FileInStore, FileStore
from debusine.server.file_backend.interface import FileBackendInterface
from debusine.utils import NotSupportedError


class ExternalDebianSuiteFileBackend(FileBackendInterface):
    """Mirror an external repository in debusine."""

    def __init__(
        self,
        file_store: FileStore,
        archive_root_url: str,
        dist: str,
        components: str,
    ):
        """Initialize instance."""
        super().__init__()
        self.db_store = file_store
        # All files are relative to the archive root
        # https://wiki.debian.org/DebianRepository/Format
        self.archive_root_url = archive_root_url
        # For Collections to grab and parse the appropriate Packages files
        self.dist = dist
        self.components = components

    def remove_file(self, db_file: File):  # noqa: U100
        """
        Remove the file from the FileStore.

        This may change if we cache the remote file locally.
        """
        raise NotSupportedError(
            "ExternalDebianSuiteFileBackend is a read-only file backend"
        )

    def _remove_file(self, db_file: File):  # noqa: U100
        """
        Remove the file pointed by fileobj from the underlying storage.

        This may change if we cache the remote file locally.
        """
        raise NotImplementedError()

    def add_file(
        self, local_path: Path, db_file: Optional[File] = None  # noqa: U100
    ) -> File:
        """
        Copy local_path to underlying storage and register it in the FileStore.

        The optional ``fileobj`` provides the File used to identify the content
        available in local_path.
        """
        raise NotSupportedError(
            "ExternalDebianSuiteFileBackend is a read-only file backend"
        )

    def get_local_path(self, db_file: File) -> Optional[Path]:  # noqa: U100
        """
        Return None: no local path for now.

        This may change if we cache the remote file locally.
        """
        return None

    def get_url(self, db_file: File) -> Optional[str]:
        """Return a URL pointing to the content when possible or None."""
        file_in_store = FileInStore.objects.get(
            file=db_file, store=self.db_store
        )
        return self.archive_root_url + file_in_store.data['relative_url']

    def add_remote_file(
        self,
        relative_url: str,
        hash_digest: bytes,
        size: int,
    ) -> File:
        """Register remote file in the DB."""
        db_file_kwargs = {
            File.current_hash_algorithm: hash_digest,
            'size': size,
        }
        db_file, created = File.objects.get_or_create(**db_file_kwargs)
        FileInStore.objects.get_or_create(
            store=self.db_store,
            file=db_file,
            data={'relative_url': relative_url},
        )
        return db_file
