"""This module provides a unified interface for loading evaluation data from organizational repositories (IOM, UNHCR, etc.) and transforming it into standardized JSON format."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_readers.ipynb.

# %% auto 0
__all__ = ['default_config', 'EvalReader', 'iom_input_cfg', 'Evaluation', 'IOMRepoReader', 'load_evals', 'in_docs', 'find_eval']

# %% ../nbs/01_readers.ipynb 3
from pathlib import Path
from dataclasses import dataclass
import json
import hashlib
import pandas as pd
from fastcore.all import *

# %% ../nbs/01_readers.ipynb 6
class EvalReader:
    def __init__(self, 
                 cfg:dict # Configuration dict with field mappings and processing rules
                ): store_attr()
    def read(self): raise NotImplementedError
    def tfm(self, df): raise NotImplementedError
    def to_json(self, output_path): raise NotImplementedError
    def __call__(self):
        df = self.read()
        return self.tfm(df)

# %% ../nbs/01_readers.ipynb 7
def iom_input_cfg():
    return {
        'date_cols': ['Date of Publication', 'Evaluation Period From Date', 'Evaluation Period To Date'],
        'string_cols': ['Year'],
        'list_fields': {
            'Countries Covered': {'separator': ',', 'clean': True}
        },
        'document_fields': ['Document Subtype', 'File URL', 'File description'],
        'id_gen': {
            'method': 'md5',
            'fields': ['Title', 'Year', 'Project Code']  # fields to hash
        },
        'field_mappings': {
            'Title': 'title',
            'Year': 'year',
            # other mappings
        }
    }

# %% ../nbs/01_readers.ipynb 8
@dataclass
class Evaluation:
    "An evaluation with rich notebook display"
    id:str
    docs:list
    meta:dict
        
    def _repr_markdown_(self):
        title = self.meta.get('Title', 'Untitled')
        year = self.meta.get('Year', 'n/a')
        org = self.meta.get('Evaluation Commissioner', 'Unknown')
        countries = self.meta.get('Countries Covered', [])
        country_str = ', '.join(countries[:3]) if countries else 'Not specified'
        if len(countries) > 3: country_str += f' (+{len(countries)-3} more)'
        
        return f"""
### {title}
**Year:** {year} | **Organization:** {org} | **Countries:** {country_str}

**Documents:** {len(self.docs)} available  
**ID:** `{self.id}`
"""

# %% ../nbs/01_readers.ipynb 10
class IOMRepoReader(EvalReader):
    def __init__(self, 
                 fname:Path # Path to the CSV export file
                 ): 
        cfg = iom_input_cfg()  
        super().__init__(cfg)
        store_attr()


# %% ../nbs/01_readers.ipynb 12
@patch
def read(self:IOMRepoReader): return pd.read_csv(self.fname)

# %% ../nbs/01_readers.ipynb 15
@patch
def _mk_id(self:IOMRepoReader, 
           row # DataFrame row containing evaluation metadata
          ):
    id_str = ''.join(str(row[f]) for f in self.cfg['id_gen']['fields'])
    return hashlib.md5(id_str.encode('utf-8')).hexdigest()

# %% ../nbs/01_readers.ipynb 19
@patch
def _mk_docs(self:IOMRepoReader, 
             row # DataFrame row with document fields
            ):
    "Parse document fields into structured records"
    stypes = [s.strip() for s in str(row['Document Subtype']).split(', ')]
    urls = [u.strip() for u in str(row['File URL']).split(', ')]
    descs = [d.strip() for d in str(row['File description']).split(', ')]
    return [dict(subtype=st, url=u, desc=d) for st,u,d in zip(stypes,urls,descs) if u.strip()]

# %% ../nbs/01_readers.ipynb 22
@patch
def _proc_dates(self:IOMRepoReader, df):
    df[self.cfg['date_cols']] = df[self.cfg['date_cols']].astype(str)
    return df

# %% ../nbs/01_readers.ipynb 24
@patch
def _proc_lists(self:IOMRepoReader, df):
    for fname,fcfg in self.cfg['list_fields'].items():
        df[fname] = df[fname].astype(str).str.split(fcfg['separator']).apply(lambda x: [item.strip() for item in x if item.strip()])
    return df

# %% ../nbs/01_readers.ipynb 26
@patch
def _to_dict(self:IOMRepoReader, row):
    "Convert row to evaluation dict"
    meta_cols = [col for col in row.index if col not in ['id', 'docs']]
    return dict(id=row['id'], docs=row['docs'], meta={f:row[f] for f in meta_cols})

# %% ../nbs/01_readers.ipynb 27
@patch
def _to_eval(self:IOMRepoReader, row):
    "Convert row to Evaluation object"
    meta_cols = [col for col in row.index if col not in ['id', 'docs']]
    return Evaluation(id=row['id'], docs=row['docs'], meta={f:row[f] for f in meta_cols})


# %% ../nbs/01_readers.ipynb 28
@patch
def tfm(self:IOMRepoReader, df:pd.DataFrame):
    "Transform raw dataframe to evaluation objects"
    df_proc = self._proc_lists(self._proc_dates(df.copy()))
    df_proc['id'] = df_proc.apply(self._mk_id, axis=1)
    df_proc['docs'] = df_proc.apply(self._mk_docs, axis=1)
    return [self._to_eval(row) for _,row in df_proc.iterrows()]

# %% ../nbs/01_readers.ipynb 31
@patch
def to_json(self:IOMRepoReader, out_path:Path):
    evals = self()
    evals_dict = [dict(id=e.id, docs=e.docs, meta=e.meta) for e in evals]
    with open(out_path, 'w', encoding='utf-8') as f: json.dump(evals_dict, f, indent=4, ensure_ascii=False)

# %% ../nbs/01_readers.ipynb 39
default_config = AttrDict(id='id', docs='docs', url='url')

# %% ../nbs/01_readers.ipynb 40
def load_evals(json_file):
    "Load evaluations from JSON file"
    return L([Evaluation(**o) for o in json.loads(Path(json_file).read_text())])

# %% ../nbs/01_readers.ipynb 43
def in_docs(
    ev:Evaluation, # Evaluation object
    url:str # URL of an evaluation report 
    ):
    "Check if a URL is in the documents of an evaluation" 
    return any(L(ev.docs).filter(lambda x: x['url'] == url))

# %% ../nbs/01_readers.ipynb 45
def find_eval(
    evals:list, # List of evaluations
    query:str, # Title or URL of evaluation
    by:str='title' # 'title' or 'url'
    ): 
    "Find evaluation by title or URL"
    if by == 'title': return first([o for o in evals if o.meta['Title'] == query])
    if by == 'url': return first([o for o in evals if in_docs(o, query)])
