# Copyright (c) 2022 CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com
from dataclasses import dataclass, field
from datetime import datetime
from typing import NamedTuple, Optional
from dateutil.parser import isoparse

from czc.enum_util import AutoExtendEnum
from czc.type_util import deserializable
from voluptuous import Schema, All, Any, SetTo, Required, Coerce, Maybe

from src.common.helpers import NonEmptyString


def _parse_datetime(value):
    if isinstance(value, datetime):
        return value
    return isoparse(value)


class BillingIngestStatus(AutoExtendEnum):
    success = 'success'
    failure = 'failure'
    user_actionable_failure = 'user_actionable_failure'
    no_data = 'no_data'
    no_change = 'no_change'
    account_disabled = 'account_disabled'
    missing_parameter = 'missing_parameter'
    new_connection = 'new_connection'
    rate_limited = 'rate_limited'
    ingest_disabled = 'ingest_disabled'
    running = 'running'
    new_data = 'new_data'
    access_denied = 'access_denied'
    recovering = 'recovering'  # Used when a connection is unpaused from an error state


class PausedState(AutoExtendEnum):
    unpaused = 'unpaused'
    paused_by_user = 'paused_by_user'
    paused_by_system = 'paused_by_system'


class BillingReportFormat(AutoExtendEnum):
    aws = 'aws'
    snowflake = 'snowflake'
    common = 'common'
    presidio = 'presidio'


class ConnectionType(AutoExtendEnum):
    implicit = 'implicit'
    managed = 'managed'
    tier1 = 'tier1'


@deserializable(schema=Schema({
    Required('cz_organization_id'): str,
    Required('cloud_account_id'): str,
    Required(All(Any('billing_entity', 'cloud_provider'), SetTo('billing_entity'))): NonEmptyString,
    'role_arn': Maybe(str),
    'external_id': Maybe(str),
    'bucket_name': Maybe(str),
    'bucket_path': Maybe(str),
    'provisioning_process': Maybe(str),
    'creation_date': Maybe(_parse_datetime),
    Required('is_enabled'): bool,
    'metadata': dict,
    'billing_report_format': Maybe(Coerce(BillingReportFormat)),
    'cz_account_name': Maybe(str),
}))
class CloudAccountInfo(NamedTuple):
    cz_organization_id: str
    cloud_account_id: str
    billing_entity: str
    role_arn: Optional[str]
    external_id: Optional[str]
    bucket_name: Optional[str]
    bucket_path: Optional[str]
    provisioning_process: Optional[str]
    creation_date: Optional[datetime]
    is_enabled: bool
    metadata: dict
    billing_report_format: Optional[BillingReportFormat] = None
    cz_account_name: Optional[str] = None


@deserializable()
class S3BucketInfo(NamedTuple):
    bucket_name: Optional[str] = None
    bucket_path: Optional[str] = None
    cloud_account_id: Optional[str] = None
    role_arn: Optional[str] = None
    external_id: Optional[str] = None
    is_local: bool = False


@dataclass(frozen=True)
class _BillingConnection:
    id: str
    name: str
    billing_report_format: BillingReportFormat
    connection_type: ConnectionType
    billing_entity: str
    s3_bucket_info: Optional[S3BucketInfo] = None
    billing_account_id: Optional[str] = None
    expected_ingest_rate: Optional[float] = None
    created: Optional[datetime] = None
    last_updated: Optional[datetime] = None
    paused_state: PausedState = PausedState.unpaused
    is_deleted: bool = False
    metadata: dict = field(default_factory=dict)
    capabilities: dict = field(default_factory=dict)
    is_hidden: bool = False

    @property
    def is_implicit(self) -> bool:
        return self.connection_type == ConnectionType.implicit

    @property
    def is_draft(self) -> bool:
        return self.s3_bucket_info is None

    @property
    def is_enabled(self) -> bool:
        return self.paused_state == PausedState.unpaused


@deserializable()
@dataclass(frozen=True)
class BillingConnection(_BillingConnection):
    pass
