import gzip
import json
import os
import os.path as path
import shutil
import tempfile

import boto3

from src.system_var import environment


class S3Client:
    __s3_client = boto3.client('s3')
    _bucket_name: str

    def list_objects(self, bucket_name: str, folder_name: str):
        response = self.__s3_client.list_objects_v2(
            Bucket=bucket_name, Prefix=folder_name)
        if response['KeyCount'] == 0:
            return []
        contents = response['Contents']
        # 末尾がスラッシュで終わるものはフォルダとみなしてスキップする
        objects = [{'filename': content['Key'], 'size': content['Size']}
                   for content in contents if not content['Key'].endswith('/')]
        return objects

    def copy(self, src_bucket: str, src_key: str, dest_bucket: str, dest_key: str) -> None:
        copy_source = {'Bucket': src_bucket, 'Key': src_key}
        self.__s3_client.copy(copy_source, dest_bucket, dest_key)
        return

    def put_object(self, bucket: str, key: str, json_body: str, content_type='application/json') -> None:
        self.__s3_client.put_object(
            Bucket=bucket,
            Key=key,
            Body=json_body.encode('utf-8'),
            ContentType=content_type
        )

    def delete_file(self, bucket_name: str, file_key: str):
        self.__s3_client.delete_object(
            Bucket=bucket_name,
            Key=file_key
        )


class S3Bucket():
    _s3_client = S3Client()
    _bucket_name: str = None


class JskIOBucket(S3Bucket):
    _bucket_name = environment.JSK_IO_BUCKET
    _recv_folder = environment.JSK_RECEIVE_FOLDER

    _s3_file_list = None

    def get_file_list(self):
        self._s3_file_list = self._s3_client.list_objects(
            self._bucket_name, self._recv_folder)
        return self._s3_file_list

    def download_data_file(self, data_filename: str):
        temporary_dir = tempfile.mkdtemp()
        temporary_file_path = path.join(
            temporary_dir, f'{data_filename.replace(f"{self._recv_folder}/", "")}')
        with open(temporary_file_path, mode='wb') as f:
            self._s3_client.download_file(self._bucket_name, data_filename, f)
            f.seek(0)
        return temporary_file_path

    def unzip_data_file(self, filename: str):
        temp_dir = os.path.dirname(filename)
        decompress_filename = os.path.basename(filename).replace('.gz', '')
        decompress_file_path = os.path.join(temp_dir, decompress_filename)
        with gzip.open(filename, 'rb') as gz:
            with open(decompress_file_path, 'wb') as decompressed_file:
                shutil.copyfileobj(gz, decompressed_file)

        ret = [decompress_file_path]
        return ret

    def transfer_file_to_import(self, target_file: dict):
        data_import_bucket = DataImportBucket()
        transfer_from_file_path = target_file.get("filename")
        transfer_to_filename = transfer_from_file_path.replace(
            f"{self._recv_folder}/", "")
        data_import_key = f'{data_import_bucket._folder}/{transfer_to_filename}'
        self._s3_client.copy(self._bucket_name, transfer_from_file_path,
                             data_import_bucket._bucket_name, data_import_key)

    def backup_file(self, target_file: dict, datetime_key: str):
        jsk_backup_bucket = JskBackupBucket()
        backup_from_file_path = target_file.get("filename")
        backup_to_filename = backup_from_file_path.replace(
            f"{self._recv_folder}/", "")
        backup_key = f'{jsk_backup_bucket._folder}/{datetime_key}/{backup_to_filename}'
        self._s3_client.copy(self._bucket_name, backup_from_file_path,
                             jsk_backup_bucket._bucket_name, backup_key)

    def delete_file(self, target_file: dict):
        delete_path = target_file.get("filename")
        self._s3_client.delete_file(
            self._bucket_name, delete_path)


class DataImportBucket(S3Bucket):
    _bucket_name = environment.DATA_IMPORT_BUCKET
    _folder = environment.DATA_IMPORT_FOLDER


class UltmarcBucket(S3Bucket):
    _bucket_name = environment.ULTMARC_DATA_BUCKET
    _folder = environment.ULTMARC_RECEIVE_FOLDER

    def get_file_list(self):
        return self._s3_client.list_objects(self._bucket_name, self._folder)

    def backup_file(self, target_file: dict, datetime_key: str):
        # バックアップバケットにコピー
        ultmarc_backup_bucket = UltmarcBackupBucket()
        target_file_path = target_file.get("filename")
        backup_key = f'{ultmarc_backup_bucket._folder}/{datetime_key}/{target_file_path.replace(f"{self._folder}/", "")}'
        self._s3_client.copy(self._bucket_name, target_file_path,
                             ultmarc_backup_bucket._bucket_name, backup_key)

    def delete_file(self, target_file: dict):
        delete_path = target_file.get("filename")
        self._s3_client.delete_file(
            self._bucket_name, delete_path)


class UltmarcImportBucket(S3Bucket):
    _bucket_name = environment.ULTMARC_DATA_BUCKET
    _folder = environment.ULTMARC_IMPORT_FOLDER

    def transfer_file_to_import(self, target_file: dict):
        backup_from_file_path = target_file.get("filename")
        backup_to_filename = backup_from_file_path.replace(
            f"{self._folder}/", "")
        data_import_key = f'{self._folder}/{backup_to_filename}'
        self._s3_client.copy(self._bucket_name, backup_from_file_path,
                             self._bucket_name, data_import_key)


class JskUltBackupBucket(S3Bucket):
    _bucket_name = environment.JSKULT_BACKUP_BUCKET


class UltmarcBackupBucket(JskUltBackupBucket):
    _folder = environment.ULTMARC_BACKUP_FOLDER


class JskBackupBucket(JskUltBackupBucket):
    _folder = environment.JSK_BACKUP_FOLDER


class TransferResultOutputBucket(JskUltBackupBucket):
    _folder = environment.RESULT_OUTPUT_FOLDER

    def put_transfer_result(self, transfer_result: dict, datetime_key: str):
        object_key = f'{self._folder}/{datetime_key}/{environment.RESULT_OUTPUT_FILE_NAME}'
        json_body = json.dumps(transfer_result, ensure_ascii=False)
        self._s3_client.put_object(
            self._bucket_name,
            object_key,
            json_body
        )
