from __future__ import annotations

import time
from typing import Any


def build_profit_sense_cooldown_profile(state: dict[str, Any] | None) -> dict[str, Any]:
    """
    Compress recent exit quality into a reusable cooldown profile so bridge and
    pipeline openings share the same post-close pacing rules.
    """
    profile = {
        "bias_label": "NEUTRAL",
        "cooldown_add_sec": 0,
        "cooldown_multiplier": 1.0,
        "allow_fast_reentry": True,
        "floor_sec": 60,
        "consecutive_losses": int((state or {}).get("consecutive_losses", 0) or 0),
        "opportunity_state": "UNKNOWN",
        "profit_capture_ratio": 0.0,
        "profit_giveback_atr": 0.0,
        "peak_profit_atr": 0.0,
        "momentum_decay_score": 0.0,
    }
    if not isinstance(state, dict):
        return profile

    telemetry = state.get("trailing_telemetry", {})
    if not isinstance(telemetry, dict):
        telemetry = {}
    rows = state.get("recent_closures", [])
    if not isinstance(rows, list):
        rows = []
    last_row = rows[-1] if rows and isinstance(rows[-1], dict) else {}

    opportunity_state = str(
        last_row.get("opportunity_state") or telemetry.get("opportunity_state") or "UNKNOWN"
    ).upper()
    capture_ratio = float(
        last_row.get("profit_capture_ratio", telemetry.get("profit_capture_ratio", 0.0)) or 0.0
    )
    giveback_atr = float(
        last_row.get("profit_giveback_atr", telemetry.get("profit_giveback_atr", 0.0)) or 0.0
    )
    peak_profit_atr = float(
        last_row.get("peak_profit_atr", telemetry.get("peak_profit_atr", 0.0)) or 0.0
    )
    momentum_decay_score = float(
        last_row.get("momentum_decay_score", telemetry.get("momentum_decay_score", 0.0)) or 0.0
    )
    last_pnl = float(last_row.get("pnl", 0.0) or 0.0) if last_row else 0.0
    consecutive_losses = int(profile["consecutive_losses"])

    profile.update(
        {
            "opportunity_state": opportunity_state,
            "profit_capture_ratio": round(capture_ratio, 4),
            "profit_giveback_atr": round(giveback_atr, 4),
            "peak_profit_atr": round(peak_profit_atr, 4),
            "momentum_decay_score": round(momentum_decay_score, 4),
        }
    )

    cooldown_add_sec = 0
    cooldown_multiplier = 1.0
    allow_fast_reentry = True
    bias_label = "NEUTRAL"

    if last_pnl < 0 and consecutive_losses > 0:
        cooldown_add_sec += min(15 * 60, consecutive_losses * 3 * 60)
        cooldown_multiplier = max(cooldown_multiplier, 1.0 + min(0.60, consecutive_losses * 0.16))
        allow_fast_reentry = False
        bias_label = "LOSS_STREAK"

    if opportunity_state in {"LOSS_EXPANDING", "LOSS_STABILIZING"}:
        cooldown_add_sec += 3 * 60
        cooldown_multiplier = max(cooldown_multiplier, 1.18)
        allow_fast_reentry = False
        bias_label = "LOSS_PRESSURE"

    if peak_profit_atr >= 1.0 and capture_ratio <= 0.42:
        cooldown_add_sec += 4 * 60
        cooldown_multiplier = max(cooldown_multiplier, 1.22)
        allow_fast_reentry = False
        bias_label = "CAPTURE_FAILURE"

    if opportunity_state in {"PROFIT_GIVEBACK", "PROFIT_EXHAUSTING"} and giveback_atr >= 0.45:
        cooldown_add_sec += 3 * 60
        cooldown_multiplier = max(cooldown_multiplier, 1.18)
        allow_fast_reentry = False
        bias_label = "PROFIT_GIVEBACK"

    if momentum_decay_score >= 0.65 and capture_ratio <= 0.55 and peak_profit_atr >= 0.8:
        cooldown_add_sec += 2 * 60
        cooldown_multiplier = max(cooldown_multiplier, 1.12)
        allow_fast_reentry = False
        if bias_label == "NEUTRAL":
            bias_label = "MOMENTUM_DECAY"

    if (
        bias_label == "NEUTRAL"
        and peak_profit_atr >= 1.0
        and capture_ratio >= 0.78
        and consecutive_losses <= 0
    ):
        cooldown_multiplier = min(cooldown_multiplier, 0.88)
        bias_label = "HEALTHY_CAPTURE"

    profile.update(
        {
            "bias_label": bias_label,
            "cooldown_add_sec": int(max(0, cooldown_add_sec)),
            "cooldown_multiplier": float(max(0.75, min(1.8, cooldown_multiplier))),
            "allow_fast_reentry": bool(allow_fast_reentry),
            "floor_sec": 2 * 60 if not allow_fast_reentry else 60,
        }
    )
    return profile


def cooldown_remaining_sec(
    state: dict[str, Any] | None,
    *,
    cooldown_sec: int,
    now_ts: float | None = None,
) -> int:
    required_cooldown = int(max(30, cooldown_sec or 0))
    profit_profile = build_profit_sense_cooldown_profile(state)
    state_dict = state if isinstance(state, dict) else {}
    now_value = float(now_ts if now_ts is not None else time.time())
    try:
        rows = state_dict.get("recent_closures", [])
        if isinstance(rows, list) and rows:
            last = rows[-1] if isinstance(rows[-1], dict) else {}
            last_pnl = float(last.get("pnl", 0.0) or 0.0)
            last_action = str(last.get("close_action", "CLOSE") or "CLOSE").upper()
            risk_tier = str(last.get("counter_trend_risk", "LOW") or "LOW").upper()
            if last_pnl > 0 and risk_tier in {"LOW", "MEDIUM"}:
                required_cooldown = min(required_cooldown, 90)
                if last_action in {"CLOSE", "CLOSE_TICKET"}:
                    required_cooldown = min(required_cooldown, 60)
            elif last_pnl < 0:
                required_cooldown = int(min(12 * 60, max(required_cooldown, 7 * 60)))
    except Exception:
        pass

    required_cooldown = int(
        required_cooldown * float(profit_profile.get("cooldown_multiplier", 1.0) or 1.0)
    )
    required_cooldown += int(profit_profile.get("cooldown_add_sec", 0) or 0)
    required_cooldown = max(int(profit_profile.get("floor_sec", 60) or 60), required_cooldown)
    required_cooldown = min(25 * 60, required_cooldown)

    last_close_time = float(state_dict.get("last_close_time", 0.0) or 0.0)
    if last_close_time <= 0:
        return 0
    elapsed = now_value - last_close_time
    if elapsed >= required_cooldown:
        return 0
    return int(max(0, required_cooldown - elapsed))


def same_side_reentry_veto(
    state: dict[str, Any] | None,
    side: str,
    *,
    now_ts: float | None = None,
) -> tuple[int, str]:
    if not isinstance(state, dict):
        return 0, ""
    side_upper = str(side or "").upper()
    if side_upper not in {"BUY", "SELL"}:
        return 0, ""

    rows = state.get("recent_closures", [])
    if not isinstance(rows, list):
        rows = []
    last_row = rows[-1] if rows and isinstance(rows[-1], dict) else {}
    last_side = str(last_row.get("side") or state.get("last_closed_side", "") or "").upper()
    if last_side != side_upper:
        return 0, ""

    now_value = float(now_ts if now_ts is not None else time.time())
    close_action = str(
        last_row.get("close_action") or state.get("last_close_action", "CLOSE") or "CLOSE"
    ).upper()
    last_close_ts = max(
        float(state.get("last_close_time", 0.0) or 0.0),
        float(state.get("last_closed_ts", 0.0) or 0.0),
        float(last_row.get("ts", 0.0) or 0.0),
    )
    if last_close_ts <= 0:
        return 0, ""

    if close_action == "CLOSE_PARTIAL":
        partial_lock = 60
        elapsed = now_value - last_close_ts
        if elapsed >= partial_lock:
            return 0, ""
        return int(partial_lock - elapsed), "PARTIAL_CLOSE_COOLDOWN"

    last_pnl = float(last_row.get("pnl", 0.0) or 0.0) if last_row else 0.0
    opportunity_state = str(last_row.get("opportunity_state", "UNKNOWN") or "UNKNOWN").upper()
    peak_profit_atr = float(last_row.get("peak_profit_atr", 0.0) or 0.0)
    capture_ratio = float(last_row.get("profit_capture_ratio", 0.0) or 0.0)
    giveback_atr = float(last_row.get("profit_giveback_atr", 0.0) or 0.0)

    reentry_lock_sec = 120
    reason = "SAME_SIDE_REENTRY_LOCK"
    if (
        last_pnl < 0
        or opportunity_state in {"LOSS_EXPANDING", "LOSS_STABILIZING"}
        or (peak_profit_atr >= 0.8 and capture_ratio <= 0.45)
        or giveback_atr >= 0.45
    ):
        reentry_lock_sec = 300
        reason = "SAME_SIDE_REENTRY_AFTER_BAD_EXIT"

    elapsed = now_value - last_close_ts
    if elapsed >= reentry_lock_sec:
        return 0, ""
    return int(max(1, reentry_lock_sec - elapsed)), reason
