import asyncio
import argparse
import os
import sys
import tempfile
import unittest
from datetime import date
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch


HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))

import execute_creation_once  # noqa: E402
import execute_creation_apply  # noqa: E402
import run_daily_service  # noqa: E402
import sync_feishu_account_config  # noqa: E402
from tools import delivery_config  # noqa: E402
from tools import feishu_doc  # noqa: E402
from tools import im_approval_ad_creation  # noqa: E402
from tools import im_approval_creation  # noqa: E402


class CreationAccountScopeTest(unittest.TestCase):
    def test_open_ended_age_uses_default_maximum(self):
        self.assertEqual((45, 66), sync_feishu_account_config._parse_age_range("45+"))

    def test_account_id_parser_accepts_positive_integer(self):
        self.assertEqual(84502339, execute_creation_once._parse_account_id_arg("84502339"))

    def test_account_id_parser_rejects_invalid_values(self):
        for raw in ("not-a-number", "0", "-1"):
            with self.subTest(raw=raw), self.assertRaises(argparse.ArgumentTypeError):
                execute_creation_once._parse_account_id_arg(raw)

    def test_removed_environment_scope_fails_closed(self):
        with patch.dict(os.environ, {"CREATION_ONLY_ACCOUNT_IDS": "bad"}):
            with self.assertRaisesRegex(ValueError, "已停用"):
                execute_creation_once._normalize_requested_account_ids(None)

    def test_explicit_scope_filters_without_falling_back_to_all(self):
        with patch.dict(os.environ, {"CREATION_ONLY_CROWD_PACKAGES": ""}, clear=False):
            selected = execute_creation_once._filter_creation_accounts(
                [101, 202],
                "test",
                {202},
            )
        self.assertEqual([202], selected)

    def test_requested_account_must_be_enabled_for_config_date(self):
        with self.assertRaisesRegex(ValueError, "拒绝执行"):
            execute_creation_once._validate_requested_accounts(
                {202},
                [101],
                date(2026, 8, 6),
            )

    def test_feishu_sync_uses_latest_row_and_only_requested_account(self):
        rows = [
            ["自动化账户"],
            ["日期", "账户id", "人群包", "初始出价", "预算（单广告）", "是否自动化执行"],
            ["2026-08-04", "101", "泛人群", "0.30", "100", "是"],
            ["2026-08-05", "101", "泛人群", "0.35", "100", "是"],
            ["2026-08-05", "202", "泛人群", "0.40", "100", "是"],
        ]
        with patch.object(
            sync_feishu_account_config, "_fetch_rows", return_value=rows
        ), patch.object(
            sync_feishu_account_config, "upsert_account_config"
        ) as upsert, patch.object(
            sync_feishu_account_config, "set_account_automation_enabled"
        ):
            stats = sync_feishu_account_config.sync_from_feishu(account_ids={101})

        self.assertEqual("latest_per_account", stats["selection_mode"])
        self.assertEqual(1, stats["enabled"])
        record = upsert.call_args.args[0]
        self.assertEqual(101, record.account_id)
        self.assertEqual(date(2026, 8, 5), record.config_date)
        self.assertEqual(35, record.bid_amount_fen)


class DailyServiceOneOffTest(unittest.TestCase):
    def test_one_off_forwards_repeated_accounts_and_uses_creation_lock(self):
        with patch.object(run_daily_service, "_run_script") as run_script:
            run_daily_service.run_creation_once(
                account_ids=[101, 202],
                config_date="2026-08-06",
            )
        run_script.assert_called_once_with(
            "execute_creation_once.py",
            "ad_daily_creation",
            [
                "--config-date",
                "2026-08-06",
                "--account-id",
                "101",
                "--account-id",
                "202",
            ],
        )

    def test_one_off_requires_account_id_before_runtime_initialization(self):
        with patch.object(run_daily_service, "initialize_schema") as initialize_schema:
            with self.assertRaises(SystemExit) as raised:
                run_daily_service.main(["--run-creation-once"])
        self.assertEqual(2, raised.exception.code)
        initialize_schema.assert_not_called()


class RegionDictionaryTest(unittest.TestCase):
    def test_operational_excluded_region_label_keeps_template(self):
        self.assertIsNone(delivery_config.parse_geo_location_config("排地域"))

    def test_checked_in_region_dictionary_is_valid(self):
        path = delivery_config.validate_region_dictionary()
        self.assertEqual("regions_all.json", path.name)

    def test_missing_region_dictionary_fails_closed(self):
        with tempfile.TemporaryDirectory() as temp_dir:
            missing = Path(temp_dir) / "regions_all.json"
            with patch.object(delivery_config, "REGION_DICTIONARY_PATH", missing):
                with self.assertRaisesRegex(RuntimeError, "地域字典缺失"):
                    delivery_config.validate_region_dictionary()


class CreationChatRoutingTest(unittest.TestCase):
    def test_ad_approval_defaults_to_creation_chat(self):
        captured = {}

        async def fake_import(**kwargs):
            captured.update(kwargs)
            return SimpleNamespace(
                metadata={"sheet_token": "token", "url": "url", "im_sent": True}
            )

        with patch.object(
            im_approval_ad_creation, "FEISHU_CREATION_CHAT_ID", "chat-creation"
        ), patch.object(
            im_approval_ad_creation, "import_to_feishu", side_effect=fake_import
        ), patch.object(
            im_approval_ad_creation, "_query_first_sheet_id", return_value="sheet"
        ):
            asyncio.run(
                im_approval_ad_creation.send_ad_approval_to_feishu(Path("ad.xlsx"))
            )
        self.assertEqual("chat-creation", captured["chat_id"])

    def test_creative_approval_defaults_to_creation_chat(self):
        captured = {}

        async def fake_import(**kwargs):
            captured.update(kwargs)
            return SimpleNamespace(
                metadata={"sheet_token": "token", "url": "url", "im_sent": True}
            )

        with patch.object(
            im_approval_creation, "FEISHU_CREATION_CHAT_ID", "chat-creation"
        ), patch.object(
            feishu_doc, "import_to_feishu", side_effect=fake_import
        ), patch.object(
            im_approval_creation, "_query_first_sheet_id", return_value="sheet"
        ):
            asyncio.run(im_approval_creation.send_approval_to_feishu(Path("creative.xlsx")))
        self.assertEqual("chat-creation", captured["chat_id"])

    def test_execution_summary_defaults_to_creation_chat(self):
        response = SimpleNamespace(
            raise_for_status=lambda: None,
            json=lambda: {"code": 0},
        )
        with patch.object(
            execute_creation_apply, "FEISHU_CREATION_CHAT_ID", "chat-creation"
        ), patch.object(
            feishu_doc, "_get_tenant_token", return_value="token"
        ), patch.object(
            feishu_doc, "_auth_headers", return_value={}
        ), patch("httpx.post", return_value=response) as post:
            execute_creation_apply._send_apply_summary_to_feishu(
                {
                    "total": {"approved": 0, "posted_ok": 0, "posted_failed": 0},
                    "records": [],
                }
            )
        self.assertEqual("chat-creation", post.call_args.kwargs["json"]["receive_id"])


if __name__ == "__main__":
    unittest.main()
