from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from fastapi import FastAPI, Header, HTTPException
from fastapi.responses import FileResponse, JSONResponse
from pydantic import BaseModel, Field

from connectors.ai_provider import (
    analyze_text, configure_provider, provider_catalog, routing_settings,
    save_routing_settings, test_provider,
)
from core_db import SOURCE_ROOT, connect
from core_schema import ensure_internal_token, init_core_schema
from knowledge import analyze_snapshot, get_knowledge, list_knowledge, product_identification, summary as knowledge_summary
from runtime import start_sync, sync_state
from secret_store import configured as secret_configured, save_secret
from source_store import delete_snapshot, get_snapshot, init_source_schema, list_snapshots, source_status

app=FastAPI(title="ORIGUS Core",version="0.3.0",docs_url=None,redoc_url=None,openapi_url=None)

class TechCredentials(BaseModel):
    username:str
    password:str
    base_url:str="http://nps3.cntd.ru/docs"

class SyncPayload(BaseModel):
    document_slug:str|None=None

class AIProviderPayload(BaseModel):
    provider:str
    api_key:str
    model:str|None=None

class AISettingsPayload(BaseModel):
    primary_provider:str="groq"
    fallback_enabled:bool=False
    fallback_order:list[str]=Field(default_factory=lambda:["gemini","openrouter"])

class AIAnalyzePayload(BaseModel):
    purpose:str
    text:str
    instructions:str|None=None
    model:str|None=None
    provider:str|None=None

class ProductIdentifyPayload(BaseModel):
    name:str=""
    purpose:str=""
    description:str=""
    characteristics:dict[str,Any]=Field(default_factory=dict)
    model:str|None=None

@app.on_event("startup")
def startup()->None:
    init_core_schema(); init_source_schema()

def _check_token(value:str)->None:
    if not value or value!=ensure_internal_token():
        raise HTTPException(status_code=403,detail="Некорректный ORIGUS Core token.")

@app.get("/health")
def health(): return {"status":"ok","service":"origus-core","version":"0.3.0"}

@app.get("/api/status")
def status(x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    return JSONResponse({
        "ok":True,"version":"0.3.0","techexpert":source_status(),
        "ai":{"settings":routing_settings(),"providers":provider_catalog()},
        "runtime":sync_state(),"knowledge":knowledge_summary(),
    })

@app.post("/api/connectors/techexpert/credentials")
def save_techexpert_credentials(payload:TechCredentials,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    username=payload.username.strip()
    if not username or not payload.password: raise HTTPException(status_code=400,detail="Логин и пароль обязательны.")
    base=payload.base_url.strip() or "http://nps3.cntd.ru/docs"
    save_secret("techexpert",{"username":username,"password":payload.password,"base_url":base})
    with connect() as con:
        con.execute("UPDATE connectors SET status='configured',base_url=?,last_error=NULL WHERE connector_key='techexpert'",(base,)); con.commit()
    return {"ok":True}

@app.post("/api/connectors/techexpert/test")
def test_techexpert(x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    if not secret_configured("techexpert"): raise HTTPException(status_code=400,detail="Сначала настройте Техэксперт.")
    import subprocess,sys
    base=Path(__file__).resolve().parent
    result=subprocess.run([sys.executable,"-m","connectors.techexpert","test-login"],cwd=str(base),text=True,capture_output=True,timeout=150)
    output=((result.stdout or "")+"\n"+(result.stderr or "")).strip()
    if result.returncode!=0: raise HTTPException(status_code=400,detail=output[-3000:] or "Авторизация не прошла.")
    return {"ok":True,"message":output[-3000:] or "Авторизация: OK"}

@app.post("/api/connectors/techexpert/sync")
def sync_techexpert(payload:SyncPayload,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    if not secret_configured("techexpert"): raise HTTPException(status_code=400,detail="Сначала настройте Техэксперт.")
    try: pid=start_sync((payload.document_slug or "").strip() or None)
    except RuntimeError as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc
    return {"ok":True,"pid":pid}

@app.get("/api/ai/providers")
def ai_providers(x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token); return {"ok":True,"settings":routing_settings(),"providers":provider_catalog()}

@app.post("/api/ai/providers/configure")
def ai_provider_configure(payload:AIProviderPayload,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    try: return configure_provider(payload.provider,payload.api_key,payload.model)
    except Exception as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc

@app.post("/api/ai/providers/{provider}/test")
def ai_provider_test(provider:str,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    try: return test_provider(provider)
    except Exception as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc

@app.post("/api/ai/settings")
def ai_settings(payload:AISettingsPayload,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    try: return {"ok":True,"settings":save_routing_settings(payload.primary_provider,payload.fallback_enabled,payload.fallback_order)}
    except Exception as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc

@app.post("/api/ai/analyze")
def ai_analyze(payload:AIAnalyzePayload,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    try:
        return analyze_text(purpose=payload.purpose.strip() or "general",text=payload.text,instructions=payload.instructions,model=payload.model,provider=payload.provider)
    except Exception as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc

@app.post("/api/knowledge/snapshots/{snapshot_id}/analyze")
def knowledge_analyze_snapshot(snapshot_id:int,force:bool=False,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    try: return analyze_snapshot(snapshot_id,force=force)
    except Exception as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc

@app.get("/api/knowledge")
def knowledge_list(x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token); return {"ok":True,"summary":knowledge_summary(),"items":list_knowledge()}

@app.get("/api/knowledge/{document_slug}")
def knowledge_document(document_slug:str,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token); item=get_knowledge(document_slug)
    if not item: raise HTTPException(status_code=404,detail="Разбор документа ещё не создан.")
    return {"ok":True,**item}

@app.post("/api/product/identify")
def identify_product_api(payload:ProductIdentifyPayload,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    if not (payload.name.strip() or payload.purpose.strip() or payload.description.strip() or payload.characteristics):
        raise HTTPException(status_code=400,detail="Нет данных о продукции.")
    try:
        return product_identification(name=payload.name.strip(),purpose=payload.purpose.strip(),description=payload.description.strip(),characteristics=payload.characteristics,model=payload.model)
    except Exception as exc: raise HTTPException(status_code=400,detail=str(exc)) from exc

@app.get("/api/snapshots")
def snapshots(document_slug:str|None=None,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token); return {"ok":True,"items":list_snapshots((document_slug or "").strip() or None)}

@app.get("/api/snapshots/{snapshot_id}")
def snapshot_meta(snapshot_id:int,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token); item=get_snapshot(snapshot_id)
    if not item: raise HTTPException(status_code=404,detail="Версия не найдена.")
    return {"ok":True,"item":item}

@app.get("/api/snapshots/{snapshot_id}/download")
def snapshot_download(snapshot_id:int,kind:str="source",x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token); item=get_snapshot(snapshot_id)
    if not item: raise HTTPException(status_code=404,detail="Версия не найдена.")
    field={"text":"text_path","html":"html_path","source":"downloaded_file_path","manifest":"manifest_path"}.get(kind)
    if not field: raise HTTPException(status_code=400,detail="Неизвестный тип файла.")
    raw=item.get(field)
    if not raw: raise HTTPException(status_code=404,detail="Файл отсутствует.")
    path=Path(raw).resolve()
    try: path.relative_to(SOURCE_ROOT.resolve())
    except ValueError as exc: raise HTTPException(status_code=403,detail="Некорректный путь.") from exc
    if not path.exists(): raise HTTPException(status_code=404,detail="Файл не найден.")
    return FileResponse(path,filename=path.name,headers={"Cache-Control":"private, no-store"})

@app.post("/api/snapshots/{snapshot_id}/delete")
def snapshot_delete(snapshot_id:int,x_core_token:str=Header(default="",alias="X-ORIGUS-Core-Token")):
    _check_token(x_core_token)
    try: result=delete_snapshot(snapshot_id)
    except RuntimeError as exc: raise HTTPException(status_code=404,detail=str(exc)) from exc
    return {"ok":True,"result":result}
