"""
services/coding_service.py — CodingService
Handles: run code (AI compiler), submit code, evaluate, persist results.
"""
from __future__ import annotations
from config import settings
from utils import json_db as db
from services.openai_service import compile_code, evaluate_code

# Question bank — in production load from DB; here as static list matching the HTML
QUESTIONS = [
    {
        "id": 1, "title": "FizzBuzz", "difficulty": "easy", "points": 10,
        "statement": (
            "Write a function fizzBuzz(n) that prints numbers from 1 to n. "
            "For multiples of 3 print 'Fizz'; for multiples of 5 print 'Buzz'; "
            "for multiples of both print 'FizzBuzz'. Otherwise print the number."
        ),
        "hints": [
            {"label": "Input: n = 15", "type": "input"},
            {"label": "Output: 1,2,Fizz,4,Buzz,...,FizzBuzz", "type": "output"},
        ],
    },
    {
        "id": 2, "title": "Palindrome Check", "difficulty": "easy", "points": 10,
        "statement": (
            "Write a function isPalindrome(s) that returns true if the string s "
            "is a palindrome ignoring case and non-alphanumeric characters."
        ),
        "hints": [
            {"label": "Input: 'A man a plan a canal Panama'", "type": "input"},
            {"label": "Output: true", "type": "output"},
        ],
    },
    {
        "id": 3, "title": "Fibonacci Sequence", "difficulty": "medium", "points": 20,
        "statement": (
            "Write a function fibonacci(n) that returns an array containing "
            "the first n numbers in the Fibonacci sequence."
        ),
        "hints": [
            {"label": "Input: n = 8", "type": "input"},
            {"label": "Output: [0,1,1,2,3,5,8,13]", "type": "output"},
        ],
    },
    {
        "id": 4, "title": "Find Duplicates", "difficulty": "medium", "points": 20,
        "statement": (
            "Write a function findDuplicates(arr) that returns all elements "
            "appearing more than once, each duplicate only once."
        ),
        "hints": [
            {"label": "Input: [1,2,3,2,4,3,5]", "type": "input"},
            {"label": "Output: [2,3]", "type": "output"},
        ],
    },
    {
        "id": 5, "title": "Binary Search", "difficulty": "hard", "points": 40,
        "statement": (
            "Implement binarySearch(arr, target) that searches a sorted array. "
            "Return the index or -1 if not found. Must run in O(log n). "
            "No built-in search methods."
        ),
        "hints": [
            {"label": "Input: arr=[1,3,5,7,9,11], target=7", "type": "input"},
            {"label": "Output: 3 (index)", "type": "output"},
        ],
    },
]

MAX_SCORE = sum(q["points"] for q in QUESTIONS)


class CodingService:
    """All coding assessment logic. Injected into routes."""

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

    @staticmethod
    def get_questions() -> list[dict]:
        return QUESTIONS

    @staticmethod
    def get_question(question_id: int) -> dict | None:
        return next((q for q in QUESTIONS if q["id"] == question_id), None)

    # ── Run Code ──────────────────────────────────────────────────

    @staticmethod
    def run_code(language: str, code: str, question_id: int) -> dict:
        """
        Send code to AI compiler. Returns compiler-style output.
        Does NOT reveal solutions or provide fixes.
        """
        q = CodingService.get_question(question_id)
        title = q["title"] if q else f"Q{question_id}"
        return compile_code(language, code, title)

    # ── Submit Code ───────────────────────────────────────────────

    @staticmethod
    def submit_code(
        candidate_id: str,
        access_key: str,
        question_id: int,
        question_text: str,
        language: str,
        code: str,
    ) -> dict:
        """
        Submit code: evaluate with AI, persist to coding_assessment.json.
        """
        q = CodingService.get_question(question_id)
        max_marks = q["points"] if q else 10

        # AI evaluation
        eval_result = evaluate_code(language, code, question_text, max_marks)

        # Persist
        record = db.insert(settings.CODING_RESULTS_FILE, {
            "candidate_id":   candidate_id,
            "access_key":     access_key,
            "question_id":    question_id,
            "question_text":  question_text,
            "language":       language,
            "code":           code,
            "ai_result":      eval_result["ai_result"],
            "ai_feedback":    eval_result["ai_feedback"],
            "ai_score":       eval_result["ai_score"],
            "ai_marks":       eval_result["ai_marks"],
            "max_marks":      eval_result["max_marks"],
            "final_marks":    None,     # filled later by admin review
            "reviewed":       False,
            "strengths":      eval_result["strengths"],
            "improvements":   eval_result["improvements"],
        })

        return {
            "submission_id": record["id"],
            "question_id":   question_id,
            **eval_result,
            "submitted_at":  record["created_at"],
        }

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

    @staticmethod
    def final_submit(
        candidate_id: str,
        access_key: str,
        submissions: list[dict],
        time_used_seconds: int,
    ) -> dict:
        """
        Record the final submission metadata.
        """
        submitted_ids = {s.get("question_id") for s in submissions}
        total_ai_marks = 0

        for s in submissions:
            qid  = s.get("question_id")
            prev = db.find_one(
                settings.CODING_RESULTS_FILE,
                candidate_id=candidate_id,
                question_id=qid,
            )
            if prev:
                total_ai_marks += prev.get("ai_marks", 0)

        ref = f"PH-CODE-{db.now_iso()[:4]}-{str(abs(hash(candidate_id)))[:4]}"

        return {
            "reference":         ref,
            "total_submitted":   len(submissions),
            "estimated_score":   total_ai_marks,
            "max_score":         MAX_SCORE,
            "submitted_at":      db.now_iso(),
        }

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

    @staticmethod
    def get_candidate_submissions(candidate_id: str) -> list[dict]:
        return db.find_many(settings.CODING_RESULTS_FILE, candidate_id=candidate_id)

    # ── Admin: mark reviewed ──────────────────────────────────────

    @staticmethod
    def set_final_marks(submission_id: str, final_marks: int) -> dict | None:
        return db.update(settings.CODING_RESULTS_FILE, submission_id, {
            "final_marks": final_marks,
            "reviewed":    True,
        })


coding_service = CodingService()
