"""
init-mater-project

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

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

from pathlib import Path
from typing import List, Optional
import textwrap
import click
import nbformat
from jinja2 import Environment, FileSystemLoader, Template
from loguru import logger

from src.settings import MaterConfig


@logger.catch
def generate_data_script(config: MaterConfig) -> None:
    """
    # Create notebook to transform dataset file(s) in MATER-ready data

    ## Arguments
    - `config` (MaterConfig): Configuration object containing paths and simulation parameters

    ## Returns
    - `None`: Creates files in transforms_data_path directory but doesn't return a value
    """

    try:
        dataset_files = load_dataset_files(config.paths.raw_path)

        if not dataset_files:
            click.echo(
                f"❌ Error: No supported dataset files found in {config.paths.raw_path}"
            )
            click.echo(
                "Supported formats: json, csv, xlsx, parquet, pickle, tsv, yaml, xml"
            )
            return

        files_created = 0

        for dataset_file in dataset_files:
            file_format = detect_file_format(dataset_file)
            if not file_format:
                click.echo(f"Skipping unsupported format: {dataset_file}")
                continue

            success = generate_notebook(config, dataset_file, file_format)

            if success:
                files_created += 1

        if files_created > 0:
            click.echo(f"\n✅ Generated {files_created} notebook(s) successfully")
            click.echo("\n💡 Next steps:")
            click.echo("   - Open notebook")
            click.echo("   - Complete data transformation logic")
            click.echo(
                "   - Execute the whole notebook to generate a MATER-ready JSON file"
            )

    except FileNotFoundError as e:
        if "Templates directory not found" in str(e):
            click.echo("❌ Error: Templates directory not found")
        elif "template not found" in str(e):
            click.echo("❌ Error: Required template file missing")
        else:
            click.echo(f"❌ Error: File not found - {e}")
        raise
    except PermissionError:
        click.echo("❌ Error: Permission denied")
        raise
    except Exception as e:
        click.echo(f"❌ Error creating files: {e}")


def generate_notebook(
    config: MaterConfig, dataset_file: Path, file_format: str
) -> bool:
    """
    # Generate Jupyter notebook for interactive data transformation

    ## Arguments
    - `config` (MaterConfig): Configuration object
    - `dataset_file` (Path): Path to source dataset file
    - `file_format` (str): Detected file format

    ## Returns
    - `bool`: True if notebook was created successfully
    """
    notebook_name = dataset_file.stem
    notebook_file = config.paths.transforms_data_path / f"{notebook_name}.ipynb"

    if notebook_file.exists():
        response = input(
            f"Notebook '{notebook_file}' already exists. Overwrite? (y/N): "
        )
        if response.lower() not in ["y", "yes"]:
            click.echo(f"Skipped: {notebook_file}")
            return False

    notebook = create_notebook_structure(
        template_path=config.paths.template_data_file,
        dataset_path=str(dataset_file),
        file_format=file_format,
        provider_info=config.provider_info,
        metadata_info=config.metadata_info,
    )

    with open(notebook_file, "w", encoding="utf-8") as f:
        nbformat.write(notebook, f)

    click.echo(f"Generated notebook: {notebook_file}")
    return True


def create_notebook_structure(
    template_path: Path,
    dataset_path: str,
    file_format: str,
    provider_info: dict,
    metadata_info: dict,
) -> nbformat.NotebookNode:
    """
    # Create notebook structure with cells from template

    ## Arguments
    - `dataset_path` (str): Path to source dataset
    - `file_format` (str): Detected file format
    - `provider_info` (dict): Provider information
    - `metadata_info` (dict): Metadata information

    ## Returns
    - `nbformat.NotebookNode`: Complete notebook structure
    """
    if not template_path.exists():
        raise FileNotFoundError(f"Notebook template not found: {template_path}")

    with open(template_path, "r", encoding="utf-8") as f:
        template_content = f.read()

    template = Template(template_content)

    rendered_content = template.render(
        dataset_path=dataset_path, provider=provider_info, metadata=metadata_info
    )

    notebook = nbformat.reads(rendered_content, as_version=4)

    loading_code = get_loading_template(file_format, dataset_path)
    loading_code_indented = textwrap.indent(loading_code, "    ")

    for cell in notebook.cells:
        if cell.cell_type == "code" and "PLACEHOLDER_LOADING_CODE" in cell.source:
            cell.source = cell.source.replace(
                "# PLACEHOLDER_LOADING_CODE", loading_code_indented
            )

    return notebook


def load_dataset_files(path: Path) -> List[Path]:
    """
    # Find all supported dataset files in specified path (file or directory)

    ## Arguments
    - `path` (Path): File path or directory path to search for dataset files

    ## Returns
    - `List[Path]`: List of Path objects for supported dataset files found
    """
    supported_extensions = {
        ".json",
        ".csv",
        ".xlsx",
        ".xls",
        ".parquet",
        ".pickle",
        ".pkl",
        ".tsv",
        ".yaml",
        ".yml",
        ".xml",
    }

    if path.is_file():
        if path.suffix.lower() in supported_extensions:
            click.echo(f"Found dataset file: {path}")
            return [path]
        else:
            return []

    elif path.is_dir():
        dataset_files = [
            f for f in path.rglob("*") if f.suffix.lower() in supported_extensions
        ]

        if dataset_files:
            click.echo(f"Found dataset files in folder: {path}")
            for file_path in dataset_files:
                relative_path = file_path.relative_to(path)
                click.echo(f"  - {relative_path}")

        return dataset_files

    else:
        return []


def detect_file_format(file_path: Path) -> Optional[str]:
    """
    # Detect file format based on extension and validate file existence

    ## Arguments
    - `file_path` (Path): Path to the file to analyze

    ## Returns
    - `Optional[str]`: String identifier for the file format, or None if unsupported/missing
    """
    if not file_path.exists():
        return None

    suffix = file_path.suffix.lower()
    match suffix:
        case ".json":
            return "json"
        case ".csv":
            return "csv"
        case ".xlsx" | ".xls":
            return "excel"
        case ".parquet":
            return "parquet"
        case ".pickle" | ".pkl":
            return "pickle"
        case ".tsv":
            return "tsv"
        case ".yaml" | ".yml":
            return "yaml"
        case ".xml":
            return "xml"
        case _:
            return None


def get_loading_template(file_format: str, dataset_path: str) -> str:
    """
    # Generate loading code template based on file format

    ## Arguments
    - `file_format` (str): File format identifier for template selection
    - `dataset_path` (str): Path to dataset file for template rendering

    ## Returns
    - `str`: Generated loading code specific to the file format
    """
    template_dir = Path("transforms/templates")

    if not template_dir.exists():
        raise FileNotFoundError(f"Templates directory not found: {template_dir}")

    env = Environment(loader=FileSystemLoader(str(template_dir)))

    try:
        template = env.get_template(f"load_{file_format}.py.j2")
        return template.render(dataset_path=dataset_path)
    except Exception as e:
        raise FileNotFoundError(
            f"Loading template not found: {template_dir}/load_{file_format}.py.j2"
        ) from e
