from __future__ import annotations

import json
import os
import time
import uuid
from typing import Any, Dict, List

_STATE_FILE = os.environ.get("KYBERNIS_DEMO_PAYMENTS_STATE", ".payments_demo_state.json")


class PaymentProviderTimeout(RuntimeError):
    pass


def _truthy_env(name: str, default: bool = False) -> bool:
    raw = os.getenv(name, "").strip().lower()
    if not raw:
        return default
    return raw in {"1", "true", "yes", "on"}


def _default_state() -> Dict[str, Any]:
    return {
        "fail_once_keys": {},
        "pricing_flags_by_payment": {
            "pay_87422": {"restockEligible": True, "revision": 1}
        },
        "payments": {
            # Demo customer has a duplicate charge yesterday
            "cust_4829": [
                {"payment_id": "pay_87421", "amount_cents": 12000, "status": "succeeded", "created_day": "yesterday"},
                {"payment_id": "pay_87422", "amount_cents": 12000, "status": "succeeded", "created_day": "yesterday"},
            ]
        },
    }


def _load_state() -> Dict[str, Any]:
    if not os.path.exists(_STATE_FILE):
        return _default_state()
    with open(_STATE_FILE, "r", encoding="utf-8") as f:
        loaded = json.load(f)
    if not isinstance(loaded, dict):
        return _default_state()
    loaded.setdefault("fail_once_keys", {})
    loaded.setdefault("payments", {})
    loaded.setdefault("pricing_flags_by_payment", {})
    return loaded


def _save_state(state: Dict[str, Any]) -> None:
    with open(_STATE_FILE, "w", encoding="utf-8") as f:
        json.dump(state, f)


def lookup_recent_payments(*, customer_id: str, days: int = 3) -> List[Dict[str, Any]]:
    """
    Look up recent payments for an authenticated customer.

    Args:
      customer_id: internal customer identifier from auth/session context.
      days: how far back to look (demo ignores precision).

    Returns:
      List of payments: {payment_id, amount_cents, status, created_day}
    """
    time.sleep(0.15)
    state = _load_state()
    return list(state["payments"].get(customer_id, []))


def _fail_once_key(customer_id: str, payment_id: str, amount_cents: int, reason: str) -> str:
    return f"{customer_id}|{payment_id}|{amount_cents}|{reason}"


def _get_pricing_flags_from_state(state: Dict[str, Any], payment_id: str) -> Dict[str, Any]:
    row = (state.get("pricing_flags_by_payment") or {}).get(payment_id, {})
    if not isinstance(row, dict):
        row = {}
    restock_eligible = bool(row.get("restockEligible", True))
    revision = int(row.get("revision", 1))
    return {"restockEligible": restock_eligible, "revision": revision}


def current_pricing_flags(*, payment_id: str) -> Dict[str, Any]:
    """
    Returns current pricing flags for a payment_id from demo runtime state.
    """
    state = _load_state()
    return _get_pricing_flags_from_state(state, payment_id)


def _flip_pricing_flags_after_success(state: Dict[str, Any], payment_id: str) -> None:
    flags = _get_pricing_flags_from_state(state, payment_id)
    next_row = {
        "restockEligible": False,
        "revision": int(flags.get("revision", 1)) + 1,
    }
    pricing_map = state.setdefault("pricing_flags_by_payment", {})
    pricing_map[payment_id] = next_row


def issue_refund(*, customer_id: str, payment_id: str, amount_cents: int, reason: str) -> Dict[str, Any]:
    """
    Issue a refund for a specific payment.

    Demo behavior:
      - fails once (timeout) for the same (customer_id, payment_id, amount_cents, reason)
      - succeeds on the next call

    This simulates a mid-work transient failure that would normally trigger risky retries.
    """
    time.sleep(0.2)
    state = _load_state()
    pricing_flags = _get_pricing_flags_from_state(state, payment_id)

    k = _fail_once_key(customer_id, payment_id, amount_cents, reason)
    already_failed = bool(state["fail_once_keys"].get(k))
    try:
        # Optional transient provider failure simulation for retry demos.
        if _truthy_env("KYB_DEMO_FAIL_ONCE", default=False) and not already_failed:
            state["fail_once_keys"][k] = True
            _save_state(state)
            raise PaymentProviderTimeout("timeout before provider commit")

        receipt_id = f"rcpt_{uuid.uuid4().hex[:12]}"
        # Simulate late dependency mutation after successful provider execution.
        # First successful refund flips runtime pricing to restockEligible=false.
        _flip_pricing_flags_after_success(state, payment_id)
        _save_state(state)
        return {
            "status": "succeeded",
            "provider": "demo_payments",
            "provider_receipt_id": receipt_id,
            "customer_id": customer_id,
            "payment_id": payment_id,
            "amount_cents": amount_cents,
            "reason": reason,
            "pricing_flags_used": pricing_flags,
        }
    except Exception as e:
        return {
            "status": "error",
            "error_type": type(e).__name__,
            "message": str(e),
            "provider": "demo_payments",
            "pricing_flags_used": pricing_flags,
        }
# def _lazy_issue_refund(*, user_id: str, payment_id: str, amount_cents: int, reason: str):
#     """Lazily import the demo payments provider and invoke it.

#     IMPORTANT: This tool must NEVER raise.
#     It returns a structured {status: "..."} dict so ADK doesn't crash.
#     """
#     payments_tools = importlib.import_module("sdk.python.refund_demo.tools")
#     try:
#         return payments_tools.issue_refund(
#             user_id=user_id,
#             payment_id=payment_id,
#             amount_cents=amount_cents,
#             reason=reason,
#         )
#     except Exception as e:
#         return {
#             "status": "error",
#             "error_type": type(e).__name__,
#             "message": str(e),
#             "provider": "demo_payments",
#         }
