from functools import lru_cache
from typing import Any, Dict

import boto3
from attrs import define

from vbv_lernwelt.files.utils import assert_settings


@define
class S3Credentials:
    access_key_id: str
    secret_access_key: str
    region_name: str
    bucket_name: str
    default_acl: str
    presigned_expiry: int
    max_size: int


@lru_cache
def s3_get_credentials() -> S3Credentials:
    required_config = assert_settings(
        [
            "AWS_S3_ACCESS_KEY_ID",
            "AWS_S3_SECRET_ACCESS_KEY",
            "AWS_S3_REGION_NAME",
            "AWS_STORAGE_BUCKET_NAME",
            "AWS_DEFAULT_ACL",
            "AWS_PRESIGNED_EXPIRY",
            "FILE_MAX_SIZE",
        ],
        "S3 credentials not found.",
    )

    return S3Credentials(
        access_key_id=required_config["AWS_S3_ACCESS_KEY_ID"],
        secret_access_key=required_config["AWS_S3_SECRET_ACCESS_KEY"],
        region_name=required_config["AWS_S3_REGION_NAME"],
        bucket_name=required_config["AWS_STORAGE_BUCKET_NAME"],
        default_acl=required_config["AWS_DEFAULT_ACL"],
        presigned_expiry=required_config["AWS_PRESIGNED_EXPIRY"],
        max_size=required_config["FILE_MAX_SIZE"],
    )


def s3_get_client():
    credentials = s3_get_credentials()

    return boto3.client(
        service_name="s3",
        aws_access_key_id=credentials.access_key_id,
        aws_secret_access_key=credentials.secret_access_key,
        region_name=credentials.region_name,
    )


def s3_generate_presigned_post(
    *, file_path: str, file_type: str, file_name: str
) -> Dict[str, Any]:
    credentials = s3_get_credentials()
    s3_client = s3_get_client()

    acl = credentials.default_acl
    expires_in = credentials.presigned_expiry

    """
    TODO: Create a type for the presigned_data
    It looks like this:
    {
        'fields': {
            'Content-Type': 'image/png',
            'acl': 'private',
            'key': 'files/<hash>.png',
            'policy': 'some-long-base64-string',
            'x-amz-algorithm': 'AWS4-HMAC-SHA256',
            'x-amz-credential': 'AKIASOZLZI5FJDJ6XTSZ/20220405/eu-central-1/s3/aws4_request',
            'x-amz-date': '20220405T114912Z',
            'x-amz-signature': '<hash>',
        },
        'url': 'https://django-styleguide-example.s3.amazonaws.com/'
    }
    """
    presigned_data = s3_client.generate_presigned_post(
        credentials.bucket_name,
        file_path,
        Fields={
            "acl": acl,
            "Content-Type": file_type,
            f"Content-Disposition": f"attachment; filename={file_name}",
        },
        Conditions=[
            {"acl": acl},
            {"Content-Type": file_type},
            # As an example, allow file size up to 10 MiB
            # More on conditions, here:
            # https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
            ["content-length-range", 1, credentials.max_size],
            ["starts-with", "$Content-Disposition", ""],
        ],
        ExpiresIn=expires_in,
    )

    return presigned_data


def s3_generate_presigned_url(*, file_path: str) -> str:
    credentials = s3_get_credentials()
    s3_client = s3_get_client()

    return s3_client.generate_presigned_url(
        "get_object",
        Params={"Bucket": credentials.bucket_name, "Key": file_path},
        ExpiresIn=credentials.presigned_expiry,
    )


def s3_delete_file(*, file_path: str):
    credentials = s3_get_credentials()
    s3_client = s3_get_client()

    s3_client.delete_object(
        Bucket=credentials.bucket_name,
        Key=file_path,
    )
