import os, json, time, asyncio, logging
from typing import Dict, List, Optional
from dataclasses import dataclass
from aiohttp import web
import aiohttp

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
log = logging.getLogger("free_gateway")

FREE_MODELS = [
    {"name": "deepseek-v4-flash-free", "provider": "openrouter", "base_url": "https://openrouter.ai/api/v1", "api_key_env": "OPENROUTER_API_KEY"},
    {"name": "mimo-v2.5-free", "provider": "openrouter", "base_url": "https://openrouter.ai/api/v1", "api_key_env": "OPENROUTER_API_KEY"},
    {"name": "hunyuan-v3-free", "provider": "openrouter", "base_url": "https://openrouter.ai/api/v1", "api_key_env": "OPENROUTER_API_KEY"},
    {"name": "glm-4.5-free", "provider": "zhipu", "base_url": "https://open.bigmodel.cn/api/paas/v4", "api_key_env": "ZHIPU_API_KEY"},
    {"name": "qwen2.5-72b-instruct", "provider": "modelscope", "base_url": "https://api-inference.modelscope.cn/v1", "api_key_env": "MODELSCOPE_API_KEY"},
]

@dataclass
class ProviderState:
    healthy: bool = True
    last_error: float = 0
    errors: int = 0

states: Dict[str, ProviderState] = {m["name"]: ProviderState() for m in FREE_MODELS}

def get_api_key(env_name: str) -> Optional[str]:
    return os.environ.get(env_name)

async def try_request(session: aiohttp.ClientSession, model: dict, payload: dict, headers: dict, timeout: int = 30) -> Optional[dict]:
    api_key = get_api_key(model["api_key_env"])
    if not api_key:
        return None
    h = dict(headers)
    h["Authorization"] = f"Bearer {api_key}"
    url = f"{model['base_url']}/chat/completions"
    pl = dict(payload)
    pl["model"] = model["name"]
    try:
        async with session.post(url, json=pl, headers=h, timeout=timeout) as r:
            if r.status == 200:
                return await r.json()
            txt = await r.text()
            log.warning(f"{model['name']} HTTP {r.status}: {txt[:200]}")
    except Exception as e:
        log.warning(f"{model['name']} error: {e}")
    return None

async def handle_chat(request: web.Request) -> web.Response:
    try:
        payload = await request.json()
    except Exception:
        return web.json_response({"error": "invalid json"}, status=400)

    auth = request.headers.get("Authorization", "")
    headers = {"Content-Type": "application/json"}
    if auth:
        headers["Authorization"] = auth

    async with aiohttp.ClientSession() as session:
        for model in FREE_MODELS:
            state = states[model["name"]]
            if not state.healthy and time.time() - state.last_error < 60:
                continue
            log.info(f"Trying {model['name']}...")
            resp = await try_request(session, model, payload, headers)
            if resp:
                state.healthy = True
                state.errors = 0
                log.info(f"Success with {model['name']}")
                return web.json_response(resp)
            state.healthy = False
            state.last_error = time.time()
            state.errors += 1

    return web.json_response({"error": "all providers failed"}, status=502)

async def handle_models(request: web.Request) -> web.Response:
    return web.json_response({"data": [{"id": m["name"], "object": "model"} for m in FREE_MODELS]})

app = web.Application()
app.router.add_post("/v1/chat/completions", handle_chat)
app.router.add_get("/v1/models", handle_models)

if __name__ == "__main__":
    port = int(os.environ.get("PORT", "8800"))
    log.info(f"Starting free gateway on :{port}")
    web.run_app(app, host="0.0.0.0", port=port)
