import rich_click as click
import tomllib
import json
from pathlib import Path
from typing import Optional
from pydantic import ValidationError
from commands.build_dimensions import build_dimensions_hierarchy
from commands.build_input_data import build_input_data
from commands.generate_data_script import generate_data_script
from commands.list_simulation import list_simulation_variables, list_simulation_dimensions
from commands.map_dimensions import map_dimensions, MappingMode
from commands.run import run_simulation
from settings import MaterConfig


@click.group()
@click.version_option(version="0.1.0")
def cli():
    """{{ project_name }} - MATER project (generated by init-mater-project)"""
    pass


@cli.group()
def simulation():
    """Manage MATER simulations"""
    pass


@simulation.command()
@click.option("-n", "--name", help="Override simulation name")
@click.option("--start-time", type=int, help="Override simulation start time")
@click.option("--end-time", type=int, help="Override simulation end time")
@click.option("--scenario", help="Override scenario")
@click.option("--frequency", help="Override frequency")
@click.option("--config-path", default="config.toml", 
              help="Override config TOML file")
@click.option('--example', is_flag=True, help='Use example data')
def run(name, start_time, end_time, scenario, frequency, config_path, example):
    """Run a MATER simulation"""
    
    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return
        
        # Apply CLI overrides if any
        try:
            if name:
                config.simulation.name = name
            if start_time:
                config.simulation.start_time = start_time
            if end_time:
                config.simulation.end_time = end_time
            if scenario:
                config.simulation.scenario = scenario
            if frequency:
                config.simulation.frequency = frequency
        
        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
        
        click.echo("🚀 Running MATER simulation")
        click.echo(f"   - 📁 Name: {config.simulation.name}")
        click.echo(f"   - 📅 Period: {config.simulation.start_time} - {config.simulation.end_time}")
        click.echo(f"   - 🎯 Scenario: {config.simulation.scenario}")
        click.echo(f"   - ⏱️  Frequency: {config.simulation.frequency}")

    run_simulation(config)


@simulation.command()
@click.option('--variables-dimensions-path', help='Override variables dimensions path')
@click.option('--config-path', default='config.toml', help='Override config TOML file')
@click.option('--example', is_flag=True, help='Use example data')
def list_variables(variables_dimensions_path, config_path, example):
    """List variables and their associated dimensions"""

    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return
        
        # Apply CLI overrides if any
        try:
            if variables_dimensions_path:
                config.paths.variables_dimensions_path = Path(variables_dimensions_path)

        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
        
    click.echo(f"📂 Using variable dimension file: {config.paths.variables_dimensions_path}\n")
    list_simulation_variables(config)


@simulation.command()
@click.option('--dimensions-hierarchy-file', help='Override dimensions hierarchy file')
@click.option('--config-path', default='config.toml', help='Override config TOML file')
@click.option('--example', is_flag=True, help='Use example data')
def list_dimensions(dimensions_hierarchy_file, config_path, example):
    """List dimensions and their values from hierarchy"""
    
    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return

        # Apply CLI overrides if any
        try:
            if dimensions_hierarchy_file:
                config.paths.dimensions_hierarchy_file = Path(dimensions_hierarchy_file)

        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
        
    click.echo(f"📂 Using dimensions hierarchy file: {config.paths.dimensions_hierarchy_file}\n")
    list_simulation_dimensions(config)


@cli.group()
def data():
    """Manage MATER data transformation"""
    pass


@data.command()
@click.option('--raw-path', help='Override raw data path')
@click.option('--transforms-script-path', help='Override transforms script path')
@click.option('--config-path', default='config.toml', help='Override config TOML file')
@click.option('--example', is_flag=True, help='Use example data')
def generate(raw_path, transforms_script_path, config_path, example):
    """Generate scripts to transform raw data into MATER-formatted input data"""
    
    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return
    
        # Apply CLI overrides if any
        try:
            if raw_path:
                config.paths.raw_path = Path(raw_path)
            if transforms_script_path:
                config.paths.transforms_script_path = Path(transforms_script_path)

        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
    
    generate_data_script(config)


@data.command()
@click.option('--input-path', help='Override input data path')
@click.option('--raw-path', help='Override raw data path')
@click.option('--transforms-script-path', help='Override transforms script path')
@click.option('--config-path', default='config.toml', help='Override config TOML file')
@click.option('--example', is_flag=True, help='Use example data')
def build(input_path, raw_path, transforms_script_path, config_path, example):
    """Execute transformation scripts to build MATER input data"""
    
    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return
    
        # Apply CLI overrides if any
        try:
            if input_path:
                config.paths.input_path = Path(input_path)
            if raw_path:
                config.paths.raw_path = Path(raw_path)
            if transforms_script_path:
                config.paths.transforms_script_path = Path(transforms_script_path)

        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
    
    build_input_data(config)


@cli.group()
def dimensions():
    """Manage dimensions mapping and hierarchy"""
    pass


@dimensions.command()
@click.option('-m', '--mode', 
              type=click.Choice(MappingMode, case_sensitive=False),
              help='Mapping operation mode')
@click.option('--input-path', help='Override input data path')
@click.option('--dimensions-values-path', help='Override dimensions values path')
@click.option('--dimensions-mapping-file', help='Override dimensions mapping file path')
@click.option('--config-path', default='config.toml', help='Override config TOML file')
@click.option('--example', is_flag=True, help='Use example data')
def map(mode, input_path, dimensions_values_path, dimensions_mapping_file, config_path, example):
    """Map input dimensions to references and create or update mapping file"""
    
    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return
    
        # Apply CLI overrides if any
        try:
            if input_path:
                config.paths.input_path = Path(input_path)
            if dimensions_values_path:
                config.paths.dimensions_values_path = Path(dimensions_values_path)
            if dimensions_mapping_file:
                config.paths.dimensions_mapping_file = Path(dimensions_mapping_file)

        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
    
    if mode is None:
        mode = MappingMode.INITIAL
    
    click.echo(f"📂 Input data JSON file(s): {config.paths.input_path}")
    click.echo(f"📚 Dimensions values file: {config.paths.dimensions_values_path}")
    
    if mode == MappingMode.INITIAL:
        click.echo("\n🗺️  Mapping dimensions to reference...")

    elif mode == MappingMode.INCREMENTAL:
        click.echo("\n🔄 Adding new dimensions to existing mapping...")

    elif mode == MappingMode.RESOLVE:
        click.echo("\n🔧 Resolving references and validating mapping...")

    elif mode == MappingMode.FORCE:
        click.echo("\n⚡ Recreating mapping from scratch...")
        
    map_dimensions(config, mode)


@dimensions.command()
@click.option('--dimensions-mapping-file', help='Override dimensions mapping file path')
@click.option('--dimensions-hierarchy-file', help='Override dimensions hierarchy file path')
@click.option('--config-path', default='config.toml', help='Override config TOML file')
@click.option('--example', is_flag=True, help='Use example data')
def build(dimensions_mapping_file, dimensions_hierarchy_file, config_path, example):
    """Build dimensions hierarchy from mapping file"""
     
    if example:
        config = load_config(config_path, example=True)
        if config is None:
            return
        
        click.echo("🧪 Example data")
        
    else:
        config = load_config(config_path)
        if config is None:
            return
    
        # Apply CLI overrides if any
        try:
            if dimensions_mapping_file:
                config.paths.dimensions_mapping_file = Path(dimensions_mapping_file)
            if dimensions_hierarchy_file:
                config.paths.dimensions_hierarchy_file = Path(dimensions_hierarchy_file)

        except ValidationError as e:
            click.echo("❌ Configuration validation error:")
            for error in e.errors():
                field = " -> ".join(str(x) for x in error['loc'])
                click.echo(f"  {field}: {error['msg']}")
            return
    
    click.echo(f"📂 Input mapping file: {config.paths.dimensions_mapping_file}")
    click.echo(f"📄 Output hierarchy file: {config.paths.dimensions_hierarchy_file}")
    click.echo("\n🏗️  Building dimensions hierarchy...")
    build_dimensions_hierarchy(config, example=example)


def load_config(config_path: str = "config.toml", example: bool = False) -> Optional[MaterConfig]:
    """
    Load configuration from TOML file with optional example settings override
    
    Args:
        config_path: Path to the TOML configuration file
        example: If True, override config with example settings after loading
        
    Returns:
        MaterConfig instance if successful, None if loading failed
    """
    config_file = Path(config_path)
    
    if not config_file.exists():
        click.echo(f"❌ Error: Configuration file not found: '{config_path}'")
        click.echo("Make sure a config TOML file exists in your project (config.toml by default).")
        return None
    
    try:
        with open(config_file, "rb") as f:
            toml_data = tomllib.load(f)
        
        config = MaterConfig(
            simulation=toml_data.get("simulation", {}),
            paths=toml_data.get("paths", {}),
            provider=toml_data.get("provider", {}),
            metadata=toml_data.get("metadata", {})
        )
        
        if example:
            config.use_example_settings()
            
        return config
        
    except ValidationError as e:
        click.echo("❌ Configuration validation error:")
        for error in e.errors():
            field = " -> ".join(str(x) for x in error['loc'])
            click.echo(f" {field}: {error['msg']}")
        return None
    except Exception as e:
        click.echo(f"❌ Error loading configuration: {e}")
        return None


if __name__ == "__main__":
    cli()