# -*- coding: utf-8 -*-

from __future__ import annotations

import json
from typing import Any

import boto3

from parsedmarc.log import logger
from parsedmarc.utils import human_timestamp_to_datetime


class S3Client(object):
    """A client for interacting with Amazon S3"""

    def __init__(
        self,
        bucket_name: str,
        bucket_path: str,
        region_name: str,
        endpoint_url: str,
        access_key_id: str,
        secret_access_key: str,
    ):
        """
        Initializes the S3Client
        Args:
            bucket_name (str): The S3 Bucket
            bucket_path (str): The path to save reports
            region_name (str): The region name
            endpoint_url (str): The endpoint URL
            access_key_id (str): The access key id
            secret_access_key (str): The secret access key
        """
        self.bucket_name = bucket_name
        self.bucket_path = bucket_path
        self.metadata_keys = [
            "org_name",
            "org_email",
            "report_id",
            "begin_date",
            "end_date",
        ]

        # https://github.com/boto/boto3/blob/1.24.7/boto3/session.py#L312
        self.s3 = boto3.resource(
            "s3",
            region_name=region_name,
            endpoint_url=endpoint_url,
            aws_access_key_id=access_key_id,
            aws_secret_access_key=secret_access_key,
        )
        self.bucket = self.s3.Bucket(self.bucket_name)  # type: ignore

    def save_aggregate_report_to_s3(self, report: dict[str, Any]):
        self.save_report_to_s3(report, "aggregate")

    def save_failure_report_to_s3(self, report: dict[str, Any]):
        self.save_report_to_s3(report, "failure")

    def save_smtp_tls_report_to_s3(self, report: dict[str, Any]):
        self.save_report_to_s3(report, "smtp_tls")

    def save_report_to_s3(self, report: dict[str, Any], report_type: str):
        if report_type == "smtp_tls":
            # SMTP TLS reports (RFC 8460) are flat — they have no
            # `report_metadata` sub-object — and parse_smtp_tls_report_json
            # stores begin_date as the ISO string from the report JSON
            # (per SMTPTLSReport's TypedDict).
            report_date = human_timestamp_to_datetime(report["begin_date"])
            report_id = report["report_id"]
            metadata_source = {
                "org_name": report.get("organization_name"),
                "report_id": report.get("report_id"),
                "begin_date": str(report.get("begin_date")),
                "end_date": str(report.get("end_date")),
            }
        else:
            report_date = human_timestamp_to_datetime(
                report["report_metadata"]["begin_date"]
            )
            report_id = report["report_metadata"]["report_id"]
            metadata_source = report["report_metadata"]
        path_template = "{0}/{1}/year={2}/month={3:02d}/day={4:02d}/{5}.json"
        object_path = path_template.format(
            self.bucket_path,
            report_type,
            report_date.year,
            report_date.month,
            report_date.day,
            report_id,
        )
        logger.debug(
            f"Saving {report_type} report to s3://{self.bucket_name}/{object_path}"
        )
        object_metadata = {
            k: v
            for k, v in metadata_source.items()
            if k in self.metadata_keys and v is not None
        }
        self.bucket.put_object(
            Body=json.dumps(report, default=str),
            Key=object_path,
            Metadata=object_metadata,
        )

    def close(self):
        """Clean up the boto3 resource."""
        try:
            if self.s3.meta is not None:
                self.s3.meta.client.close()
        except Exception:
            pass

    # Backward-compatible alias
    save_forensic_report_to_s3 = save_failure_report_to_s3
