#!/usr/bin/env python3
"""Bloom's taxonomy helpers for ASUCB.

Canonical module for Bloom's verb data and objective classification.
Extracted from alignment_validator.py so that lightweight consumers
(e.g., preflight_checks.py) do not pull in the full alignment-graph
dependency chain.

Public API
----------
BLOOMS_LEVELS          dict[str, int]  — ordered level map (Remember=1 … Create=6)
BLOOMS_VERBS           dict[str, set]  — verbs keyed by Bloom's level
UNMEASURABLE_VERBS     set[str]        — verbs that signal unmeasurable objectives
classify_blooms(text)                  — returns (verb, level_str)
is_measurable(text)                    — returns (bool, verb)
"""

import re

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

BLOOMS_LEVELS = {
    "Remember": 1, "Understand": 2, "Apply": 3,
    "Analyze": 4, "Evaluate": 5, "Create": 6,
}

BLOOMS_VERBS = {
    "Remember": {"list", "define", "identify", "name", "recall", "recognize", "state", "label", "match", "select"},
    "Understand": {"describe", "explain", "summarize", "paraphrase", "classify", "discuss", "interpret", "articulate"},
    "Apply": {"apply", "demonstrate", "solve", "use", "implement", "execute", "illustrate", "calculate", "practice", "operate"},
    "Analyze": {"analyze", "compare", "contrast", "differentiate", "distinguish", "examine", "categorize", "deconstruct", "investigate", "organize"},
    "Evaluate": {"evaluate", "assess", "critique", "judge", "justify", "defend", "argue", "appraise", "prioritize", "recommend"},
    "Create": {"create", "design", "develop", "compose", "construct", "formulate", "propose", "plan", "produce", "synthesize"},
}

UNMEASURABLE_VERBS = {
    "understand", "learn", "know", "be aware", "realize",
    "appreciate", "become familiar", "gain knowledge", "be exposed",
}


# ---------------------------------------------------------------------------
# Bloom's utilities
# ---------------------------------------------------------------------------

def classify_blooms(text):
    """Extract leading verb from objective text and classify Bloom's level.

    Returns (verb, blooms_level) or (verb, "Unknown").
    """
    # Strip ID-like prefixes: "1.1:", "M1.1:", "CLO-1:", "2.3:", but NOT plain verbs
    cleaned = re.sub(r'^(?:CLO-?\d+|MLO-?\d+|M\d+[\.\d]*|[\d]+[\.\d]*)\s*[:\-\s]\s*', '', text.strip(), flags=re.IGNORECASE)
    words = cleaned.split()
    if not words:
        return ("", "Unknown")
    raw_verb = words[0].lower()
    # Check the raw verb and common de-conjugations against all Bloom's sets
    candidates = {raw_verb}
    if raw_verb.endswith('es') and len(raw_verb) > 3:
        candidates.add(raw_verb[:-1])   # "analyzes" → "analyze"
        candidates.add(raw_verb[:-2])   # "describes" → "describ" (won't match, harmless)
        candidates.add(raw_verb[:-2] + 'e')  # "evaluates" → "evaluate"
    elif raw_verb.endswith('s') and len(raw_verb) > 3:
        candidates.add(raw_verb[:-1])   # "plans" → "plan"
    for level, verbs in BLOOMS_VERBS.items():
        for candidate in candidates:
            if candidate in verbs:
                return (candidate, level)
    return (raw_verb, "Unknown")


def is_measurable(text):
    """Check if objective uses a measurable verb. Returns (bool, verb)."""
    cleaned = re.sub(r'^[\d.]+[:\s]*|^[A-Z][\w.-]*[:\s]*', '', text.strip())
    words = cleaned.split()
    if not words:
        return (False, "")
    verb = words[0].lower()
    # Check against full verb phrase (e.g., "be aware", "become familiar")
    two_word = " ".join(words[:2]).lower() if len(words) > 1 else ""
    three_word = " ".join(words[:3]).lower() if len(words) > 2 else ""
    for unmeasurable in UNMEASURABLE_VERBS:
        if verb == unmeasurable or two_word == unmeasurable or three_word == unmeasurable:
            return (False, unmeasurable)
    return (True, verb)
