# -*- coding: utf-8 -*-
"""DWD 第一阶段重构 — 单元测试。"""
from __future__ import annotations

import logging
from unittest.mock import MagicMock

from tests.unit.task_test_utils import FakeCursor, FakeDBOperations

from tasks.dwd.dwd_load_task import DwdLoadTask


def _make_task() -> DwdLoadTask:
    """构造一个最小可用的 DwdLoadTask 实例。"""
    config = MagicMock()
    # side_effect 让 config.get(key, default) 正确返回 default
    config.get = MagicMock(side_effect=lambda key, default=None: default)
    db = FakeDBOperations()
    api = MagicMock()
    logger = logging.getLogger("test_dwd_phase1")
    return DwdLoadTask(config, db, api, logger)


# ── 5.2 _build_column_mapping: fetched_at 缺失时使用 ods_table / cur 参数 ──


class TestBuildColumnMappingMissingFetchedAt:
    """验证 _build_column_mapping() 在 fetched_at 缺失时的行为（需求 4.1）。"""

    def test_returns_zero_counts_when_fetched_at_missing(self):
        """ods_cols 不含 fetched_at → 返回全零计数字典。"""
        task = _make_task()
        cur = FakeCursor(recorder=[])
        result = task._build_column_mapping(
            cur,
            dwd_table="dwd.dwd_settlement_head",
            ods_table="ods.settlement_records",
            pk_cols=["id"],
            ods_cols=["id", "amount", "create_time"],  # 无 fetched_at
        )
        assert result == {"processed": 0, "inserted": 0, "updated": 0, "skipped": 0}

    def test_error_log_contains_ods_table_param(self, caplog):
        """日志中应包含传入的 ods_table 参数值，证明使用了方法参数。"""
        task = _make_task()
        cur = FakeCursor(recorder=[])
        ods_table = "ods.my_custom_test_table"

        with caplog.at_level(logging.ERROR, logger="test_dwd_phase1"):
            task._build_column_mapping(
                cur,
                dwd_table="dwd.dwd_settlement_head",
                ods_table=ods_table,
                pk_cols=["id"],
                ods_cols=["id", "amount"],
            )

        assert any(ods_table in record.message for record in caplog.records), (
            f"日志中未找到 ods_table='{ods_table}'，实际日志: {[r.message for r in caplog.records]}"
        )

    def test_calls_log_missing_fetched_at_with_cur(self):
        """应调用 _log_missing_fetched_at(cur, ods_table)，验证 cur 参数被正确传递。"""
        task = _make_task()
        cur = FakeCursor(recorder=[])
        ods_table = "ods.settlement_records"

        # 替换为 mock 以验证调用
        task._log_missing_fetched_at = MagicMock()

        task._build_column_mapping(
            cur,
            dwd_table="dwd.dwd_settlement_head",
            ods_table=ods_table,
            pk_cols=["id"],
            ods_cols=["id", "amount"],
        )

        task._log_missing_fetched_at.assert_not_called()
        # _log_missing_fetched_at 仅在 fetched_at 存在时调用，
        # fetched_at 缺失时直接 return，不会走到 _log_missing_fetched_at

    def test_fetched_at_present_calls_log_missing_with_cur(self):
        """fetched_at 存在时，验证 _log_missing_fetched_at 被调用且传入了正确的 cur 和 ods_table。"""
        task = _make_task()
        cur = FakeCursor(recorder=[])
        ods_table = "ods.settlement_records"

        task._log_missing_fetched_at = MagicMock()

        task._build_column_mapping(
            cur,
            dwd_table="dwd.dwd_settlement_head",
            ods_table=ods_table,
            pk_cols=["id"],
            ods_cols=["id", "amount", "fetched_at"],
        )

        task._log_missing_fetched_at.assert_called_once_with(cur, ods_table)


# ── 6.1 验证死代码已清理：hasattr 检查所有已删除的方法和常量 ──

import pytest


class TestDeadCodeRemoved:
    """验证重构后 DwdLoadTask 上不再存在已删除的方法和常量（需求 1.3, 2.1, 2.2, 3.2-3.7）。"""

    @pytest.mark.parametrize(
        "attr, requirement",
        [
            ("_get_fact_watermark", "1.3"),
            ("_insert_missing_by_pk", "2.1"),
            ("_pick_order_column", "3.2"),
            ("_upsert_scd2_row", "3.4"),
            ("_close_current_dim", "3.5"),
            ("_insert_dim_row", "3.6"),
            ("_merge_dim_type1_upsert", "3.7"),
        ],
        ids=lambda v: v if not v.startswith("_") else v.lstrip("_"),
    )
    def test_method_removed(self, attr: str, requirement: str):
        """已删除的方法不应存在于 DwdLoadTask 实例上。"""
        task = _make_task()
        assert not hasattr(task, attr), (
            f"需求 {requirement}：方法 {attr} 应已删除，但仍存在于 DwdLoadTask"
        )

    @pytest.mark.parametrize(
        "attr, requirement",
        [
            ("FACT_ORDER_CANDIDATES", "3.3"),
            ("FACT_MISSING_FILL_TABLES", "2.2"),
        ],
        ids=lambda v: v if not v.startswith("FACT") else v,
    )
    def test_constant_removed(self, attr: str, requirement: str):
        """已删除的常量不应存在于 DwdLoadTask 类上。"""
        assert not hasattr(DwdLoadTask, attr), (
            f"需求 {requirement}：常量 {attr} 应已删除，但仍存在于 DwdLoadTask"
        )


class TestBaseFileRemoved:
    """验证 base_dwd_task.py 已删除（需求 3.1）。"""

    def test_base_dwd_task_import_raises(self):
        """导入 BaseDwdTask 应抛出 ImportError。"""
        with pytest.raises(ImportError):
            from tasks.dwd.base_dwd_task import BaseDwdTask  # noqa: F401


# ── 6.2 验证外部模块导入正常（debug_dwd.py、integrity_checker.py 无 ImportError） ──

import importlib


class TestExternalModuleImports:
    """验证外部模块导入正常且包含 _TIME_COLUMN_CANDIDATES（需求 3.9）。"""

    def test_debug_dwd_imports_without_error(self):
        """scripts.debug.debug_dwd 应能正常导入，无 ImportError。"""
        mod = importlib.import_module("scripts.debug.debug_dwd")
        assert mod is not None

    def test_integrity_checker_imports_without_error(self):
        """quality.integrity_checker 应能正常导入，无 ImportError。"""
        mod = importlib.import_module("quality.integrity_checker")
        assert mod is not None

    def test_debug_dwd_has_time_column_candidates(self):
        """debug_dwd 应包含 _TIME_COLUMN_CANDIDATES 常量。"""
        mod = importlib.import_module("scripts.debug.debug_dwd")
        assert hasattr(mod, "_TIME_COLUMN_CANDIDATES"), (
            "debug_dwd 缺少 _TIME_COLUMN_CANDIDATES 常量"
        )

    def test_integrity_checker_has_time_column_candidates(self):
        """integrity_checker 应包含 _TIME_COLUMN_CANDIDATES 常量。"""
        mod = importlib.import_module("quality.integrity_checker")
        assert hasattr(mod, "_TIME_COLUMN_CANDIDATES"), (
            "integrity_checker 缺少 _TIME_COLUMN_CANDIDATES 常量"
        )

    def test_debug_dwd_time_column_candidates_content(self):
        """debug_dwd._TIME_COLUMN_CANDIDATES 应包含预期的列名。"""
        mod = importlib.import_module("scripts.debug.debug_dwd")
        expected = [
            "pay_time", "create_time", "update_time",
            "occur_time", "settle_time", "start_use_time", "fetched_at",
        ]
        assert mod._TIME_COLUMN_CANDIDATES == expected

    def test_integrity_checker_time_column_candidates_content(self):
        """integrity_checker._TIME_COLUMN_CANDIDATES 应包含预期的列名。"""
        mod = importlib.import_module("quality.integrity_checker")
        expected = [
            "pay_time", "create_time", "update_time",
            "occur_time", "settle_time", "start_use_time", "fetched_at",
        ]
        assert mod._TIME_COLUMN_CANDIDATES == expected
