"""
init-mater-project

Copyright (C) 2025 [Lauranne Sarribouette] <lauranne.sarribouette@univ-grenoble-alpes.fr>

SPDX-License-Identifier: LGPL-3.0-or-later
"""

import rich_click as click

try:
    import tomllib  # Python 3.11+
except ModuleNotFoundError:
    import tomli as tomllib  # Python 3.10 backport

from pathlib import Path
from typing import Optional
from pydantic import ValidationError

from src.commands.build_dimensions import build_dimensions_hierarchy
from src.commands.generate_data_script import generate_data_script
from src.commands.list_simulation import (
    list_simulation_variables,
    list_simulation_dimensions,
)

from src.commands.map_dimensions import map_dimensions, MappingMode
from src.commands.run import run_simulation
from src.commands.serve_app import serve_app
from src.logging_config import setup_logging
from src.settings import MaterConfig


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


@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 path"
)
@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

        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("--app-path", help="Override app path")
@click.option("--output-folder", help="Override output folder")
@click.option(
    "--config-path", default="config.toml", help="Override config TOML file path"
)
@click.option("--example", is_flag=True, help="Use example data")
def serve(app_path, output_folder, config_path, example):
    """Launch interactive Streamlit app"""

    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

        try:
            if output_folder:
                config.paths.output_folder = Path(output_folder)
            if app_path:
                config.paths.app_path = Path(app_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 output folder: {config.paths.output_folder}")
    click.echo(f"📂 Using app path: {config.paths.app_path}\n")
    serve_app(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 path"
)
@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

        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(
    "--simulation-dimensions-file", help="Override simulation dimensions file"
)
@click.option(
    "--config-path", default="config.toml", help="Override config TOML file path"
)
@click.option("--example", is_flag=True, help="Use example data")
def list_dimensions(simulation_dimensions_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

        try:
            if simulation_dimensions_file:
                config.paths.simulation_dimensions_file = Path(
                    simulation_dimensions_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 simulation dimensions file: {config.paths.simulation_dimensions_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-data-path", help="Override transforms data path")
@click.option(
    "--config-path", default="config.toml", help="Override config TOML file path"
)
@click.option("--example", is_flag=True, help="Use example data")
def generate(raw_path, transforms_data_path, config_path, example):
    """Generate notebooks to transform raw data into MATER-ready 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

        try:
            if raw_path:
                config.paths.raw_path = Path(raw_path)
            if transforms_data_path:
                config.paths.transforms_data_path = Path(transforms_data_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)


@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(
    "--reference-dimensions-file", help="Override reference dimensions file path"
)
@click.option("--dimensions-mapping-file", help="Override dimensions mapping file path")
@click.option(
    "--config-path", default="config.toml", help="Override config TOML file path"
)
@click.option("--example", is_flag=True, help="Use example data")
def map(
    mode,
    input_path,
    reference_dimensions_file,
    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

        try:
            if input_path:
                config.paths.input_path = Path(input_path)
            if reference_dimensions_file:
                config.paths.reference_dimensions_file = Path(reference_dimensions_file)
            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"📚 Reference dimensions file: {config.paths.reference_dimensions_file}"
    )

    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(
    "--simulation-dimensions-file", help="Override simulation dimensions file path"
)
@click.option(
    "--config-path", default="config.toml", help="Override config TOML file path"
)
@click.option("--example", is_flag=True, help="Use example data")
def build(dimensions_mapping_file, simulation_dimensions_file, config_path, example):  # noqa: F811
    """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

        try:
            if dimensions_mapping_file:
                config.paths.dimensions_mapping_file = Path(dimensions_mapping_file)
            if simulation_dimensions_file:
                config.paths.simulation_dimensions_file = Path(
                    simulation_dimensions_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.simulation_dimensions_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()
