"""
routes/coding.py — CodingService API
POST /api/coding/run          — AI Compiler (run code, get errors/output)
POST /api/coding/submit       — Submit a single answer (AI evaluated)
POST /api/coding/final-submit — Final submission of all answers
GET  /api/coding/questions    — Get question list
GET  /api/coding/history      — Get candidate's submission history
POST /api/admin/coding/review/{id} — Admin: set final marks

Each submit and final-submit logs an "API SPEC SAMPLE" record so the
backend dev can use those JSON snapshots verbatim as the contract for
the upstream coding-results service. The same payloads are mirrored to
data/by_access_key/{access_key}/coding_submit.json and coding_final.json.
"""
from __future__ import annotations
import json
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, status

from models.schemas import (
    RunCodeRequest, RunCodeResponse,
    SubmitCodeRequest, SubmitCodeResponse,
    FinalSubmitRequest, FinalSubmitResponse,
    SuccessResponse,
)
from services.coding_service import coding_service
from middleware.auth_middleware import get_current_candidate
from routes.peoplehub_external import (
    _audit_dir,
    _log_outgoing_request,
)
from utils.logger import get_logger

log = get_logger(__name__)

router = APIRouter(tags=["coding"])


def _mirror_to_access_key(access_key: str, kind: str, record: dict) -> None:
    """Append a record to data/by_access_key/{access_key}/{kind}.json.

    Best-effort: never raises. Used to give the backend dev a real,
    per-candidate JSON file that exactly mirrors what the future
    upstream API will need to accept.
    """
    if not access_key:
        return
    try:
        path = _audit_dir(access_key) / f"{kind}.json"
        existing = []
        if path.exists():
            try:
                existing = json.loads(path.read_text(encoding="utf-8"))
                if not isinstance(existing, list):
                    existing = [existing]
            except Exception:  # noqa: BLE001
                existing = []
        full = dict(record)
        full["access_key"] = access_key
        full["saved_at"]   = datetime.now(timezone.utc).isoformat()
        existing.append(full)
        # ensure_ascii=False keeps non-Latin characters readable.
        path.write_text(
            json.dumps(existing, indent=2, default=str, ensure_ascii=False),
            encoding="utf-8",
        )
        log.info("[coding] mirrored → %s/%s (%d total)", access_key, kind, len(existing))
    except Exception as exc:  # noqa: BLE001
        log.warning("[coding] mirror failed (ignored): %s", exc)


# ── Questions ─────────────────────────────────────────────────────────────────

@router.get("/api/coding/questions", response_model=SuccessResponse)
def get_questions(_: dict = Depends(get_current_candidate)):
    """Return the coding question list (without solutions)."""
    return SuccessResponse(data=coding_service.get_questions())


# ── Run / Compile ─────────────────────────────────────────────────────────────

@router.post("/api/coding/run", response_model=RunCodeResponse)
def run_code(body: RunCodeRequest, candidate: dict = Depends(get_current_candidate)):
    """
    AI Compiler: analyze code, return compiler-style errors/output.
    Does NOT provide solutions or suggestions — behaves like a real compiler.
    """
    if not body.code.strip():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Code cannot be empty.",
        )

    result = coding_service.run_code(
        language=body.language,
        code=body.code,
        question_id=body.question_id,
    )
    return RunCodeResponse(output=result["output"], status=result["status"])


# ── Submit Single Question ────────────────────────────────────────────────────

@router.post("/api/coding/submit", response_model=SubmitCodeResponse)
def submit_code(body: SubmitCodeRequest, candidate: dict = Depends(get_current_candidate)):
    """
    Submit answer for a single question.
    AI evaluates and scores it; result stored in coding_assessment.json
    AND mirrored to data/by_access_key/{access_key}/coding_submit.json so
    the backend dev has a per-candidate audit trail.
    """
    if not body.code.strip():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Submitted code cannot be empty.",
        )

    access_key = candidate.get("access_key", "")

    # Build the canonical request snapshot up front so the log + the
    # mirrored file see the same shape the upstream API will receive.
    spec_payload = {
        "access_key":    access_key,
        "candidate_id":  candidate.get("id"),
        "question_id":   body.question_id,
        "question_text": body.question_text,
        "language":      body.language,
        "code":          body.code,
        "submitted_at":  datetime.now(timezone.utc).isoformat(),
    }
    _log_outgoing_request("coding_submit", access_key, spec_payload)

    result = coding_service.submit_code(
        candidate_id=candidate["id"],
        access_key=access_key,
        question_id=body.question_id,
        question_text=body.question_text,
        language=body.language,
        code=body.code,
    )

    # Mirror the request + the AI evaluation result against the access_key.
    _mirror_to_access_key(access_key, "coding_submit", {
        "request":  spec_payload,
        "response": {
            "submission_id": result.get("submission_id"),
            "question_id":   result.get("question_id"),
            "ai_result":     result.get("ai_result"),
            "ai_score":      result.get("ai_score"),
            "ai_marks":      result.get("ai_marks"),
            "max_marks":     result.get("max_marks"),
        },
    })

    return SubmitCodeResponse(
        submission_id=result["submission_id"],
        question_id=result["question_id"],
        ai_result=result["ai_result"],
        ai_feedback=result["ai_feedback"],
        ai_score=result["ai_score"],
        ai_marks=result["ai_marks"],
        max_marks=result["max_marks"],
        submitted_at=result["submitted_at"],
    )


# ── Final Submit ──────────────────────────────────────────────────────────────

@router.post("/api/coding/final-submit", response_model=FinalSubmitResponse)
def final_submit(body: FinalSubmitRequest, candidate: dict = Depends(get_current_candidate)):
    """
    Final submission. Records completion and returns a reference number.

    The full request payload is also:
      • Logged as an "API SPEC SAMPLE" for the backend developer.
      • Mirrored to data/by_access_key/{access_key}/coding_final.json so
        each candidate's final submission is a separate, inspectable file.
    """
    access_key = candidate.get("access_key", "")

    spec_payload = {
        "access_key":        access_key,
        "candidate_id":      candidate.get("id"),
        "time_used_seconds": body.time_used_seconds,
        "finished_at":       datetime.now(timezone.utc).isoformat(),
        "submissions":       [
            (s.model_dump() if hasattr(s, "model_dump") else dict(s))
            for s in (body.submissions or [])
        ],
    }
    _log_outgoing_request("coding_final", access_key, spec_payload)

    result = coding_service.final_submit(
        candidate_id=candidate["id"],
        access_key=access_key,
        submissions=body.submissions,
        time_used_seconds=body.time_used_seconds,
    )

    _mirror_to_access_key(access_key, "coding_final", {
        "request":  spec_payload,
        "response": result,
    })

    return FinalSubmitResponse(**result)


# ── History ───────────────────────────────────────────────────────────────────

@router.get("/api/coding/history", response_model=SuccessResponse)
def history(candidate: dict = Depends(get_current_candidate)):
    """Get all coding submissions for the current candidate."""
    submissions = coding_service.get_candidate_submissions(candidate["id"])
    return SuccessResponse(data=submissions)


# ── Admin: set final marks ────────────────────────────────────────────────────

@router.post("/api/admin/coding/review/{submission_id}", response_model=SuccessResponse)
def admin_review(
    submission_id: str,
    final_marks: int,
    # In production: add admin-only auth dependency here
):
    """
    Admin endpoint: override AI marks with manually reviewed final marks.
    Protect this endpoint with admin JWT in production.
    """
    result = coding_service.set_final_marks(submission_id, final_marks)
    if not result:
        raise HTTPException(status_code=404, detail="Submission not found.")
    return SuccessResponse(message="Final marks updated.", data=result)
