# parc_sdk/scoring.py
"""
PARC Engine v0.1
evaluate_response() → returns cognitive shape (c, m, f, k)

This minimal scorer:
- loads anchors from JSON in the same folder
- computes similarity scores using difflib
- assigns correct vs misconception alignment
- normalizes to c, m, f
- computes basic confidence k
"""

import os
import json
import difflib
from typing import Dict

# ---------------------------------------------------------
# Load anchors with absolute path (bulletproof on any system)
# ---------------------------------------------------------

ANCHOR_PATH = os.path.join(os.path.dirname(__file__), "anchors_math.json")

try:
    with open(ANCHOR_PATH, "r") as f:
        ANCHORS = json.load(f)
except Exception as e:
    print("Error loading anchors:", e)
    ANCHORS = {}


# ---------------------------------------------------------
# String similarity (placeholder for real embeddings)
# ---------------------------------------------------------

def _similarity(a: str, b: str) -> float:
    """
    Simple string similarity.
    Replace later with embeddings.
    """
    return difflib.SequenceMatcher(None, a.lower(), b.lower()).ratio()


# ---------------------------------------------------------
# Main PARC scoring function
# ---------------------------------------------------------

def evaluate_response(concept_id: str, answer_text: str) -> Dict[str, float]:
    """
    Convert a student answer into:
    {c, m, f, k, misc_label}

    c = correctness alignment
    m = misconception alignment
    f = fog
    k = confidence
    """
    # Concept not found → pure fog
    if concept_id not in ANCHORS:
        return {
            "c": 0.0,
            "m": 0.0,
            "f": 1.0,
            "k": 0.3,
            "misc_label": "unknown_concept"
        }

    anchors = ANCHORS[concept_id]

    # -------------------------------
    # Correct similarity
    # -------------------------------
    correct_list = anchors.get("correct", [])
    if correct_list:
        s_c = max(_similarity(answer_text, a) for a in correct_list)
    else:
        s_c = 0.0

    # -------------------------------
    # Misconception similarity
    # -------------------------------
    misc_scores = {}
    for label, examples in anchors.get("misconceptions", {}).items():
        misc_scores[label] = max(_similarity(answer_text, ex) for ex in examples)

    if misc_scores:
        misc_label = max(misc_scores, key=misc_scores.get)
        s_m = misc_scores[misc_label]
    else:
        misc_label = None
        s_m = 0.0

    # -------------------------------
    # Irrelevant score (remainder)
    # -------------------------------
    s_r = 1.0 - max(s_c, s_m)

    # -------------------------------
    # Normalize to c, m, f
    # -------------------------------
    total = s_c + s_m + s_r + 1e-9
    c = s_c / total
    m = s_m / total
    f = 1 - max(c, m)

    # -------------------------------
    # Confidence (rough calibration)
    # -------------------------------
    k = max(0.05, min(1.0, (c + (1 - f)) / 2))

    return {
        "c": c,
        "m": m,
        "f": f,
        "k": k,
        "misc_label": misc_label
    }

