# 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
from datetime import date, datetime
from enum import Enum
from random import randint
from typing import Any

from dateutil import relativedelta


@dataclass(frozen=True)
class KeyDefinition():
    '''
    Provides information on the keys for a generated dictionary

    value - The value for the key (usually a string)
    is_required - Whether the key is required or not
    skip - Don't provide specific test cases
    '''
    value: Any
    is_required: bool
    skip: bool = False


@dataclass(frozen=True)
class Required(KeyDefinition):
    '''
    Indicates that a key is required. Can be used to override the default set
    for a generated dictionary.
    '''
    is_required: bool = True


@dataclass(frozen=True)
class Optional(KeyDefinition):
    '''
    Indicates that a key is optional. Can be used to override the default set
    for a generated dictionary.
    '''
    is_required: bool = False


@dataclass(frozen=True)
class Dynamic(KeyDefinition):
    '''
    Indicates a key is dynamically generated. Dynamically generated keys
    by default will bypass special case testing (i.e. missing)
    '''
    is_required: bool = False  # This will cause the 'missing key' tests to get skipped


class KeyGenerator():
    def __init__(self) -> None:
        pass


class Granularity(Enum):
    HOURLY = 1
    DAILY = 2
    WEEKLY = 3
    MONTHLY = 4


_time_deltas = {
    Granularity.HOURLY: lambda x: relativedelta.relativedelta(hours=x),
    Granularity.DAILY: lambda x: relativedelta.relativedelta(days=x),
    Granularity.WEEKLY: lambda x: relativedelta.relativedelta(weeks=x),
    Granularity.MONTHLY: lambda x: relativedelta.relativedelta(months=x)
}


class FromTimeInterval(KeyGenerator):
    '''
    Generate key values based on iso formatted time intervals.
    Intervals can be monthly, weekly, daily, or hourly.

    {
        FromTimeInterval('2020-01-01T00:00:00', 12, 'monthly'): CurrencyGenerator()
    }
    '''
    start: str
    total: int
    granularity: Granularity

    def __init__(self, start: str, total: int, granularity: Granularity) -> None:
        super().__init__()
        self.start = start
        self.total = total
        self.granularity = granularity

    def __call__(self, *args: Any, **kwds: Any) -> Any:
        try:
            dt = date.fromisoformat(self.start)
        except ValueError:
            dt = datetime.fromisoformat(self.start)

        test_idx = randint(0, (self.total - 1))
        return [Dynamic((dt + _time_deltas[self.granularity](x)).isoformat(), skip=(x != test_idx)) for x in range(self.total)]
