from typing import (
    Callable,
    List,
    Optional,
)


class Boto3S3StorageService(object):
    def __init__(self, s3_client, bucket_name: str):
        self.s3_client = s3_client
        self.bucket_name = bucket_name

    def get_file_content(self, full_path: str) -> bytes:
        response = self.s3_client.get_object(
            Bucket=self.bucket_name,
            Key=full_path,
        )

        return response["Body"].read()

    def copy_file(
        self,
        source_path: str,
        destination_path: str,
    ) -> None:
        self.s3_client.copy_object(
            Bucket=self.bucket_name,
            CopySource={
                "Bucket": self.bucket_name,
                "Key": source_path,
            },
            Key=destination_path,
        )

    def delete_file(self, full_path: str) -> None:
        self.s3_client.delete_object(
            Bucket=self.bucket_name,
            Key=full_path,
        )


    def list_directories(self, path: str) -> List[str]:
        response = self.s3_client.list_objects_v2(
            Bucket=self.bucket_name,
            Delimiter="/",
            Prefix=path,
        )

        if "CommonPrefixes" not in response:
            return []

        ret = []
        for common_prefix in response["CommonPrefixes"]:
            common_prefix = common_prefix["Prefix"][:-1]
            ret.append(common_prefix.split("/")[-1])

        return ret

    def list_files(
        self,
        path: str,
        file_filter: Callable[[str], bool] = lambda x: True,
    ) -> List[str]:
        return self._list_files_recursively(
            path=path,
            file_filter=file_filter,
        )

    def _list_files_recursively(
        self,
        path: str,
        file_filter: Callable[[str], bool] = lambda x: True,
        continuation_token: Optional[str] = None,
    ) -> List[str]:
        kwargs = {
            "Bucket": self.bucket_name,
            "Delimiter": "/",
            "Prefix": path,
        }
        if continuation_token is not None:
            kwargs["ContinuationToken"] = continuation_token

        resp = self.s3_client.list_objects_v2(**kwargs)

        if "Contents" not in resp:
            return []

        ret = []
        for key in resp["Contents"]:
            full_file_name = key["Key"]
            if file_filter(full_file_name):
                ret.append(full_file_name)

        if resp["IsTruncated"]:
            ret.extend(
                self._list_files_recursively(
                    path=path,
                    file_filter=file_filter,
                    continuation_token=resp["NextContinuationToken"],
                )
            )

        return ret
