from __future__ import annotations

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware

from .config import settings
from .extractor import SolidWorksExtractor
from .grader import FeatureGrader
from .knowledge import KnowledgeRepository
from .models import (
    AuditPartRequest,
    ObservationRequest,
    SessionMode,
    StandardKind,
    StartSessionRequest,
    SubmitTestRequest,
)
from .sessions import LearningService, SessionStore
from .skillflow import normalize_steps


app = FastAPI(title=settings.app_name)
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:5173",
        "http://127.0.0.1:5173",
        "http://localhost:5174",
        "http://127.0.0.1:5174",
        "http://localhost:5175",
        "http://127.0.0.1:5175",
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

knowledge = KnowledgeRepository()
store = SessionStore()
grader = FeatureGrader()
learning = LearningService(store, grader)
extractor = SolidWorksExtractor()


@app.get("/health")
async def health() -> dict[str, object]:
    return {
        "ok": True,
        "service": settings.app_name,
        "standards": len(knowledge.list_standards()),
        "software_paths": settings.software_paths(),
    }


@app.post("/api/knowledge/reload")
async def reload_knowledge() -> dict[str, object]:
    knowledge.reload()
    return {"ok": True, "standards": len(knowledge.list_standards())}


@app.get("/api/standards")
async def list_standards(query: str = "", kind: StandardKind | None = None) -> dict[str, object]:
    items = knowledge.list_standards(query=query, kind=kind)
    return {"items": [item.model_dump(mode="json", exclude={"raw"}) for item in items]}


@app.get("/api/standards/{standard_id}")
async def get_standard(standard_id: str) -> dict[str, object]:
    try:
        return knowledge.require(standard_id).model_dump(mode="json")
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc


@app.post("/api/teaching/sessions")
async def start_teaching(request: StartSessionRequest) -> dict[str, object]:
    try:
        standard = knowledge.require(request.standard_id)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    return store.start(SessionMode.teaching, standard).model_dump(mode="json")


@app.post("/api/testing/sessions")
async def start_testing(request: StartSessionRequest) -> dict[str, object]:
    try:
        standard = knowledge.require(request.standard_id)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    return store.start(SessionMode.testing, standard).model_dump(mode="json")


@app.get("/api/sessions/{session_id}")
async def get_session(session_id: str) -> dict[str, object]:
    try:
        return store.require(session_id).model_dump(mode="json")
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc


@app.post("/api/teaching/sessions/{session_id}/observations")
async def submit_teaching_observation(session_id: str, request: ObservationRequest) -> dict[str, object]:
    observed = normalize_steps({"steps": request.steps})
    try:
        result = learning.submit_teaching_observation(session_id, observed)
        return result.model_dump(mode="json")
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc


@app.post("/api/teaching/sessions/{session_id}/audit-part")
async def audit_part_for_teaching(session_id: str, request: AuditPartRequest) -> dict[str, object]:
    try:
        observed = extractor.audit_part(request.part_path)
        result = learning.submit_teaching_observation(session_id, observed[-1:] if observed else [])
        return {"observed_steps": [item.model_dump(mode="json") for item in observed], "result": result.model_dump(mode="json")}
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    except Exception as exc:
        raise HTTPException(status_code=500, detail=str(exc)) from exc


@app.post("/api/teaching/sessions/{session_id}/audit-active-part")
async def audit_active_part_for_teaching(session_id: str) -> dict[str, object]:
    try:
        session = store.require(session_id)
        observed = extractor.audit_active_part()
        current_index = session.current_step_index
        current_step = observed[current_index : current_index + 1] if current_index < len(observed) else []
        result = learning.submit_teaching_observation(session_id, current_step)
        return {
            "observed_steps": [item.model_dump(mode="json") for item in observed],
            "submitted_steps": [item.model_dump(mode="json") for item in current_step],
            "result": result.model_dump(mode="json"),
        }
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    except Exception as exc:
        raise HTTPException(status_code=500, detail=str(exc)) from exc


@app.post("/api/testing/sessions/{session_id}/submit")
async def submit_test(session_id: str, request: SubmitTestRequest) -> dict[str, object]:
    try:
        observed = extractor.audit_part(request.part_path) if request.part_path else normalize_steps({"steps": request.steps})
        report = learning.submit_test(session_id, observed)
        return report.model_dump(mode="json")
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    except Exception as exc:
        raise HTTPException(status_code=500, detail=str(exc)) from exc
