"""Tests for cfg_* enforcement post-processor."""
from __future__ import annotations

import copy

from pipeline.cfg_enforcer import (
    emit_cfg_seed_inserts,
    enforce_cfg_tables,
)


def _base_schema() -> dict:
    """Minimal Mantara JSON with one enum + one referencing table."""
    return {
        "$schema": "mantara.schema.v1",
        "system_name": "Test System",
        "schema_name": "test_sys",
        "description": "test",
        "menus": [
            {
                "menu_id": 1,
                "menu_name": "Operations",
                "sequence_number": 1,
                "description": "ops",
                "submenus": [
                    {
                        "submenu_id": 101,
                        "submenu_name": "Orders",
                        "sequence_number": 1,
                        "description": "orders",
                        "tables": [
                            {
                                "table_name": "orders",
                                "comment": "Order header",
                                "columns": [
                                    {"name": "id", "type": "SERIAL", "constraints": "PRIMARY KEY"},
                                    {"name": "status", "type": "VARCHAR(30)",
                                     "constraints": "NOT NULL"},
                                ],
                                "foreign_keys": [
                                    {"column": "status",
                                     "references": "test_sys.cfg_order_status(code)"},
                                ],
                            }
                        ],
                    }
                ],
            }
        ],
        "enum_types": [
            {
                "type_name": "test_sys.cfg_order_status_enum",
                "values": ["pending", "shipped", "delivered"],
                "description": "Order lifecycle status.",
            }
        ],
        "assumptions": [],
        "open_questions": [],
    }


def test_lifts_enum_to_cfg_table():
    s = _base_schema()
    enforce_cfg_tables(s)
    cfg_tables = [
        t for m in s["menus"] for sm in m.get("submenus", []) or []
        for t in sm.get("tables") or [] if t["table_name"].startswith("cfg_")
    ]
    assert len(cfg_tables) == 1
    assert cfg_tables[0]["table_name"] == "cfg_order_status"


def test_clears_enum_types():
    s = _base_schema()
    enforce_cfg_tables(s)
    assert s["enum_types"] == []


def test_creates_configuration_menu():
    s = _base_schema()
    enforce_cfg_tables(s)
    cfg_menus = [m for m in s["menus"] if m["menu_name"] == "Configuration"]
    assert len(cfg_menus) == 1


def test_rewrites_referencing_column_to_id_fk():
    s = _base_schema()
    enforce_cfg_tables(s)
    orders = s["menus"][0]["submenus"][0]["tables"][0]
    col_names = {c["name"]: c for c in orders["columns"]}
    assert "status" not in col_names, "status should be renamed to status_id"
    assert "status_id" in col_names
    assert col_names["status_id"]["type"] == "INT"
    fks = orders["foreign_keys"]
    assert any(
        fk["column"] == "status_id"
        and "cfg_order_status(cfg_order_status_id)" in fk["references"]
        for fk in fks
    )


def test_idempotent():
    """Calling enforce twice should not duplicate cfg tables or columns."""
    s = _base_schema()
    enforce_cfg_tables(s)
    snap1 = copy.deepcopy(s)
    enforce_cfg_tables(s)
    # cfg tables count must not grow
    cfg1 = [t["table_name"] for m in snap1["menus"] for sm in m.get("submenus") or []
            for t in sm.get("tables") or [] if t["table_name"].startswith("cfg_")]
    cfg2 = [t["table_name"] for m in s["menus"] for sm in m.get("submenus") or []
            for t in sm.get("tables") or [] if t["table_name"].startswith("cfg_")]
    assert cfg1 == cfg2


def test_no_enum_no_op():
    s = _base_schema()
    s["enum_types"] = []
    # remove the FK reference too
    s["menus"][0]["submenus"][0]["tables"][0]["foreign_keys"] = []
    s["menus"][0]["submenus"][0]["tables"][0]["columns"] = [
        c for c in s["menus"][0]["submenus"][0]["tables"][0]["columns"]
        if c["name"] != "status"
    ]
    before = copy.deepcopy(s)
    enforce_cfg_tables(s)
    # menus structure unchanged (no Configuration menu added)
    s_menus = [m["menu_name"] for m in s["menus"]]
    before_menus = [m["menu_name"] for m in before["menus"]]
    assert s_menus == before_menus


def test_seed_insert_emission():
    s = _base_schema()
    enforce_cfg_tables(s)
    sql = emit_cfg_seed_inserts(s)
    assert "INSERT INTO test_sys.cfg_order_status" in sql
    assert "'pending'" in sql
    assert "'shipped'" in sql
    assert "'delivered'" in sql


def test_handles_existing_cfg_table_without_duplication():
    """If a cfg_* table already exists, skip lifting that one."""
    s = _base_schema()
    # Pre-create cfg_order_status manually
    cfg_menu = {
        "menu_id": 99, "menu_name": "Configuration", "sequence_number": 99,
        "description": "config", "submenus": [
            {
                "submenu_id": 9901, "submenu_name": "Order Status",
                "sequence_number": 1, "description": "x",
                "tables": [{
                    "table_name": "cfg_order_status",
                    "comment": "pre-existing",
                    "columns": [
                        {"name": "cfg_order_status_id", "type": "SERIAL",
                         "constraints": "PRIMARY KEY"},
                    ],
                    "foreign_keys": [],
                }],
            }
        ]
    }
    s["menus"].append(cfg_menu)
    enforce_cfg_tables(s)
    stats = s["_cfg_enforcer"]
    assert stats["already_present"] == 1
    assert stats["lifted"] == 0


def test_strips_schema_prefix_in_enum_name():
    s = _base_schema()
    s["enum_types"][0]["type_name"] = "cfg_order_status_enum"  # no schema prefix
    enforce_cfg_tables(s)
    cfg_names = [t["table_name"] for m in s["menus"] for sm in m.get("submenus") or []
                 for t in sm.get("tables") or [] if t["table_name"].startswith("cfg_")]
    assert "cfg_order_status" in cfg_names


def test_handles_non_cfg_prefixed_enum():
    s = _base_schema()
    s["enum_types"][0]["type_name"] = "test_sys.order_state_enum"  # missing cfg_ prefix
    enforce_cfg_tables(s)
    cfg_names = [t["table_name"] for m in s["menus"] for sm in m.get("submenus") or []
                 for t in sm.get("tables") or [] if t["table_name"].startswith("cfg_")]
    assert "cfg_order_state" in cfg_names
