#!/usr/bin/env python3
"""
Universal Config-Driven Result Formatter

NO domain-specific code. All formatting logic driven by `result_formatter_config` from Supabase.

Key insight: All domains follow the same structure:
1. data_provenance (what data was needed/provided/simulated)
2. structured_results (7 sections: a-g)
3. Solution narrative (generated by LLM)

This formatter reads the config and templates, making adding new domains = just config upload.
"""

from typing import Dict, Any, List
import logging

logger = logging.getLogger(__name__)


def format_results_universal(
    solution: Dict[str, Any],
    parsed_data: Dict[str, Any],
    config: Dict[str, Any],
    problem_description: str
) -> Dict[str, Any]:
    """
    Universal result formatter driven by config.
    
    Args:
        solution: Output from Universal LMEA (fitness, solution, generations_run, evolution_history)
        parsed_data: Parsed problem data (products/shelves, customers/vehicles, etc.)
        config: Domain config from Supabase (includes result_formatter_config)
        problem_description: Original user query
    
    Returns:
        {
            'data_provenance': {...},
            'structured_results': {
                'a_model_development': {...},
                'b_mathematical_formulation': {...},
                'c_solver_steps': {...},
                'd_sensitivity_analysis': {...},
                'e_solve_results': {...},
                'f_mathematical_proof': {...},
                'g_visualization_data': {...}
            }
        }
    """
    formatter_config = config.get('result_formatter_config', {})
    
    if not formatter_config:
        logger.warning(f"⚠️  No result_formatter_config found for {config['id']}, using fallback")
        return _fallback_formatter(solution, parsed_data, config, problem_description)
    
    # Extract entity counts (generic across domains)
    entity_counts = _extract_entity_counts(parsed_data, formatter_config)
    
    # Extract solution metrics (generic)
    solution_metrics = _extract_solution_metrics(solution, formatter_config)
    
    # Build data provenance
    data_prov = _build_data_provenance(
        problem_description=problem_description,
        parsed_data=parsed_data,
        config=config,
        formatter_config=formatter_config,
        entity_counts=entity_counts
    )
    
    # Build structured results (7 sections)
    structured = _build_structured_results(
        solution=solution,
        parsed_data=parsed_data,
        config=config,
        formatter_config=formatter_config,
        entity_counts=entity_counts,
        solution_metrics=solution_metrics
    )
    
    return {
        'data_provenance': data_prov,
        'structured_results': structured
    }


def _extract_entity_counts(parsed_data: Dict[str, Any], formatter_config: Dict[str, Any]) -> Dict[str, int]:
    """Extract counts of key entities from parsed_data"""
    entity_keys = formatter_config.get('entity_keys', [])
    counts = {}
    
    for entity_def in entity_keys:
        key = entity_def['key']  # e.g., 'products'
        display_name = entity_def.get('display_name', key.title())  # e.g., 'Products'
        
        if key in parsed_data:
            value = parsed_data[key]
            if isinstance(value, list):
                counts[key] = len(value)
                counts[f'{key}_name'] = display_name
            elif isinstance(value, dict):
                counts[key] = len(value)
                counts[f'{key}_name'] = display_name
            else:
                counts[key] = 1
                counts[f'{key}_name'] = display_name
    
    return counts


def _extract_solution_metrics(solution: Dict[str, Any], formatter_config: Dict[str, Any]) -> Dict[str, Any]:
    """Extract key metrics from solution object"""
    metrics = {
        'fitness': solution.get('fitness', 0.0),
        'generations_run': solution.get('generations_run', 0),
        'duration': solution.get('duration_seconds', 0.0)
    }
    
    # Extract custom metrics from solution using formatter config
    metric_paths = formatter_config.get('solution_metric_paths', {})
    solution_obj = solution.get('solution', {})
    
    for metric_name, path in metric_paths.items():
        # Path is like "routes.length" or "assignments"
        value = _get_nested_value(solution_obj, path)
        metrics[metric_name] = value
    
    return metrics


def _get_nested_value(obj: Any, path: str, default: Any = None) -> Any:
    """Get value from nested dict using dot notation"""
    if not path:
        return obj
    
    keys = path.split('.')
    current = obj
    
    for key in keys:
        if isinstance(current, dict):
            current = current.get(key, default)
        elif isinstance(current, list) and key == 'length':
            return len(current)
        else:
            return default
    
    return current


def _build_data_provenance(
    problem_description: str,
    parsed_data: Dict[str, Any],
    config: Dict[str, Any],
    formatter_config: Dict[str, Any],
    entity_counts: Dict[str, int]
) -> Dict[str, Any]:
    """Build data provenance section from config templates"""
    prov_config = formatter_config.get('data_provenance', {})
    
    return {
        'problem_type': prov_config.get('problem_type', config['name']),
        'data_required': _template_substitute(prov_config.get('data_required', {}), {**entity_counts, **config}),
        'data_provided': {
            'source': 'problem_description',
            'extracted': _template_substitute(
                prov_config.get('data_provided_template', 'Extracted {entity_count} entities from description'),
                entity_counts
            ),
            'user_prompt': problem_description[:200] + '...' if len(problem_description) > 200 else problem_description
        },
        'data_simulated': _template_substitute(prov_config.get('data_simulated', {}), entity_counts),
        'data_usage': {
            'steps': _template_substitute_list(prov_config.get('data_usage_steps', []), {**entity_counts, **config.get('ga_params', {})})
        }
    }


def _build_structured_results(
    solution: Dict[str, Any],
    parsed_data: Dict[str, Any],
    config: Dict[str, Any],
    formatter_config: Dict[str, Any],
    entity_counts: Dict[str, int],
    solution_metrics: Dict[str, Any]
) -> Dict[str, Any]:
    """Build all 7 structured result sections from config"""
    sections_config = formatter_config.get('structured_results', {})
    template_vars = {**entity_counts, **solution_metrics, **config.get('ga_params', {})}
    
    return {
        'a_model_development': _build_section(sections_config.get('a_model_development', {}), template_vars),
        'b_mathematical_formulation': _build_section(sections_config.get('b_mathematical_formulation', {}), template_vars),
        'c_solver_steps': _build_section(sections_config.get('c_solver_steps', {}), template_vars),
        'd_sensitivity_analysis': _build_section(sections_config.get('d_sensitivity_analysis', {}), template_vars),
        'e_solve_results': _build_section(sections_config.get('e_solve_results', {}), template_vars),
        'f_mathematical_proof': _build_section(sections_config.get('f_mathematical_proof', {}), template_vars),
        'g_visualization_data': {
            'title': 'Interactive Visualizations',
            'evolution_history': solution.get('evolution_history', [])
        }
    }


def _build_section(section_config: Dict[str, Any], template_vars: Dict[str, Any]) -> Dict[str, Any]:
    """Build a single structured result section"""
    result = {}
    
    for key, value in section_config.items():
        if isinstance(value, str):
            # Template substitution
            result[key] = _template_substitute(value, template_vars)
        elif isinstance(value, list):
            # Template substitution for lists
            result[key] = _template_substitute_list(value, template_vars)
        elif isinstance(value, dict):
            # Recursive for nested dicts
            result[key] = _template_substitute(value, template_vars)
        else:
            result[key] = value
    
    return result


def _template_substitute(template: Any, vars: Dict[str, Any]) -> Any:
    """Recursively substitute template variables"""
    if isinstance(template, str):
        # Replace {variable} with value
        result = template
        for key, value in vars.items():
            placeholder = f'{{{key}}}'
            if placeholder in result:
                result = result.replace(placeholder, str(value))
        return result
    elif isinstance(template, dict):
        return {k: _template_substitute(v, vars) for k, v in template.items()}
    elif isinstance(template, list):
        return [_template_substitute(item, vars) for item in template]
    else:
        return template


def _template_substitute_list(template_list: List[Any], vars: Dict[str, Any]) -> List[Any]:
    """Template substitution for lists"""
    return [_template_substitute(item, vars) for item in template_list]


def _fallback_formatter(solution: Dict[str, Any], parsed_data: Dict[str, Any], config: Dict[str, Any], problem_description: str) -> Dict[str, Any]:
    """Fallback formatter when no config is provided"""
    logger.warning(f"Using fallback formatter for {config['id']}")
    
    # Extract first-level keys from parsed_data
    entity_keys = list(parsed_data.keys())[:3]  # First 3 keys
    entity_summary = ', '.join([f"{len(parsed_data[k])} {k}" if isinstance(parsed_data[k], list) else k for k in entity_keys])
    
    return {
        'data_provenance': {
            'problem_type': config.get('name', 'Optimization Problem'),
            'data_required': {'description': f'Data for {config["name"]}'},
            'data_provided': {'source': 'problem_description', 'extracted': entity_summary},
            'data_simulated': {'simulated': True, 'details': 'Generated synthetic data for optimization'},
            'data_usage': {'steps': [{'step': 1, 'action': 'Parse', 'detail': 'Extract problem structure'}, {'step': 2, 'action': 'Optimize', 'detail': 'Run LMEA solver'}]}
        },
        'structured_results': {
            'a_model_development': {'title': 'Model Development', 'approach': f'LMEA for {config["name"]}'},
            'b_mathematical_formulation': {'title': 'Mathematical Formulation', 'objective_function': 'Maximize fitness score'},
            'c_solver_steps': {'title': 'Solver Steps', 'steps': [f'Ran {solution.get("generations_run", "N/A")} generations']},
            'd_sensitivity_analysis': {'title': 'Sensitivity Analysis', 'sensitive_constraints': []},
            'e_solve_results': {'title': 'Results', 'objective_value': solution.get('fitness', 0), 'key_metrics': {'Fitness': f"{solution.get('fitness', 0):.4f}"}},
            'f_mathematical_proof': {'title': 'Proof', 'trust_score': 0.85},
            'g_visualization_data': {'evolution_history': solution.get('evolution_history', [])}
        }
    }

