import os
from datetime import datetime
from typing import Optional
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse
from pydantic import BaseModel

from .database import get_key, save_key, update_key, list_keys, init_db
from .utils import gen_key, hash_hwid, is_admin, free_expiry, is_expired

app = FastAPI(docs_url=None, redoc_url=None)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])

@app.on_event("startup")
def startup(): init_db()

class GetkeyReq(BaseModel):
    hwid: str
class VerifyReq(BaseModel):
    key: str; hwid: str
class ActivateReq(BaseModel):
    key: str; hwid: str
class MarkUsedReq(BaseModel):
    key: str; hwid: str
class AdminGenReq(BaseModel):
    token: str; tier: str = "paid"; note: Optional[str] = None
class AdminRevokeReq(BaseModel):
    token: str; key: str
class AdminActivateReq(BaseModel):
    token: str; hwid: str; key: str
class AdminResetReq(BaseModel):
    token: str; key: str

@app.post("/api/getkey")
async def getkey(req: GetkeyReq):
    hashed = hash_hwid(req.hwid)
    for k in list_keys():
        if (k.get("hwid")==hashed and k.get("tier")=="free"
                and k.get("is_active") and not k.get("is_used")
                and not is_expired(k.get("expires_at"))):
            return {"ok":True,"key":k["key_string"],"tier":"free","message":"Already have active key."}
    key_str = gen_key("free"); expires = free_expiry()
    data = {"key_string":key_str,"hwid":hashed,"tier":"free","is_active":True,
            "is_used":False,"created_at":datetime.utcnow().isoformat(),
            "expires_at":expires.isoformat(),"used_count":0,"note":""}
    save_key(data)
    return {"ok":True,"key":key_str,"tier":"free","expires_at":expires.isoformat()}

@app.post("/api/activate")
async def activate(req: ActivateReq):
    hashed = hash_hwid(req.hwid); k = get_key(req.key)
    if not k: raise HTTPException(403,"Invalid key.")
    if not k.get("is_active"): raise HTTPException(403,"Key revoked.")
    if is_expired(k.get("expires_at")): raise HTTPException(403,"Key expired.")
    if k.get("is_used") and k.get("tier")=="free": raise HTTPException(403,"Free key already used.")
    if k.get("hwid") and k["hwid"]!=hashed: raise HTTPException(403,"Key bound to a different machine.")
    if not k.get("hwid"): update_key(req.key,{"hwid":hashed})
    return {"ok":True,"tier":k["tier"],"expires_at":k.get("expires_at")}

@app.post("/api/verify")
async def verify(req: VerifyReq):
    hashed = hash_hwid(req.hwid); k = get_key(req.key)
    if not k: raise HTTPException(403,"Invalid key.")
    if not k.get("is_active"): raise HTTPException(403,"Key revoked.")
    if is_expired(k.get("expires_at")): raise HTTPException(403,"Key expired.")
    if k.get("is_used") and k.get("tier")=="free": raise HTTPException(403,"Free key already used.")
    if k.get("hwid") and k["hwid"]!=hashed: raise HTTPException(403,"Key bound to a different machine.")
    update_key(req.key,{"used_count":k.get("used_count",0)+1})
    features={"profiles":["fast","balanced"] if k["tier"]=="free" else ["fast","balanced","max","stealth","vm","vm_max"],
              "native":k["tier"]=="paid","nested_vm":k["tier"]=="paid","poly_vm":True,"one_shot":k["tier"]=="free"}
    return {"ok":True,"tier":k["tier"],"features":features,"expires_at":k.get("expires_at")}

@app.post("/api/mark_used")
async def mark_used(req: MarkUsedReq):
    k = get_key(req.key)
    if k and k.get("tier")=="free": update_key(req.key,{"is_used":True})
    return {"ok":True}

@app.get("/api/status")
async def status(): return {"status":"online","version":"1.0.0"}

@app.post("/api/admin/genkey")
async def admin_genkey(req: AdminGenReq):
    if not is_admin(req.token): raise HTTPException(401,"Unauthorized.")
    tier = req.tier if req.tier in ("free","paid") else "paid"
    key_str = gen_key(tier); expires = free_expiry() if tier=="free" else None
    data={"key_string":key_str,"hwid":None,"tier":tier,"is_active":True,"is_used":False,
          "created_at":datetime.utcnow().isoformat(),"expires_at":expires.isoformat() if expires else None,
          "used_count":0,"note":req.note or ""}
    save_key(data)
    return {"ok":True,"key":key_str,"tier":tier,"expires_at":data["expires_at"] or "lifetime"}

@app.post("/api/admin/activate")
async def admin_activate(req: AdminActivateReq):
    if not is_admin(req.token): raise HTTPException(401,"Unauthorized.")
    k = get_key(req.key)
    if not k: raise HTTPException(404,"Key not found.")
    update_key(req.key,{"hwid":req.hwid,"is_active":True})
    return {"ok":True,"message":f"Key {req.key} bound to HWID."}

@app.post("/api/admin/reset_hwid")
async def admin_reset(req: AdminResetReq):
    if not is_admin(req.token): raise HTTPException(401,"Unauthorized.")
    k = get_key(req.key)
    if not k: raise HTTPException(404,"Key not found.")
    update_key(req.key,{"hwid":None})
    return {"ok":True,"message":"HWID reset."}

@app.post("/api/admin/revoke")
async def admin_revoke(req: AdminRevokeReq):
    if not is_admin(req.token): raise HTTPException(401,"Unauthorized.")
    k = get_key(req.key)
    if not k: raise HTTPException(404,"Key not found.")
    update_key(req.key,{"is_active":False})
    return {"ok":True,"message":f"Key {req.key} revoked."}

@app.get("/api/admin/keys")
async def admin_keys(token: str):
    if not is_admin(token): raise HTTPException(401,"Unauthorized.")
    keys = list_keys()
    return {"ok":True,"keys":[{"key":k["key_string"],"tier":k["tier"],
            "hwid":(k["hwid"][:12]+"..." if k.get("hwid") else None),
            "hwid_full":k.get("hwid"),"active":k.get("is_active"),
            "used":k.get("is_used"),"uses":k.get("used_count",0),
            "note":k.get("note",""),"expires":k.get("expires_at") or "lifetime",
            "created":k.get("created_at")} for k in keys]}

@app.get("/api/admin/stats")
async def admin_stats(token: str):
    if not is_admin(token): raise HTTPException(401,"Unauthorized.")
    all_keys = list_keys()
    return {"ok":True,"total_keys":len(all_keys),
            "active_keys":sum(1 for k in all_keys if k.get("is_active")),
            "free_keys":sum(1 for k in all_keys if k.get("tier")=="free" and k.get("is_active")),
            "paid_keys":sum(1 for k in all_keys if k.get("tier")=="paid" and k.get("is_active")),
            "total_uses":sum(k.get("used_count",0) for k in all_keys)}

_T = lambda f: os.path.join(os.path.dirname(__file__),"..","web","templates",f)
@app.get("/", response_class=HTMLResponse)
async def pg_index(): return FileResponse(_T("index.html"))
@app.get("/getkey", response_class=HTMLResponse)
async def pg_getkey(): return FileResponse(_T("getkey.html"))
@app.get("/admin", response_class=HTMLResponse)
async def pg_admin(): return FileResponse(_T("admin.html"))
