#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

# pyre-unsafe

from typing import Dict, List, Optional

from fbpcp.error.pcp import PcpError
from fbpcp.service.storage import StorageService
from onedocker.entity.measurement import MeasurementType
from onedocker.entity.metadata import PackageMetadata

from onedocker.repository.onedocker_package import OneDockerPackageRepository
from onedocker.service.measurement import MeasurementService
from onedocker.service.metadata import MetadataService

DEFAULT_PROD_VERSION: str = "latest"
MEASUREMENT_TYPES: List[MeasurementType] = [MeasurementType.sha256]


class OneDockerRepositoryService:
    def __init__(
        self,
        storage_svc: StorageService,
        package_repository_path: str,
        metadata_svc: Optional[MetadataService] = None,
    ) -> None:
        self.package_repo = OneDockerPackageRepository(
            storage_svc, package_repository_path
        )
        self.metadata_svc = metadata_svc
        self.measurement_svc = MeasurementService()

    def upload(
        self,
        package_name: str,
        version: str,
        source: str,
    ) -> None:
        if not self._skip_version_validation_check(version):
            all_versions = self.package_repo.get_package_versions(package_name)
            if version in all_versions:
                raise ValueError(
                    f"Version {version} already exists. Please specify another version."
                )
        self.package_repo.upload(package_name, version, source)

        if self.metadata_svc:
            self.metadata_svc.put_metadata(
                metadata=self._generate_metadata(
                    package_name=package_name, version=version, source=source
                )
            )

    def download(self, package_name: str, version: str, destination: str) -> None:
        self.package_repo.download(package_name, version, destination)

    def _generate_measurements(self, source: str) -> Dict[MeasurementType, str]:
        measurements = self.measurement_svc.generate_measurements(
            measurement_types=MEASUREMENT_TYPES, file_path=source
        )
        return measurements

    def _generate_metadata(
        self,
        package_name: str,
        version: str,
        source: str,
    ) -> PackageMetadata:
        measurements = self._generate_measurements(source)
        return PackageMetadata(
            package_name=package_name, version=version, measurements=measurements
        )

    def archive_package(self, package_name: str, version: str) -> None:
        # TODO: Archive or delete checksum file associated with the archived package if exists.
        self.package_repo.archive_package(package_name, version)

    def _skip_version_validation_check(self, version: str) -> bool:
        # TODO: T129388192 remove this check when latest is no longer in use.
        if version == DEFAULT_PROD_VERSION:
            return True
        return False

    def get_package_measurements(
        self, package_name: str, version: str
    ) -> Dict[str, str]:
        if not self.metadata_svc:
            raise PcpError(
                "No MetadataService has been provided for OneDockerRepositoryService, which is required for get_package_measurements function"
            )

        md = self.metadata_svc.get_medadata(package_name=package_name, version=version)

        return {k.value: v for k, v in md.measurements.items()}
