from __future__ import annotations

from datetime import datetime, timedelta, timezone
from decimal import Decimal
import json
from pathlib import Path

from src.approval.finance_manager_approval import (
    build_finance_approval_status,
    evaluate_finance_approval,
    evaluate_finance_approval_for_strategy_coin,
    load_finance_approvals,
)
from src.execution.order_intent import OrderIntent
from src.tools.tiny_autonomous_live_runtime import _evaluate_finance_approval_for_intent


def _write_approval(path: Path, *, intent_id: str, decision: str, created_at: str | None = None, strategy_id: str | None = None, coin: str | None = None) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    row = {
        "approval_id": f"approval-{decision}",
        "intent_id": intent_id,
        "decision": decision,
        "note": "test",
        "created_at": created_at or datetime.now(timezone.utc).isoformat(),
        "strategy_id": strategy_id,
        "coin": coin,
        "source_engine": "CryptoTrader" if strategy_id and coin else None,
        "execution_allowed": False,
        "boundary": "FinanceManager approval audit only; CryptoTrader must re-check all gates before any execution.",
    }
    with path.open("a", encoding="utf-8") as handle:
        handle.write(json.dumps(row, sort_keys=True) + "\n")


def _intent() -> OrderIntent:
    return OrderIntent(
        strategy_id="candidate_v76_strict_live_candidate",
        symbol="BTC/USDC:USDC",
        coin="BTC",
        side="buy",
        reduce_only=False,
        order_type="market",
        tif="Ioc",
        size=Decimal("0.001"),
        price=None,
        trigger_price=None,
        stop_loss=Decimal("59000"),
        take_profit=Decimal("61000"),
        client_order_id="0xabc",
        reason="test",
        risk_usd=Decimal("0.50"),
        estimated_notional_usd=Decimal("50"),
    )


def test_finance_approval_reader_loads_latest_decision_and_never_executes(tmp_path: Path) -> None:
    log = tmp_path / "trade_approvals.jsonl"
    _write_approval(log, intent_id="intent-1", decision="paper_only", created_at="2026-06-30T10:00:00+00:00")
    _write_approval(log, intent_id="intent-1", decision="approve", created_at="2026-06-30T10:05:00+00:00", strategy_id="candidate_v76_strict_live_candidate", coin="BTC")

    approvals = load_finance_approvals(log)
    decision = evaluate_finance_approval("intent-1", approvals=approvals)
    strategy_coin = evaluate_finance_approval_for_strategy_coin("candidate_v76_strict_live_candidate", "BTC", approvals=approvals)
    status = build_finance_approval_status(log)

    assert decision.allowed is True
    assert strategy_coin.allowed is True
    assert strategy_coin.intent_id == "intent-1"
    assert approvals[-1].strategy_id == "candidate_v76_strict_live_candidate"
    assert approvals[-1].coin == "BTC"
    assert decision.status == "approved_pending_crypto_trader_gates"
    assert decision.mainnet_signed_action is False
    assert "crypto_trader_must_recheck_all_gates" in decision.reasons
    assert status["latest_decisions"]["approve"] == 1
    assert status["execution_side_effects"] is False


def test_finance_approval_gate_blocks_reject_paper_only_missing_and_expired(tmp_path: Path) -> None:
    log = tmp_path / "trade_approvals.jsonl"
    _write_approval(log, intent_id="intent-paper", decision="paper_only")
    _write_approval(log, intent_id="intent-reject", decision="reject")
    _write_approval(log, intent_id="intent-old", decision="approve", created_at=(datetime.now(timezone.utc) - timedelta(days=2)).isoformat())
    approvals = load_finance_approvals(log)

    assert evaluate_finance_approval(None, approvals=approvals).reasons == ("finance_approval_intent_id_missing",)
    assert evaluate_finance_approval("missing", approvals=approvals).reasons == ("finance_approval_missing",)
    assert evaluate_finance_approval("intent-paper", approvals=approvals).reasons == ("finance_approval_paper_only",)
    assert evaluate_finance_approval("intent-reject", approvals=approvals).reasons == ("finance_approval_rejected",)
    assert evaluate_finance_approval("intent-old", approvals=approvals, max_age_seconds=60).reasons == ("finance_approval_expired",)


def test_tiny_live_runtime_requires_finance_approval_id_for_live_but_not_shadow(monkeypatch, tmp_path: Path) -> None:
    log = tmp_path / "trade_approvals.jsonl"
    monkeypatch.setenv("CTB_FINANCE_APPROVAL_LOG", str(log))
    monkeypatch.delenv("CTB_FINANCE_TRADE_INTENT_ID", raising=False)
    monkeypatch.delenv("CTB_FINANCE_INTENT_MAP_PATH", raising=False)

    live_gate = _evaluate_finance_approval_for_intent(_intent(), mode="live")
    shadow_gate = _evaluate_finance_approval_for_intent(_intent(), mode="shadow")

    assert live_gate["allowed"] is False
    assert live_gate["reasons"] == ("finance_approval_strategy_coin_missing",)
    assert live_gate["mainnet_signed_action"] is False
    assert shadow_gate["allowed"] is True
    assert shadow_gate["mainnet_signed_action"] is False

    _write_approval(log, intent_id="intent-from-dashboard", decision="approve", strategy_id="candidate_v76_strict_live_candidate", coin="BTC")
    live_gate_after_metadata = _evaluate_finance_approval_for_intent(_intent(), mode="live")
    assert live_gate_after_metadata["allowed"] is True
    assert live_gate_after_metadata["intent_id"] == "intent-from-dashboard"
    assert live_gate_after_metadata["mainnet_signed_action"] is False
