"""Bifrost interface for Amazon Simple Storage Service"""

from byfrost.shared import errors
from byfrost.shared.config import provider
from ..shared.types.dataclass import file as bfile
from ..shared.types.dataclass import bridge
from typing import List, Tuple
from ..shared.errors.interface import BifrostError
from ..shared.config import option, url
from ..shared.errors import loga
import boto3
import os


class SimpleStorageService:
    def __init__(self, bc: bridge.BridgeConfig, client: boto3.client):
        self.provider = provider.providers[bc.provider]
        self.default_bucket = bc.default_bucket
        self.region = bc.region
        self.access_key = bc.access_key
        self.secret_key = bc.secret_key
        self.default_timeout = bc.default_timeout
        self.client = client
        self.enable_debug = bc.enable_debug
        self.public_read = bc.public_read
        self.use_async = bc.use_async

    def upload_file(
        self, file_face: bfile.File
    ) -> Tuple[bfile.UploadedFile, BifrostError]:
        """upload_file uploads a file to Amazon Simple Storage Service and returns an error if one occurs."""
        if file_face is None:
            return None, BifrostError("file is none", errors.ErrBadRequest)

        # verify that the file is derived from File
        if not isinstance(file_face, bfile.File):
            return None, BifrostError(
                "invalid file type: {}".format(type(file_face)), errors.ErrBadRequest
            )

        # verify that the file has a valid path
        if file_face.path is None or file_face.path == "":
            return None, BifrostError("no path specified", errors.ErrBadRequest)

        if file_face.filename is None or file_face.filename == "":
            file_face.filename = os.path.basename(file_face.path)
        else:
            base_name, ext = os.path.splitext(file_face.filename)
            if ext == "":  # If the provided filename has no extension
                # Extract the extension from the path and append it to the filename
                _, ext_from_path = os.path.splitext(file_face.path)
                file_face.filename += ext_from_path

        # verify that the file has a valid options
        if file_face.options is None:
            file_face.options = bfile.Options()

        # verify the connection
        if not self.is_connected():
            return None, BifrostError(
                "no active Amazon Simple Storage Service client", errors.ErrClientError
            )
        # verify that the file has a valid acl
        if (
            file_face.options.acl is None
            or file_face.options.acl == ""
            and self.public_read
        ):
            file_face.options.acl = option.ACLPublicRead

        # verify that the file has a valid metadata
        if file_face.options.metadata is None:
            file_face.options.metadata = {}

        # upload file to Amazon Simple Storage Service
        obj = self.client.Object(self.default_bucket, file_face.filename)
        with open(file_face.path, "rb") as f:
            obj.put(
                Body=f,
                ACL=file_face.options.acl,
                Metadata=file_face.options.metadata,
            )
        obj = obj.get()

        return (
            bfile.UploadedFile(
                name=file_face.filename,
                bucket=self.default_bucket,
                path=file_face.path,
                size=obj["ContentLength"],
                url=url.URLSimpleStorageService.format(
                    self.default_bucket, self.region, file_face.filename
                ),
                preview=url.URLSimpleStorageService.format(
                    self.default_bucket, self.region, file_face.filename
                ),
                provider_object=obj,
            ),
            None,
        )

    def upload_multi_file(
        self,
        multi_face: bfile.MultiFile,
    ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
        """upload multi file uploads multiple files to Amazon Simple Storage Service and returns an error if one occurs."""
        if multi_face is None:
            return None, BifrostError("multi file is none", errors.ErrBadRequest)

        # verify that the multi file is derived from MultiFile
        if not isinstance(multi_face, bfile.MultiFile):
            return None, BifrostError(
                "invalid multi file type: {}".format(type(multi_face)),
                errors.ErrBadRequest,
            )

        # verify that the multi file has a valid files
        if multi_face.files is None or len(multi_face.files) == 0:
            return None, BifrostError("no files specified", errors.ErrBadRequest)

        # verify the connection
        if not self.is_connected():
            return None, BifrostError(
                "no active Amazon Simple Storage Service client", errors.ErrClientError
            )

        # upload files to Amazon Simple Storage Service
        uploaded_files = []

        for file_face in multi_face.files:
            # verify that the multi file has a valid global options
            if multi_face.global_options is not None:
                # for each option in the global option dataclass, if it is not present in the file option dataclass, then add it to the file option dataclass
                for option in multi_face.global_options.__dataclass_fields__:
                    if not hasattr(file_face.options, option):
                        setattr(
                            file_face.options,
                            option,
                            getattr(multi_face.global_options, option),
                        )

            uploaded_file, err = self.upload_file(file_face)

            if err is not None:
                if self.enable_debug:
                    loga.error(
                        "Upload for file at path %s failed with err: %s",
                        file_face.path,
                        err.message,
                    )
                uploaded_files.append(
                    bfile.UploadedFile(
                        error=err, name=file_face.filename, path=file_face.path
                    )
                )
                continue
            uploaded_files.append(uploaded_file)
        return uploaded_files, None

    def disconnect(self) -> BifrostError:
        """disconnect closes the Amazon Simple Storage Service client connection and returns an error if one occurs."""
        if self.is_connected():
            self.client = None

    def config(self) -> bridge.BridgeConfig:
        """config returns the provider configuration."""
        return bridge.BridgeConfig(
            provider=self.provider,
            region=self.region,
            default_bucket=self.default_bucket,
            access_key=self.access_key,
            secret_key=self.secret_key,
            default_timeout=self.default_timeout,
            enable_debug=self.enable_debug,
            public_read=self.public_read,
            use_async=self.use_async,
        )

    def is_connected(self) -> bool:
        """is_connected returns true if there is an active connection to the provider."""
        return self.client is not None

    def upload_folder(
        self,
        fold_face: bfile.MultiFile,
    ) -> Tuple[List[bfile.UploadedFile], BifrostError]:
        """upload_folder uploads a folder to the Simple Storage Service and returns an error if one occurs."""
        ...
