"""Readers are responsible for loading evaluation repositories and providing a unified interface for accessing their contents."""

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

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

# %% ../nbs/01_readers.ipynb 2
from pathlib import Path
from rich import print
from abc import ABC, abstractmethod
import json
import hashlib

import pandas as pd
from fastcore.all import *

# %% ../nbs/01_readers.ipynb 4
class EvalReader(ABC):
    def __init__(self, cfg): 
        self.cfg = cfg
    
    @abstractmethod
    def read(self): pass
    
    @abstractmethod
    def tfm(self, df): pass
    
    @abstractmethod
    def to_json(self, output_path): pass
    
    def __call__(self):
        df = self.read()
        return self.tfm(df)

# %% ../nbs/01_readers.ipynb 6
def iom_input_cfg():
    return {
        'sheet_name': 'extract from 2005 to Aug 2024',
        '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
class IOMRepoReader(EvalReader):
    def __init__(self, 
                 fname, # path to the excel file
                 max_n=None): # max number of rows to read
        cfg = iom_input_cfg()  
        super().__init__(cfg)
        self.fname = fname
        self.max_n = max_n
    
    def read(self): 
        """Read the excel file and return a dataframe"""
        df = pd.read_excel(self.fname, sheet_name=self.cfg['sheet_name'])
        if self.max_n:
            df = df.head(self.max_n)
        return df
    
    def tfm(self, df):
        """Transform the dataframe into a list of evaluations"""
        df_proc = df.copy()

        # Process dates
        date_cols = self.cfg['date_cols']
        df_proc[date_cols] = df_proc[date_cols].astype(str)
        
        # Process list fields
        for fname, fcfg in self.cfg['list_fields'].items():
            df_proc[fname] = (
                df_proc[fname]
                .astype(str)
                .str.split(fcfg['separator'])
                .apply(lambda x: [item.strip() for item in x if item.strip()])
            )
        
        # Generate IDs
        df_proc['id'] = df_proc.apply(self._mk_id, axis=1)
        
        # Process documents
        df_proc['docs'] = df_proc.apply(self._mk_docs, axis=1)
        
        # Collect metadata
        meta_cols = [col for col in df_proc.columns if col not in ['id', 'docs']]
        
        # Create final structure
        res = []
        for _, row in df_proc.iterrows():
            res.append({
                'id': row['id'],
                'docs': row['docs'],
                'meta': {field: row[field] for field in meta_cols}
            })
        
        return res
    
    def to_json(self, out_path):  
        evals = self()
        with open(out_path, 'w', encoding='utf-8') as f:
            json.dump(evals, f, indent=4, ensure_ascii=False)
    
    def _mk_docs(self, row):
        try:
            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(', ')]
            
            docs = []
            for stype, url, desc in zip(stypes, urls, descs):
                if url.strip():
                    docs.append({
                        'Document Subtype': stype,
                        'File URL': url,
                        'File description': desc
                    })
            return docs
        except Exception as e:
            print(f"Error processing documents for row: {e}")
            return []
    
    def _mk_id(self, row):
        """Generate MD5 hash from specified fields"""
        id_cfg = self.cfg['id_gen']
        fields = id_cfg['fields']
        
        # Concatenate the specified fields
        id_str = ''.join(str(row[field]) for field in fields)
        
        # Generate MD5 hash
        return hashlib.md5(id_str.encode('utf-8')).hexdigest()

# %% ../nbs/01_readers.ipynb 15
default_config = {
    'id_field': 'id',
    'docs_field': 'docs', 
    'url_field': 'File URL'
}

# %% ../nbs/01_readers.ipynb 16
def load_evals(
    json_file: str, # path to the JSON file
    ) -> L:
    "Load evaluations from JSON file and return flattened list of (eval_id, url) tuples"    
    with open(json_file) as f:
        evals = json.load(f)
    return L(evals)
