from types import SimpleNamespace

import pytest

import nemo_library_etl.adapter.migman.flow as flow_mod


class DummyLogger:
    def info(self, *args, **kwargs):
        pass

    def debug(self, *args, **kwargs):
        pass

    def warning(self, *args, **kwargs):
        pass

    def error(self, *args, **kwargs):
        pass


class DummyDuckDBHandler:
    def __init__(self, *args, **kwargs):
        self.con = object()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc, tb):
        return False


def make_cfg(
    *,
    extract_active=True,
    transform_active=True,
    load_active=True,
    extract_method="database",
    file_entries=None,
    source_adapter="source_system",
    etl_directory="tests/.tmp_migman_etl",
):
    if file_entries is None:
        file_entries = []

    return SimpleNamespace(
        etl_directory=etl_directory,
        extract=SimpleNamespace(
            active=extract_active,
            extract_method=extract_method,
            file=file_entries,
        ),
        transform=SimpleNamespace(active=transform_active),
        load=SimpleNamespace(active=load_active),
        setup=SimpleNamespace(source_adapter=source_adapter),
    )


def test_migman_flow_runs_active_steps_in_order(monkeypatch):
    calls = []

    monkeypatch.setattr(flow_mod, "get_run_logger", lambda: DummyLogger())
    monkeypatch.setattr(flow_mod, "NemoLibrary", lambda **kwargs: SimpleNamespace(config=None))
    monkeypatch.setattr(flow_mod, "load_config", lambda **kwargs: make_cfg())
    monkeypatch.setattr(flow_mod, "ETLFileHandler", lambda **kwargs: SimpleNamespace())
    monkeypatch.setattr(flow_mod, "ETLDuckDBHandler", DummyDuckDBHandler)

    monkeypatch.setattr(
        flow_mod,
        "extract",
        lambda **kwargs: calls.append("extract"),
    )
    monkeypatch.setattr(
        flow_mod,
        "transform",
        lambda **kwargs: calls.append("transform"),
    )
    monkeypatch.setattr(
        flow_mod,
        "load",
        lambda **kwargs: calls.append("load"),
    )

    flow_mod.migman_flow.fn(
        {
            "config_ini": "./tests/config.ini",
            "config_json": "./tests/config/migman.json",
        }
    )

    assert calls == ["extract", "transform", "load"]


def test_migman_flow_skips_inactive_steps(monkeypatch):
    calls = []

    cfg = make_cfg(
        extract_active=False,
        transform_active=True,
        load_active=False,
    )

    monkeypatch.setattr(flow_mod, "get_run_logger", lambda: DummyLogger())
    monkeypatch.setattr(flow_mod, "NemoLibrary", lambda **kwargs: SimpleNamespace(config=None))
    monkeypatch.setattr(flow_mod, "load_config", lambda **kwargs: cfg)
    monkeypatch.setattr(flow_mod, "ETLFileHandler", lambda **kwargs: SimpleNamespace())
    monkeypatch.setattr(flow_mod, "ETLDuckDBHandler", DummyDuckDBHandler)

    monkeypatch.setattr(
        flow_mod,
        "extract",
        lambda **kwargs: calls.append("extract"),
    )
    monkeypatch.setattr(
        flow_mod,
        "transform",
        lambda **kwargs: calls.append("transform"),
    )
    monkeypatch.setattr(
        flow_mod,
        "load",
        lambda **kwargs: calls.append("load"),
    )

    flow_mod.migman_flow.fn(
        {
            "config_ini": "./tests/config.ini",
            "config_json": "./tests/config/migman.json",
        }
    )

    assert calls == ["transform"]


def test_extract_dispatches_and_transform_runs_substeps(monkeypatch):
    extract_calls = []
    transform_calls = []

    monkeypatch.setattr(
        flow_mod,
        "extract_from_database",
        lambda **kwargs: extract_calls.append("database"),
    )
    monkeypatch.setattr(
        flow_mod,
        "extract_from_file",
        lambda **kwargs: extract_calls.append("file"),
    )

    db_cfg = make_cfg(extract_method="database")
    file_cfg = make_cfg(
        extract_method="file",
        file_entries=[SimpleNamespace(active=True), SimpleNamespace(active=False)],
    )
    empty_file_cfg = make_cfg(
        extract_method="file",
        file_entries=[SimpleNamespace(active=False)],
    )

    flow_mod.extract.fn(
        nl=SimpleNamespace(),
        cfg=db_cfg,
        logger=DummyLogger(),
        fh=SimpleNamespace(),
    )
    flow_mod.extract.fn(
        nl=SimpleNamespace(),
        cfg=file_cfg,
        logger=DummyLogger(),
        fh=SimpleNamespace(),
    )
    flow_mod.extract.fn(
        nl=SimpleNamespace(),
        cfg=empty_file_cfg,
        logger=DummyLogger(),
        fh=SimpleNamespace(),
    )

    assert extract_calls == ["database", "file"]

    flow_mod.local_database = SimpleNamespace(con=object())

    monkeypatch.setattr(
        flow_mod,
        "transform_joins",
        lambda **kwargs: transform_calls.append("joins"),
    )
    monkeypatch.setattr(
        flow_mod,
        "transform_mappings",
        lambda **kwargs: transform_calls.append("mappings"),
    )
    monkeypatch.setattr(
        flow_mod,
        "transform_duplicates",
        lambda **kwargs: transform_calls.append("duplicates"),
    )
    monkeypatch.setattr(
        flow_mod,
        "transform_nonempty",
        lambda **kwargs: transform_calls.append("nonempty"),
    )

    transform_cfg = make_cfg(source_adapter="db")
    flow_mod.transform.fn(
        nl=SimpleNamespace(),
        cfg=transform_cfg,
        logger=DummyLogger(),
        fh=SimpleNamespace(),
    )

    assert transform_calls == ["joins", "mappings", "duplicates", "nonempty"]


def test_transform_raises_without_source_adapter():
    flow_mod.local_database = SimpleNamespace(con=object())
    cfg = make_cfg(source_adapter="")

    with pytest.raises(ValueError, match="No adapter specified for transformation"):
        flow_mod.transform.fn(
            nl=SimpleNamespace(),
            cfg=cfg,
            logger=DummyLogger(),
            fh=SimpleNamespace(),
        )