from __future__ import annotations

import json
import os
import time
from dataclasses import asdict, dataclass, field
from typing import Any, Dict, List, Optional


MANDATE_PROFILE_DISTRIBUTION = "distribution"
MANDATE_PROFILE_BALANCED = "balanced"
MANDATE_PROFILE_CASHFLOW = "cashflow"

MANDATE_REVIEW_SENSITIVE_ONLY = "sensitive_only"
MANDATE_REVIEW_ALL_CANDIDATES = "all_candidates"

MANDATE_TRADE_DISABLED = "disabled"
MANDATE_TRADE_BUDGETED = "budgeted"


def _data_dir(data_dir: Optional[str] = None) -> str:
    return data_dir or os.path.join(os.path.expanduser("~"), ".oasyce")


def _path(data_dir: Optional[str] = None) -> str:
    return os.path.join(_data_dir(data_dir), "mandate.json")


def _now() -> int:
    return int(time.time())


@dataclass(frozen=True)
class Mandate:
    version: str = "1"
    profile: str = MANDATE_PROFILE_BALANCED
    objective: str = "grow a durable, versioned portfolio under safe defaults"
    review_mode: str = MANDATE_REVIEW_SENSITIVE_ONLY
    trade_mode: str = MANDATE_TRADE_DISABLED
    trade_tags: List[str] = field(default_factory=list)
    trade_max_spend: float = 10.0
    trust_level: int = 1
    auto_threshold: float = 10.0
    updated_at: int = field(default_factory=_now)

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


_PROFILE_DEFAULTS: Dict[str, Dict[str, Any]] = {
    MANDATE_PROFILE_DISTRIBUTION: {
        "objective": "maximize discovery, reach, and distribution for the portfolio",
        "review_mode": MANDATE_REVIEW_SENSITIVE_ONLY,
        "trade_mode": MANDATE_TRADE_DISABLED,
        "trade_tags": [],
        "trade_max_spend": 5.0,
        "trust_level": 1,
        "auto_threshold": 5.0,
    },
    MANDATE_PROFILE_BALANCED: {
        "objective": "grow a durable, versioned portfolio under safe defaults",
        "review_mode": MANDATE_REVIEW_SENSITIVE_ONLY,
        "trade_mode": MANDATE_TRADE_DISABLED,
        "trade_tags": [],
        "trade_max_spend": 10.0,
        "trust_level": 1,
        "auto_threshold": 10.0,
    },
    MANDATE_PROFILE_CASHFLOW: {
        "objective": "pursue monetizable opportunities with explicit budget discipline",
        "review_mode": MANDATE_REVIEW_SENSITIVE_ONLY,
        "trade_mode": MANDATE_TRADE_BUDGETED,
        "trade_tags": ["data", "model"],
        "trade_max_spend": 20.0,
        "trust_level": 1,
        "auto_threshold": 10.0,
    },
}


def _normalize_profile(profile: str) -> str:
    candidate = str(profile or "").strip().lower()
    if candidate not in _PROFILE_DEFAULTS:
        raise ValueError(f"Unsupported mandate profile: {profile}")
    return candidate


def _build_default_mandate(profile: str = MANDATE_PROFILE_BALANCED) -> Mandate:
    resolved = _normalize_profile(profile)
    return Mandate(profile=resolved, **_PROFILE_DEFAULTS[resolved])


def list_mandate_profiles() -> List[Dict[str, Any]]:
    profiles: List[Dict[str, Any]] = []
    for profile in (
        MANDATE_PROFILE_DISTRIBUTION,
        MANDATE_PROFILE_BALANCED,
        MANDATE_PROFILE_CASHFLOW,
    ):
        item = _build_default_mandate(profile).to_dict()
        profiles.append(
            {
                "profile": profile,
                "objective": item["objective"],
                "review_mode": item["review_mode"],
                "trade_mode": item["trade_mode"],
                "trade_tags": list(item["trade_tags"]),
                "trade_max_spend": item["trade_max_spend"],
                "trust_level": item["trust_level"],
                "auto_threshold": item["auto_threshold"],
            }
        )
    return profiles


def load_mandate(data_dir: Optional[str] = None) -> Mandate:
    target = _path(data_dir)
    if not os.path.exists(target):
        return _build_default_mandate()
    try:
        with open(target, encoding="utf-8") as handle:
            payload = json.load(handle)
    except (OSError, json.JSONDecodeError):
        return _build_default_mandate()
    profile = str(payload.get("profile") or MANDATE_PROFILE_BALANCED).strip().lower()
    defaults = _build_default_mandate(profile).to_dict()
    defaults.update(
        {
            "version": str(payload.get("version") or defaults["version"]),
            "profile": profile,
            "objective": str(payload.get("objective") or defaults["objective"]),
            "review_mode": str(payload.get("review_mode") or defaults["review_mode"]),
            "trade_mode": str(payload.get("trade_mode") or defaults["trade_mode"]),
            "trade_tags": [
                str(tag).strip()
                for tag in (payload.get("trade_tags") or defaults["trade_tags"])
                if str(tag).strip()
            ],
            "trade_max_spend": float(
                payload.get("trade_max_spend")
                if payload.get("trade_max_spend") is not None
                else defaults["trade_max_spend"]
            ),
            "trust_level": int(
                payload.get("trust_level")
                if payload.get("trust_level") is not None
                else defaults["trust_level"]
            ),
            "auto_threshold": float(
                payload.get("auto_threshold")
                if payload.get("auto_threshold") is not None
                else defaults["auto_threshold"]
            ),
            "updated_at": int(payload.get("updated_at") or defaults["updated_at"]),
        }
    )
    return Mandate(**defaults)


def save_mandate(mandate: Mandate, data_dir: Optional[str] = None) -> Mandate:
    root = _data_dir(data_dir)
    os.makedirs(root, exist_ok=True)
    with open(_path(data_dir), "w", encoding="utf-8") as handle:
        json.dump(mandate.to_dict(), handle, indent=2, ensure_ascii=True)
    return mandate


def set_mandate_profile(profile: str, data_dir: Optional[str] = None) -> Mandate:
    mandate = _build_default_mandate(profile)
    return save_mandate(mandate, data_dir=data_dir)


def mandate_payload(data_dir: Optional[str] = None) -> Dict[str, Any]:
    mandate = load_mandate(data_dir)
    return {
        "current": mandate.to_dict(),
        "available_profiles": list_mandate_profiles(),
    }


def resolve_mandate_runtime_defaults(
    *,
    data_dir: Optional[str] = None,
    auto_trade: Optional[bool] = None,
    trade_tags: Optional[List[str]] = None,
    trade_max_spend: Optional[float] = None,
    trust_level: Optional[int] = None,
    auto_threshold: Optional[float] = None,
) -> Dict[str, Any]:
    mandate = load_mandate(data_dir)
    resolved_trade_mode = mandate.trade_mode
    resolved_auto_trade = (
        auto_trade if auto_trade is not None else resolved_trade_mode == MANDATE_TRADE_BUDGETED
    )
    resolved_trade_tags = (
        [str(tag).strip() for tag in trade_tags if str(tag).strip()]
        if trade_tags is not None
        else list(mandate.trade_tags)
    )
    resolved_trade_max_spend = (
        float(trade_max_spend) if trade_max_spend is not None else float(mandate.trade_max_spend)
    )
    resolved_trust_level = int(trust_level) if trust_level is not None else int(mandate.trust_level)
    resolved_auto_threshold = (
        float(auto_threshold) if auto_threshold is not None else float(mandate.auto_threshold)
    )
    return {
        "mandate": mandate.to_dict(),
        "auto_trade": resolved_auto_trade,
        "trade_tags": resolved_trade_tags,
        "trade_max_spend": resolved_trade_max_spend,
        "trust_level": resolved_trust_level,
        "auto_threshold": resolved_auto_threshold,
    }


def apply_mandate_to_runtime(
    *,
    data_dir: Optional[str] = None,
    scheduler: Any = None,
    inbox: Any = None,
) -> Dict[str, Any]:
    defaults = resolve_mandate_runtime_defaults(data_dir=data_dir)
    changed = False
    if scheduler is not None:
        config = scheduler.get_config()
        if config.auto_trade != defaults["auto_trade"]:
            config.auto_trade = defaults["auto_trade"]
            changed = True
        if list(config.trade_tags) != list(defaults["trade_tags"]):
            config.trade_tags = list(defaults["trade_tags"])
            changed = True
        if float(config.trade_max_spend) != float(defaults["trade_max_spend"]):
            config.trade_max_spend = float(defaults["trade_max_spend"])
            changed = True
        if changed:
            scheduler.update_config(config)
    if inbox is not None:
        if int(inbox.get_trust_level()) != int(defaults["trust_level"]):
            inbox.set_trust_level(int(defaults["trust_level"]))
            changed = True
        if float(inbox.get_auto_threshold()) != float(defaults["auto_threshold"]):
            inbox.set_auto_threshold(float(defaults["auto_threshold"]))
            changed = True
    return {
        "ok": True,
        "changed": changed,
        **defaults,
    }


def requires_manual_review_for_candidate(*, data_dir: Optional[str] = None) -> Optional[str]:
    mandate = load_mandate(data_dir)
    if mandate.review_mode == MANDATE_REVIEW_ALL_CANDIDATES:
        return "mandate_requires_review"
    return None
