import requests

from typing import Literal


def import_module(
        name: Literal[
            "transform.mlv",
            "transform.silver.insertonly",
            "transform.silver.scd2"
        ],
        version: str
) -> str:
    """Imports a module from a specific version of the fabricengineer repository.

    Args:
        name (Literal[*]): The name of the module to import.
        version (str): The version of the module to import.

    Raises:
        ValueError: If the module name is unknown.

    Returns:
        str: The content of the imported module.
    """
    base_path = f"https://raw.githubusercontent.com/enricogoerlitz/fabricengineer-py/refs/tags/{version}/src/fabricengineer"

    module_map = {
        "transform.mlv": _import_module_mlv,
        "transform.silver.insertonly": _import_module_insertonly,
        "transform.silver.scd2": _import_module_scd2
    }

    if name not in module_map:
        raise ValueError(f"Unknown module: {name}")

    return module_map[name](base_path)


def _import_module_insertonly(base_path: str) -> str:
    """Imports the insertonly module from the specified base path."""
    logger_module = _import_logging_logger_module(base_path)
    timer_module = _import_logging_timelogger_module(base_path)
    lakehouse_module = _import_transform_lakehouse_module(base_path)
    base_module = _import_transform_silver_base_module(base_path)
    utils_module = _import_transform_silver_utils_module(base_path)
    insertonly_module = _import_transform_silver_insertonly_module(base_path)
    mlv_module = _import_transform_mlv_module(base_path)

    imports = """
import os
import time
import logging

from datetime import datetime
from typing import Any, Callable
from abc import ABC, abstractmethod
from dataclasses import dataclass
from uuid import uuid4

from pyspark.sql import (
    SparkSession,
    DataFrame,
    functions as F,
    types as T,
    Window
)
""".strip()

    code = "\n\n\n".join([
        imports,
        logger_module,
        lakehouse_module,
        utils_module,
        base_module,
        mlv_module,
        timer_module,
        insertonly_module
    ])

    return code


def _import_module_scd2(base_path: str) -> str:
    """Imports the scd2 module from the specified base path."""
    scd2_module = _import_transform_scd2_module(base_path)
    timer_module = _import_logging_timelogger_module(base_path)
    logger_module = _import_logging_logger_module(base_path)
    lakehouse_module = _import_transform_lakehouse_module(base_path)
    base_module = _import_transform_silver_base_module(base_path)
    utils_module = _import_transform_silver_utils_module(base_path)

    imports = """
import os
import time
import logging

from datetime import datetime
from typing import Any, Callable
from abc import ABC, abstractmethod
from dataclasses import dataclass
from uuid import uuid4

from pyspark.sql import (
    SparkSession,
    DataFrame,
    functions as F,
    types as T,
    Window
)
""".strip()

    code = "\n\n\n".join([
        imports,
        logger_module,
        lakehouse_module,
        utils_module,
        base_module,
        timer_module,
        scd2_module
    ])

    return code


def _import_module_mlv(base_path: str) -> str:
    """Imports the mlv module from the specified base path."""
    mlv_module = _import_transform_mlv_module(base_path)

    imports = """
from typing import Any
from pyspark.sql import DataFrame, SparkSession
""".strip()

    code = "\n\n\n".join([
        imports,
        mlv_module
    ])

    return code


def _import_logging_logger_module(base_path: str) -> str:
    """Imports the logger module from the logging directory."""
    logger_module = f"{base_path}/logging/logger.py"
    return _fetch_module_content(logger_module)


def _import_logging_timelogger_module(base_path: str) -> str:
    """Imports the TimeLogger module from the logging directory."""
    timelogger_module = f"{base_path}/logging/timelogger.py"
    return _fetch_module_content(timelogger_module)


def _import_transform_mlv_module(base_path: str) -> str:
    """Imports the mlv module from the transform directory."""
    mlv_module = f"{base_path}/transform/mlv/mlv.py"
    return _fetch_module_content(mlv_module)


def _import_transform_lakehouse_module(base_path: str) -> str:
    """Imports the lakehouse module from the transform directory."""
    lakehouse_module = f"{base_path}/transform/lakehouse.py"
    return _fetch_module_content(lakehouse_module)


def _import_transform_silver_base_module(base_path: str) -> str:
    """Imports the base module from the transform silver directory."""
    base_module = f"{base_path}/transform/silver/base.py"
    return _fetch_module_content(base_module)


def _import_transform_silver_utils_module(base_path: str) -> str:
    """Imports the utils module from the transform silver directory."""
    utils_module = f"{base_path}/transform/silver/utils.py"
    return _fetch_module_content(utils_module)


def _import_transform_silver_insertonly_module(base_path: str) -> str:
    """Imports the insertonly module from the transform silver directory."""
    insertonly_module = f"{base_path}/transform/silver/insertonly.py"
    return _fetch_module_content(insertonly_module)


def _import_transform_scd2_module(base_path: str) -> str:
    """Imports the scd2 module from the transform directory."""
    scd2_module = f"{base_path}/transform/silver/scd2.py"
    return _fetch_module_content(scd2_module)


def _fetch_module_content(module_path: str) -> str:
    """Fetches the content of a module from the specified path."""
    resp = requests.get(module_path)
    assert resp.status_code == 200, f"Failed to fetch module: {module_path}"

    code = resp.text.split(_filename(module_path))
    if not code or len(code) < 2:
        raise ValueError(
            (f"Module content is malformed: {module_path}."),
            f"Content: {resp.text}"
        )

    code = code[1].strip()
    return code


def _filename(path: str) -> str:
    """Extracts the filename from a given path."""
    return path.split("/")[-1]
