from __future__ import annotations

import os
import time
from pathlib import Path
from typing import Any

import httpx

ROOT = Path(__file__).resolve().parents[2]

# Publishable values are intentionally safe to ship in desktop/mobile clients.
# Secret/service-role keys are NEVER stored in this build; privileged DB access
# lives inside the authenticated Supabase Edge Function.
SUPABASE_URL = os.getenv("MELUMIYACHAT_SUPABASE_URL", "https://dzorhvanzzscfpnczilh.supabase.co").rstrip("/")
SUPABASE_PUBLISHABLE_KEY = os.getenv("MELUMIYACHAT_SUPABASE_PUBLISHABLE_KEY", "sb_publishable_P-VON1o3_2xG30NucWD6Jg_xQ1gF4IE")
EDGE_FUNCTION = os.getenv("MELUMIYACHAT_EDGE_FUNCTION", "melumiyachat-core")
WRITE_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_WRITE_EDGE_FUNCTION", "melumiyachat-write")
IMPORT_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_IMPORT_EDGE_FUNCTION", "melumiyachat-import")
BILLING_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_BILLING_EDGE_FUNCTION", "melumiyachat-billing")
CHAT_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_CHAT_EDGE_FUNCTION", "melumiyachat-chat")
IMAGE_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_IMAGE_EDGE_FUNCTION", "melumiyachat-images")
START_RP_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_START_RP_EDGE_FUNCTION", "melumiyachat-start-rp")
DIRECTOR_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_DIRECTOR_EDGE_FUNCTION", "melumiyachat-director")
WEB_SEARCH_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_WEB_SEARCH_EDGE_FUNCTION", "melumiyachat-web-search")
CHARACTER_AUTOFILL_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_CHARACTER_AUTOFILL_EDGE_FUNCTION", "melumiyachat-character-autofill")
SCENARIO_AUTOFILL_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_SCENARIO_AUTOFILL_EDGE_FUNCTION", "melumiyachat-scenario-autofill")
CONTINUITY_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_CONTINUITY_EDGE_FUNCTION", "melumiyachat-continuity")
MESSAGE_TOOLS_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_MESSAGE_TOOLS_EDGE_FUNCTION", "melumiyachat-message-tools")
SUPPORT_EDGE_FUNCTION = os.getenv("MELUMIYACHAT_SUPPORT_EDGE_FUNCTION", "melumiyachat-support")
CLOUD_ENABLED = os.getenv("MELUMIYACHAT_CLOUD_ENABLED", "1").lower() not in {"0", "false", "no"}

_ME_CACHE: dict[str, tuple[float, dict[str, Any]]] = {}
_CACHE_SECONDS = 45.0


def enabled() -> bool:
    return bool(CLOUD_ENABLED and SUPABASE_URL and SUPABASE_PUBLISHABLE_KEY)


def _headers(token: str | None = None) -> dict[str, str]:
    h = {"apikey": SUPABASE_PUBLISHABLE_KEY, "Content-Type": "application/json"}
    if token:
        h["Authorization"] = f"Bearer {token}"
    return h


def _raise(resp: httpx.Response) -> None:
    if resp.is_success:
        return
    try:
        data = resp.json()
    except Exception:
        data = {"message": resp.text}
    msg = data.get("error_description") or data.get("msg") or data.get("message") or data.get("error") or resp.text or f"HTTP {resp.status_code}"
    raise RuntimeError(str(msg))


def signup(email: str, password: str, display_name: str = "") -> dict[str, Any]:
    with httpx.Client(timeout=20) as client:
        r = client.post(
            f"{SUPABASE_URL}/auth/v1/signup",
            headers=_headers(),
            json={"email": email, "password": password, "data": {"display_name": display_name or email.split("@")[0]}},
        )
        _raise(r)
        return r.json()


def login(email: str, password: str) -> dict[str, Any]:
    with httpx.Client(timeout=20) as client:
        r = client.post(
            f"{SUPABASE_URL}/auth/v1/token?grant_type=password",
            headers=_headers(),
            json={"email": email, "password": password},
        )
        _raise(r)
        return r.json()


def refresh(refresh_token: str) -> dict[str, Any]:
    with httpx.Client(timeout=20) as client:
        r = client.post(
            f"{SUPABASE_URL}/auth/v1/token?grant_type=refresh_token",
            headers=_headers(),
            json={"refresh_token": refresh_token},
        )
        _raise(r)
        return r.json()


def logout(token: str) -> dict[str, Any]:
    with httpx.Client(timeout=20) as client:
        r = client.post(f"{SUPABASE_URL}/auth/v1/logout", headers=_headers(token))
        if r.status_code not in (200, 204):
            _raise(r)
    _ME_CACHE.pop(token, None)
    return {"ok": True}


def action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=30) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data



def write_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=30) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{WRITE_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data



def message_tools_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=45) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{MESSAGE_TOOLS_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data

def creator_image_action(token: str, action_name: str = "upload", **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=60) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/melumiyachat-creator-images",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data



def import_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=90) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{IMPORT_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data






def public_action(token: str, action_name: str, **payload: Any) -> Any:
    with httpx.Client(timeout=30) as client:
        r=client.post(f"{SUPABASE_URL}/functions/v1/melumiyachat-public",headers=_headers(token),json={"action":action_name,**payload})
        _raise(r)
        data=r.json()
        if isinstance(data,dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def me(token: str, *, fresh: bool = False) -> dict[str, Any]:
    now = time.monotonic()
    if not fresh:
        cached = _ME_CACHE.get(token)
        if cached and now - cached[0] < _CACHE_SECONDS:
            return cached[1]
    data = action(token, "me")
    if not isinstance(data, dict):
        raise RuntimeError("Invalid account response")
    _ME_CACHE[token] = (now, data)
    return data


def bootstrap_account(token: str) -> dict[str, Any]:
    data = action(token, "bootstrap_account")
    _ME_CACHE[token] = (time.monotonic(), data)
    return data




def billing_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=45) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{BILLING_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def support_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=45) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{SUPPORT_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data








def notification_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=45) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/melumiyachat-notifications",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def reward_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=45) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/melumiyachat-rewards",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def model_access_action(token: str, action_name: str, **payload: Any) -> Any:
    if not token: raise RuntimeError("Authentication required")
    with httpx.Client(timeout=60) as client:
        r=client.post(f"{SUPABASE_URL}/functions/v1/melumiyachat-model-access",headers=_headers(token),json={"action":action_name,**payload});_raise(r);data=r.json()
        if isinstance(data,dict) and data.get("error"): raise RuntimeError(str(data["error"]))
        return data



def chat_action(token: str, **payload: Any) -> Any:
    """Production customer chat. Provider credentials stay inside Supabase Edge Functions."""
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=180) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{CHAT_EDGE_FUNCTION}",
            headers=_headers(token),
            json=payload,
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def image_action(token: str, action_name: str = "generate", **payload: Any) -> Any:
    """Generate customer artwork through the authenticated cloud image service."""
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=180) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{IMAGE_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"action": action_name, **payload},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def start_rp_action(token: str, **payload: Any) -> Any:
    """Start a published Scenario through the canonical server-side seeding path."""
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=90) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{START_RP_EDGE_FUNCTION}",
            headers=_headers(token),
            json=payload,
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def continuity_action(token: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=90) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{CONTINUITY_EDGE_FUNCTION}",
            headers=_headers(token),
            json=payload,
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def director_action(token: str, **payload: Any) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=180) as client:
        r = client.post(f"{SUPABASE_URL}/functions/v1/{DIRECTOR_EDGE_FUNCTION}", headers=_headers(token), json=payload)
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def web_search_action(token: str, query: str) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=30) as client:
        r = client.post(f"{SUPABASE_URL}/functions/v1/{WEB_SEARCH_EDGE_FUNCTION}", headers=_headers(token), json={"q": query})
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data

def character_autofill_action(token: str, name: str, source: str = "", existing: dict[str, Any] | None = None, action_name: str = "autofill", selected_source_url: str = "") -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=45) as client:
        headers = _headers(token)
        headers["Cache-Control"] = "no-store"
        headers["X-Melumiya-Autofill-Client"] = "windows-v23-two-phase"
        payload = {"action": action_name or "autofill", "name": name, "source": source, "existing": existing or {}, "client_autofill_version": "windows-v23-two-phase"}
        if selected_source_url:
            payload["selected_source_url"] = selected_source_url
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{CHARACTER_AUTOFILL_EDGE_FUNCTION}?fresh={time.time_ns()}",
            headers=headers,
            json=payload,
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data


def scenario_autofill_action(token: str, title: str, source: str = "", main_character: str = "", existing: dict[str, Any] | None = None) -> Any:
    if not token:
        raise RuntimeError("Authentication required")
    with httpx.Client(timeout=60) as client:
        r = client.post(
            f"{SUPABASE_URL}/functions/v1/{SCENARIO_AUTOFILL_EDGE_FUNCTION}",
            headers=_headers(token),
            json={"title": title, "source": source, "main_character": main_character, "existing": existing or {}},
        )
        _raise(r)
        data = r.json()
        if isinstance(data, dict) and data.get("error"):
            raise RuntimeError(str(data["error"]))
        return data
