"""Maps IOM evaluation report against evaluation frameworks"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/05_mapper.ipynb.

# %% auto 0
__all__ = ['ThemeScore', 'ThemeScores', 'parse_json_response', 'sort_by_centrality', 'get_top_ids', 'mk_system_blocks',
           'map_themes', 'load_prompts', 'map_all']

# %% ../nbs/05_mapper.ipynb 4
from fastcore.all import *
from pydantic import BaseModel
from lisette.core import completion, mk_msg
from .core import load_prompt, n_tokens
from .themes import load_enablers, load_ccp, load_gcms, load_srf_outs, load_gcm_lut, fmt_enablers_ccp, fmt_srf_outs, get_srf_outs, load_all_themes
import json

# %% ../nbs/05_mapper.ipynb 6
class ThemeScore(BaseModel):
    theme_id: str       # Unique identifier for the theme
    theme_title: str    # Human-readable theme name
    centrality_score: float  # 0-1 score indicating how central theme is to report
    reasoning: str      # LLM's explanation for the score
    confidence: str     # high/medium/low confidence in assessment

# %% ../nbs/05_mapper.ipynb 7
class ThemeScores(BaseModel):
    scores: list[ThemeScore]  # All theme scores for a single mapping call

# %% ../nbs/05_mapper.ipynb 9
def parse_json_response(res): return json.loads(res.choices[0].message.content)

# %% ../nbs/05_mapper.ipynb 10
def sort_by_centrality(res):
    "Sort themes by centrality score, accepts raw response or parsed dict"
    data = res if isinstance(res, dict) else parse_json_response(res)
    return sorted(data['scores'], key=lambda x: x['centrality_score'], reverse=True)

# %% ../nbs/05_mapper.ipynb 11
def get_top_ids(res, min_score=0.7):
    "Get IDs of themes with centrality score >= min_score, accepts raw response or parsed dict"
    data = res if isinstance(res, dict) else parse_json_response(res)
    return [o['theme_id'] for o in data['scores'] if o['centrality_score'] >= min_score]

# %% ../nbs/05_mapper.ipynb 13
def mk_system_blocks(report:str  # Full report text to analyze
                    ) -> list:   # Anthropic-style content blocks with cache control
    "Create cached system message blocks from report text"
    return [{"type": "text", "text": f"## Report to Analyze\n\n{report}", "cache_control": {"type": "ephemeral"}}]

# %% ../nbs/05_mapper.ipynb 14
def map_themes(system_blocks:list,   # Cached system blocks from mk_system_blocks
               themes:str,            # Formatted themes text to score against
               prompt:str,            # Mapping instruction prompt
               model:str='claude-haiku-4-5',  # Model to use for completion
               response_format=ThemeScores    # Pydantic model for structured output
              ):
    "Map report against themes using cached system blocks"
    return completion(model=model, system=system_blocks, messages=[mk_msg(f"{prompt}\n\n## Themes\n\n{themes}")], 
                     response_format=response_format, max_tokens=8192)

# %% ../nbs/05_mapper.ipynb 20
def load_prompts(path:str='files/prompts'  # Directory containing prompt files
                ) -> AttrDict:              # Dict with srf_enablers, srf_ccps, gcm, srf_outputs prompts
    "Load all mapping prompts"
    return AttrDict({k: load_prompt(k, path) for k in ['srf_enablers', 'srf_ccps', 'gcm', 'srf_outputs']})

# %% ../nbs/05_mapper.ipynb 21
@delegates(map_themes)
def map_all(report:str,                      # Full report text to analyze
            path:str='files/themes',         # Directory containing theme JSON files
            prompt_path:str='files/prompts', # Directory containing prompt files
            verbose:bool=True,               # Print progress messages
            **kwargs                         # Additional args passed to map_themes (e.g. model)
           ) -> AttrDict:                    # Dict with enablers, ccp, gcm, outputs results
    "Map report against all theme classes: enablers → CCP → GCM → outputs"
    themes, prompts = load_all_themes(path), load_prompts(prompt_path)
    system_blocks = mk_system_blocks(report)
    
    if verbose: print("Mapping SRF Enablers...")
    enablers_res = map_themes(system_blocks, fmt_enablers_ccp(themes.enablers), prompts.srf_enablers, **kwargs)
    if verbose: print("Mapping Cross-cutting Priorities...")
    ccp_res = map_themes(system_blocks, fmt_enablers_ccp(themes.ccp), prompts.srf_ccps, **kwargs)
    if verbose: print("Mapping GCM Objectives...")
    gcm_res = map_themes(system_blocks, themes.gcms, prompts.gcm, **kwargs)
    
    top_gcm_ids = get_top_ids(gcm_res)
    if not top_gcm_ids:
        if verbose: print("No GCM objectives scored ≥0.7, skipping SRF Outputs")
        return AttrDict(enablers=enablers_res, ccp=ccp_res, gcm=gcm_res, outputs=None)
    
    if verbose: print(f"Top GCM: {top_gcm_ids[0]} (from {len(top_gcm_ids)} candidates)")
    output_ids = get_srf_outs(themes.gcm_lut, [top_gcm_ids[0]])
    if verbose: print(f"Mapping {len(output_ids)} filtered SRF Outputs...")
    outputs_res = map_themes(system_blocks, fmt_srf_outs(themes.srf_outs, output_ids), prompts.srf_outputs, **kwargs)
    return AttrDict(enablers=enablers_res, ccp=ccp_res, gcm=gcm_res, outputs=outputs_res)

