"""Self-Optimizer — 抄自 zhutoutoutousan/worldquant-miner generation_two/self_optimizer.py 算法骨架。

每 EVAL_WINDOW 筆 alpha 結果評估成功率：
    < LOW_THRESHOLD  → exploration ×1.20 / temperature ×1.10 / mutation ×1.15
    > HIGH_THRESHOLD → 全 ×0.90（緊縮）
係數受 BOUNDS 限制避免發散。
"""

from __future__ import annotations

import json
import os
import threading
from dataclasses import dataclass
from typing import Optional, Tuple

PATH = os.path.expanduser("~/.wq_gui_self_opt.json")
_lock = threading.Lock()

EVAL_WINDOW = 100
LOW_THRESHOLD = 0.30
HIGH_THRESHOLD = 0.60
BOUNDS = {
    "exploration": (0.5, 3.0),
    "temperature": (0.3, 2.0),
    "mutation": (0.5, 2.5),
}


@dataclass
class SelfOptState:
    exploration: float = 1.0
    temperature: float = 1.0
    mutation: float = 1.0
    window_pass: int = 0
    window_total: int = 0
    lifetime_evals: int = 0
    last_action: str = "init"

    def to_dict(self) -> dict:
        return {
            "exploration": self.exploration,
            "temperature": self.temperature,
            "mutation": self.mutation,
            "window_pass": self.window_pass,
            "window_total": self.window_total,
            "lifetime_evals": self.lifetime_evals,
            "last_action": self.last_action,
        }

    @classmethod
    def from_dict(cls, d: dict) -> "SelfOptState":
        s = cls()
        for k in s.__dict__:
            if k in d:
                setattr(s, k, d[k])
        return s


def _clamp(name: str, val: float) -> float:
    lo, hi = BOUNDS[name]
    return max(lo, min(hi, val))


def load_state() -> SelfOptState:
    if not os.path.exists(PATH):
        return SelfOptState()
    try:
        with open(PATH, "r", encoding="utf-8") as f:
            return SelfOptState.from_dict(json.load(f))
    except Exception:
        return SelfOptState()


def save_state(state: SelfOptState) -> None:
    try:
        with open(PATH, "w", encoding="utf-8") as f:
            json.dump(state.to_dict(), f, indent=2)
        os.chmod(PATH, 0o600)
    except Exception:
        pass


def observe(is_pass: bool) -> Optional[Tuple[SelfOptState, str]]:
    """單筆觀測；thread-safe atomic load+modify+save。回傳 (state, msg) 若觸發了 evaluation，否則 None。"""
    with _lock:
        state = load_state()
        state.window_pass += 1 if is_pass else 0
        state.window_total += 1
        if state.window_total >= EVAL_WINDOW:
            rate = state.window_pass / max(1, state.window_total)
            if rate < LOW_THRESHOLD:
                state.exploration = _clamp("exploration", state.exploration * 1.20)
                state.temperature = _clamp("temperature", state.temperature * 1.10)
                state.mutation = _clamp("mutation", state.mutation * 1.15)
                state.last_action = f"EXPAND (rate={rate:.2f}<{LOW_THRESHOLD})"
            elif rate > HIGH_THRESHOLD:
                state.exploration = _clamp("exploration", state.exploration * 0.90)
                state.temperature = _clamp("temperature", state.temperature * 0.90)
                state.mutation = _clamp("mutation", state.mutation * 0.90)
                state.last_action = f"TIGHTEN (rate={rate:.2f}>{HIGH_THRESHOLD})"
            else:
                state.last_action = f"HOLD (rate={rate:.2f})"
            state.lifetime_evals += 1
            state.window_pass = 0
            state.window_total = 0
            save_state(state)
            msg = (
                f"🤖 self-opt #{state.lifetime_evals}: {state.last_action} → "
                f"explore={state.exploration:.2f} temp={state.temperature:.2f} "
                f"mut={state.mutation:.2f}"
            )
            return state, msg
        save_state(state)
        return None


def get_exploration() -> float:
    return float(load_state().exploration)
