# dods/datagen/main.py
import sys
import inspect
from pathlib import Path
import yaml

from dods.datagen.helpers.engine import DataScenario
from dods.datagen.helpers.config_parser import parse_yaml_config


def run(config=None):
    """
    Universal entry point for the DODS Data Generator.

    Typical usage (Notebook / Python):
        >>> import dods.datagen as dg
        >>> dg.create_template()      # writes: template.yaml (basic)
        >>> df = dg.run("template")   # reads: template.yaml -> writes CSV as configured in YAML

    You can also run from a custom path:
        >>> df = dg.run("path/to/template")   # uses path/to/template.yaml
        >>> df = dg.run("config.yaml")        # explicit file

    Supported inputs:
        - str / Path:
            A YAML path. If you pass a path without extension, ".yaml" is assumed.
            Paths are resolved relative to the current working directory (CWD).
        - dict:
            A config dictionary (normalized internally like a YAML config).

    What it does:
        - Loads and validates the config (defaults are applied if missing)
        - Evaluates features top-to-bottom (later features may depend on earlier ones)
        - Applies optional post steps: noise, clipping, missing values, outliers
        - Saves the generated dataset as CSV to:
              <output_path>/<filename>
          (both are defined inside the YAML)
        - Returns the generated pandas DataFrame
    """

    print("\n[datagen] 🚀 Starting data generation...")

    # -------------------------------------------------------------------------
    # STEP 1: Load and parse configuration
    # -------------------------------------------------------------------------
    if config is None and len(sys.argv) > 1:
        # Example: `datagen contest.yaml`
        config = sys.argv[1]

    if config is None:
        raise ValueError("[datagen] ❌ You must provide a config dict or YAML file path.")

    try:
        # --- CASE 1: YAML path provided ---
        if isinstance(config, (str, Path)):
            config_path = Path(config)

            # Resolve relative paths (robust for notebooks + scripts): always relative to CWD
            if not config_path.is_absolute():
                config_path = Path.cwd() / config_path

            # Convenience: allow passing "template" instead of "template.yaml"
            if config_path.suffix == "":
                candidate = config_path.with_suffix(".yaml")
                if candidate.exists():
                    config_path = candidate

            config_path = config_path.resolve()

            if not config_path.exists():
                raise FileNotFoundError(f"Config file not found: {config_path}")


            print(f"[datagen] 📂 Loading configuration from: {config_path}")
            config = parse_yaml_config(config_path)

        # --- CASE 2: Dictionary provided directly ---
        elif isinstance(config, dict):
            print("[datagen] ✅ Using provided config dictionary.")
            # Normalize dictionary through parser for consistency
            try:
                # Write to temporary file (ensures identical parse path)
                temp_path = Path(".temp_config_auto.yaml")
                with open(temp_path, "w", encoding="utf-8") as tmp:
                    yaml.safe_dump(config, tmp)
                config = parse_yaml_config(temp_path)
                temp_path.unlink(missing_ok=True)
            except Exception as e:
                raise ValueError(f"[datagen] ❌ Failed to normalize provided config dict: {e}")


        # --- CASE 3: Invalid input type ---
        else:
            raise TypeError("[datagen] ❌ Config must be a dict or YAML file path.")

    except Exception as e:
        print(f"[datagen] ❌ Failed to load or parse configuration: {e}")
        raise

    # -------------------------------------------------------------------------
    # STEP 2: Generate synthetic data
    # -------------------------------------------------------------------------
    try:
        scenario = DataScenario(config)
        df = scenario.generate()
        print(f"[datagen] ✅ Scenario '{config.get('scenario')}' generated successfully.")
    except Exception as e:
        print(f"[datagen] ❌ Error during data generation: {e}")
        raise

    # -------------------------------------------------------------------------
    # STEP 3: Save generated dataset
    # -------------------------------------------------------------------------
    output_dir = Path(config.get("output_path", "data"))
    filename = config.get("filename", "testdata.csv")
    output_dir.mkdir(parents=True, exist_ok=True)
    output_path = output_dir / filename

    try:
        df.to_csv(output_path, index=False)
        print(f"[datagen] 💾 Saved dataset → {output_path}")
    except PermissionError:
        print(f"[datagen] ❌ Permission denied while saving to: {output_path}")
    except Exception as e:
        print(f"[datagen] ⚠️ Could not save dataset: {e}")

    # -------------------------------------------------------------------------
    # STEP 4: Finish and return
    # -------------------------------------------------------------------------
    print("[datagen] 🏁 Done.\n")
    return df


if __name__ == "__main__":
    """
    When executed directly (e.g. via CLI entry point),
    this ensures `run()` is invoked automatically.

    Example:
        $ datagen config.yaml
    """
    run()
