# app/src/trading/market_intelligence/common/timeframes.py

from __future__ import annotations

from dataclasses import dataclass

from src.trading.market_intelligence.common.enums import TimeframeRole
from src.trading.market_intelligence.common.types import (
    TimeframeName,
)


@dataclass(frozen=True, slots=True)
class Timeframe:
    # Единое описание временного интервала.
    #
    # Timeframe не знает ничего о бирже, стратегии или торговом решении.
    # Он только описывает длительность интервала и его базовую роль
    # внутри многоуровневого анализа рынка.
    name: TimeframeName
    duration_minutes: int
    role: TimeframeRole = TimeframeRole.UNKNOWN
    description: str | None = None


M1 = Timeframe(
    name="1m",
    duration_minutes=1,
    role=TimeframeRole.LOWER,
    description="Минутный интервал для детального краткосрочного анализа.",
)

M5 = Timeframe(
    name="5m",
    duration_minutes=5,
    role=TimeframeRole.PRIMARY,
    description="Основной рабочий интервал первого этапа Market Intelligence.",
)

M15 = Timeframe(
    name="15m",
    duration_minutes=15,
    role=TimeframeRole.CONFIRMATION,
    description="Интервал подтверждения между локальным и старшим анализом.",
)

H1 = Timeframe(
    name="1h",
    duration_minutes=60,
    role=TimeframeRole.HIGHER,
    description="Старший интервал, совместимый с текущим HTF-анализом.",
)

H4 = Timeframe(
    name="4h",
    duration_minutes=240,
    role=TimeframeRole.HIGHER,
    description="Старший интервал для будущего более глубокого анализа.",
)

D1 = Timeframe(
    name="1d",
    duration_minutes=1440,
    role=TimeframeRole.HIGHER,
    description="Дневной интервал для будущего долгосрочного контекста.",
)

W1 = Timeframe(
    name="1w",
    duration_minutes=10080,
    role=TimeframeRole.HIGHER,
    description="Недельный интервал для будущего стратегического контекста.",
)


SUPPORTED_TIMEFRAMES: tuple[Timeframe, ...] = (
    M1,
    M5,
    M15,
    H1,
    H4,
    D1,
    W1,
)


SUPPORTED_TIMEFRAME_NAMES: tuple[TimeframeName, ...] = tuple(
    timeframe.name
    for timeframe in SUPPORTED_TIMEFRAMES
)


# Базовая карта старшего таймфрейма.
# Она сохраняет совместимость с текущим MarketAnalysisService,
# где для рабочего 5m используется старший 1h.
HIGHER_TIMEFRAME_BY_NAME: dict[TimeframeName, TimeframeName] = {
    "1m": "5m",
    "5m": "1h",
    "15m": "1h",
    "1h": "4h",
    "4h": "1d",
    "1d": "1w",
}


def get_timeframe(name: TimeframeName) -> Timeframe | None:
    # Возвращает описание таймфрейма по его имени.
    # Если интервал пока не поддерживается архитектурой,
    # возвращается None вместо исключения.
    normalized_name = str(name).strip().lower()

    for timeframe in SUPPORTED_TIMEFRAMES:
        if timeframe.name == normalized_name:
            return timeframe

    return None


def require_timeframe(name: TimeframeName) -> Timeframe:
    # Возвращает Timeframe или явно сообщает о неподдерживаемом интервале.
    # Эту функцию следует использовать там, где отсутствие таймфрейма
    # является ошибкой конфигурации, а не обычным Runtime-состоянием.
    timeframe = get_timeframe(name)

    if timeframe is None:
        raise ValueError(f"Unsupported timeframe: {name}")

    return timeframe


def is_supported_timeframe(name: TimeframeName) -> bool:
    # Проверяет, известен ли интервал архитектуре Market Intelligence.
    return get_timeframe(name) is not None


def get_higher_timeframe(name: TimeframeName) -> Timeframe | None:
    # Возвращает старший таймфрейм для указанного интервала.
    # Если для интервала нет старшего уровня, возвращается None.
    normalized_name = str(name).strip().lower()
    higher_name = HIGHER_TIMEFRAME_BY_NAME.get(normalized_name)

    if higher_name is None:
        return None

    return get_timeframe(higher_name)


def get_timeframes_by_role(
    role: TimeframeRole,
) -> tuple[Timeframe, ...]:
    # Возвращает все интервалы с указанной архитектурной ролью.
    # Это пригодится Multi-Timeframe Engine без жёсткой привязки
    # к конкретным строковым значениям.
    return tuple(
        timeframe
        for timeframe in SUPPORTED_TIMEFRAMES
        if timeframe.role == role
    )


def timeframe_to_payload(timeframe: Timeframe) -> dict[str, object]:
    # Преобразует описание таймфрейма в простой словарь.
    # Это нужно для диагностики, payload и будущих snapshot.
    return {
        "name": timeframe.name,
        "duration_minutes": timeframe.duration_minutes,
        "role": timeframe.role.value,
        "description": timeframe.description,
    }