# app/src/trading/market_analysis/structure.py

from __future__ import annotations

from collections.abc import Sequence

from src.core.numbers import safe_float
from src.core.types import NumericLike
from src.market_data.acquisition.models.candle import Candle
from src.trading.market_analysis.models import MarketStructure


def structure_params(
    *,
    atr_percent: NumericLike | None,
    candle_noise_score: NumericLike | None,
    structure_window: int = 30,
    structure_swing_left: int = 2,
    structure_swing_right: int = 2,
    min_clean_candle_score: float = 0.55,
) -> tuple[int, int, int]:
    atr_value = safe_float(atr_percent) or 0.0
    noise_value = safe_float(candle_noise_score)

    window = structure_window
    left = structure_swing_left
    right = structure_swing_right

    if atr_value >= 0.9:
        window = 50
        left = 3
        right = 3
    elif atr_value >= 0.45:
        window = 40
        left = 3
        right = 2
    elif atr_value <= 0.18:
        window = 24
        left = 2
        right = 2

    if noise_value is not None and noise_value < min_clean_candle_score:
        window = max(window, 45)
        left = max(left, 3)
        right = max(right, 3)

    return window, left, right


# Определить структуру рынка по swing high / swing low:
# HH/HL = восходящая структура
# LH/LL = нисходящая структура
# MIXED = противоречивая структура
def market_structure(
    candles: Sequence[Candle],
    *,
    atr_percent: NumericLike | None = None,
    candle_noise_score: NumericLike | None = None,
    structure_window: int = 30,
    structure_swing_left: int = 2,
    structure_swing_right: int = 2,
    min_clean_candle_score: float = 0.55,
) -> tuple[MarketStructure, str]:
    resolved_window, left, right = structure_params(
        atr_percent=atr_percent,
        candle_noise_score=candle_noise_score,
        structure_window=structure_window,
        structure_swing_left=structure_swing_left,
        structure_swing_right=structure_swing_right,
        min_clean_candle_score=min_clean_candle_score,
    )

    window = list(candles[-resolved_window:])
    min_required = max(10, left + right + 6)

    if len(window) < min_required:
        return MarketStructure.UNKNOWN, "STRUCTURE_NOT_ENOUGH_CANDLES"

    swing_highs: list[float] = []
    swing_lows: list[float] = []

    for index in range(left, len(window) - right):
        current = window[index]

        previous_items = window[index - left:index]
        next_items = window[index + 1:index + 1 + right]

        high = safe_float(current.high_price)
        low = safe_float(current.low_price)

        if high is None or low is None:
            continue

        neighbor_highs: list[float] = []
        neighbor_lows: list[float] = []

        for item in previous_items + next_items:
            item_high = safe_float(item.high_price)
            item_low = safe_float(item.low_price)

            if item_high is not None:
                neighbor_highs.append(item_high)

            if item_low is not None:
                neighbor_lows.append(item_low)

        if len(neighbor_highs) != left + right:
            continue

        if len(neighbor_lows) != left + right:
            continue

        if all(high > item_high for item_high in neighbor_highs):
            swing_highs.append(high)

        if all(low < item_low for item_low in neighbor_lows):
            swing_lows.append(low)

    if len(swing_highs) < 2 or len(swing_lows) < 2:
        return (
            MarketStructure.UNKNOWN,
            f"STRUCTURE_NOT_ENOUGH_SWINGS:"
            f"window={resolved_window}:left={left}:right={right}:"
            f"highs={len(swing_highs)}:lows={len(swing_lows)}",
        )

    last_high = swing_highs[-1]
    prev_high = swing_highs[-2]

    last_low = swing_lows[-1]
    prev_low = swing_lows[-2]

    has_hh = last_high > prev_high
    has_hl = last_low > prev_low

    has_lh = last_high < prev_high
    has_ll = last_low < prev_low

    if has_hh and has_hl:
        return (
            MarketStructure.HH_HL,
            f"HIGHER_HIGH_HIGHER_LOW:"
            f"window={resolved_window}:left={left}:right={right}",
        )

    if has_lh and has_ll:
        return (
            MarketStructure.LH_LL,
            f"LOWER_HIGH_LOWER_LOW:"
            f"window={resolved_window}:left={left}:right={right}",
        )

    return (
        MarketStructure.MIXED,
        f"MIXED_MARKET_STRUCTURE:"
        f"window={resolved_window}:left={left}:right={right}",
    )