from pathlib import Path
from types import SimpleNamespace

import pytest

from nemo_library_etl.adapter.migman.enums import MigManTransformStep
from nemo_library_etl.adapter.migman.transform_mapping import MigManTransformMapping


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 DummyFetchResult:
    def __init__(self, rows):
        self._rows = rows

    def fetchall(self):
        return self._rows


class DummyConnection:
    def __init__(self, pragma_rows=None):
        if pragma_rows is None:
            pragma_rows = [("Color",), ("Size",), ("Other",)]
        self.pragma_rows = pragma_rows
        self.executed = []

    def execute(self, sql):
        self.executed.append(sql)
        if "pragma_table_info" in sql:
            return DummyFetchResult(self.pragma_rows)
        return DummyFetchResult([])


class DummyLocalDatabase:
    def __init__(self, pragma_rows=None, latest_table="10_joins_Customers"):
        self.con = DummyConnection(pragma_rows=pragma_rows)
        self.latest_table = latest_table
        self.ingest_calls = []
        self.export_calls = []
        self.upload_calls = []

    def ingest_csv(self, **kwargs):
        self.ingest_calls.append(kwargs)

    def latest_table_name(self, **kwargs):
        return self.latest_table

    def export_table(self, **kwargs):
        self.export_calls.append(kwargs)

    def upload_table_to_nemo(self, **kwargs):
        self.upload_calls.append(kwargs)


def make_cfg(
    *,
    etl_directory="tests/.tmp_migman_etl",
    mapping_active=True,
    mappings=None,
    synonyms=None,
    projects=None,
    dump_files=True,
    load_to_nemo=True,
    nemo_project_prefix="mmt",
    delete_temp_files=True,
):
    if mappings is None:
        mappings = []
    if synonyms is None:
        synonyms = []
    if projects is None:
        projects = ["Customers"]

    return SimpleNamespace(
        etl_directory=etl_directory,
        setup=SimpleNamespace(projects=projects),
        transform=SimpleNamespace(
            dump_files=dump_files,
            load_to_nemo=load_to_nemo,
            nemo_project_prefix=nemo_project_prefix,
            delete_temp_files=delete_temp_files,
            mapping=SimpleNamespace(
                active=mapping_active,
                mappings=mappings,
                synonyms=synonyms,
            ),
        ),
    )


def build_transformer(cfg, local_database=None):
    if local_database is None:
        local_database = DummyLocalDatabase()

    return MigManTransformMapping(
        nl=SimpleNamespace(),
        cfg=cfg,
        logger=DummyLogger(),
        fh=SimpleNamespace(),
        local_database=local_database,
    )


def test_mappings_returns_when_mapping_is_inactive():
    cfg = make_cfg(mapping_active=False)
    transformer = build_transformer(cfg)

    transformer.mappings()

    assert transformer.local_database.ingest_calls == []


def test_mappings_returns_when_no_active_mappings():
    cfg = make_cfg(
        mappings=[
            SimpleNamespace(active=False, field_name="Color"),
            SimpleNamespace(active=False, field_name="Size"),
        ]
    )
    transformer = build_transformer(cfg)

    transformer.mappings()

    assert transformer.local_database.ingest_calls == []


def test_ingest_mapping_files_loads_existing_mapping_csv(tmp_path):
    mapping_dir = tmp_path / "mapping"
    mapping_dir.mkdir(parents=True)

    mapping_file = mapping_dir / "color_code.csv"
    mapping_file.write_text("source_value;target_value\nA;Alpha\n", encoding="utf-8")

    cfg = make_cfg(
        etl_directory=str(tmp_path),
        mappings=[SimpleNamespace(active=True, field_name="Color Code")],
    )
    db = DummyLocalDatabase()
    transformer = build_transformer(cfg, db)

    transformer._ingest_mapping_files([SimpleNamespace(field_name="Color Code")])

    assert len(db.ingest_calls) == 1
    call = db.ingest_calls[0]

    assert Path(call["filename"]) == mapping_file
    assert call["table_name"] == f"{MigManTransformStep.MAPPINGS.value}_color_code"
    assert call["create_mode"] == "replace"
    assert call["header"] is True
    assert call["quote"] == '"'
    assert call["separator"] == ";"
    assert call["encoding"] == "utf-8"


def test_ingest_mapping_files_raises_when_file_is_missing(tmp_path):
    cfg = make_cfg(
        etl_directory=str(tmp_path),
        mappings=[SimpleNamespace(active=True, field_name="Color Code")],
    )
    transformer = build_transformer(cfg)

    with pytest.raises(FileNotFoundError, match="does not exist"):
        transformer._ingest_mapping_files([SimpleNamespace(field_name="Color Code")])


def test_resolve_synonym_fields_maps_projects_by_synonyms():
    cfg = make_cfg(
        projects=["Customers", "Suppliers"],
        mappings=[SimpleNamespace(active=True, field_name="Color")],
        synonyms=[
            SimpleNamespace(
                source_field="Color",
                synonym_fields=["Farbe", "Farbcode"],
            )
        ],
    )
    transformer = build_transformer(cfg)

    migmandb = [
        SimpleNamespace(project="Customers", desc_section_location_in_proalpha="Farbe"),
        SimpleNamespace(project="Suppliers", desc_section_location_in_proalpha="Color"),
        SimpleNamespace(project="Ignored", desc_section_location_in_proalpha="Color"),
    ]

    result = transformer._resolve_synonym_fields(
        migmandb,
        [SimpleNamespace(field_name="Color")],
    )

    assert set(result.keys()) == {"Customers", "Suppliers"}
    assert result["Customers"][0].desc_section_location_in_proalpha == "Farbe"
    assert result["Suppliers"][0].desc_section_location_in_proalpha == "Color"


def test_apply_mappings_builds_query_and_exports_and_uploads():
    cfg = make_cfg(
        dump_files=True,
        load_to_nemo=True,
        nemo_project_prefix="mmt",
    )
    db = DummyLocalDatabase(
        pragma_rows=[("Color",), ("Size",), ("Other",)],
        latest_table="10_joins_Customers",
    )
    transformer = build_transformer(cfg, db)

    mapped_fields = [
        SimpleNamespace(desc_section_location_in_proalpha="Color"),
        SimpleNamespace(desc_section_location_in_proalpha="Size"),
    ]
    mapping_details = {"Customers": mapped_fields}

    transformer._apply_mappings(
        migmandb=[],
        mapping_details=mapping_details,
    )

    executed_sql = "\n".join(db.con.executed)

    assert 'SELECT name FROM pragma_table_info(\'10_joins_Customers\')' in executed_sql
    assert 'CREATE OR REPLACE TABLE "20_mappings_Customers" AS' in executed_sql
    assert '"Other" AS "Other"' in executed_sql
    assert '"Color" AS "Orig_Color"' in executed_sql
    assert '"Size" AS "Orig_Size"' in executed_sql
    assert 'COALESCE(map_color.target_value,"Color") AS "Color"' in executed_sql
    assert 'COALESCE(map_size.target_value,"Size") AS "Size"' in executed_sql
    assert 'LEFT JOIN "20_mappings_color" AS map_color' in executed_sql
    assert 'LEFT JOIN "20_mappings_size" AS map_size' in executed_sql

    assert len(db.export_calls) == 1
    assert db.export_calls[0]["table_name"] == "20_mappings_Customers"
    assert db.export_calls[0]["entity"] == "Customers"

    assert len(db.upload_calls) == 1
    assert db.upload_calls[0]["table_name"] == "20_mappings_Customers"
    assert db.upload_calls[0]["project_name"] == "mmt20_mappings_Customers"
    assert db.upload_calls[0]["delete_temp_files"] is True


def test_apply_mappings_raises_when_no_previous_table_exists():
    cfg = make_cfg()
    db = DummyLocalDatabase(latest_table=None)
    transformer = build_transformer(cfg, db)

    with pytest.raises(ValueError, match="No table found for entity Customers"):
        transformer._apply_mappings(
            migmandb=[],
            mapping_details={
                "Customers": [SimpleNamespace(desc_section_location_in_proalpha="Color")]
            },
        )