"""
track_batch 模組測試

測試批量操作相關的 Ticket 操作：batch-claim, batch-complete
"""

from typing import Dict, Any, List
from unittest.mock import Mock, patch

import pytest

# 導入 track_batch 模組中的函式
from ticket_system.commands.track_batch import (
    execute_batch_claim,
    execute_batch_complete,
)


class TestBatchClaim:
    """批量認領 Ticket 測試"""

    def test_batch_claim_all_success(self):
        """
        Given: 多個 pending 狀態的 Ticket，以逗號分隔
        When: 執行 batch-claim 操作
        Then: 應返回 0，所有 Ticket 都更改為 in_progress
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001,0.31.0-W4-002,0.31.0-W4-003"
        args.version = "0.31.0"

        # 使用正確的 patch 路徑（patch 模組導入的位置）
        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load_validate:
            # 使用 lambda 函式作為 side_effect，正確處理 (version, ticket_id, auto_print_error) 參數
            def load_side_effect(version, ticket_id, auto_print_error=False):
                return ({"id": ticket_id, "status": "pending"}, None)

            mock_load_validate.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                result = execute_batch_claim(args, "0.31.0")

                assert result == 0
                # 應保存 3 個 Ticket
                assert mock_save.call_count == 3

    def test_batch_claim_partial_failure(self):
        """
        Given: 多個 Ticket，其中一些已被認領
        When: 執行 batch-claim 操作
        Then: 應返回 0（有成功的項目），但顯示失敗的 Ticket ID
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001,0.31.0-W4-002"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            # 使用 lambda 函式作為 side_effect，正確處理 (version, ticket_id) 參數
            def load_side_effect(version, ticket_id, auto_print_error=False):
                if ticket_id == "0.31.0-W4-001":
                    return ({"id": ticket_id, "status": "in_progress"}, None)  # Already claimed
                return ({"id": ticket_id, "status": "pending"}, None)

            mock_load.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket'):
                result = execute_batch_claim(args, "0.31.0")

                # 由於有一個成功的項目，返回 0（實現邏輯：success_count > 0）
                assert result == 0

    def test_batch_claim_empty_list(self):
        """
        Given: 空的 Ticket ID 列表
        When: 執行 batch-claim 操作
        Then: 應返回 2（業務拒絕：無有效 ID 屬用戶輸入錯誤）

        依 .claude/references/cli-exit-code-rules.md 規則 2：
        用戶輸入錯誤路徑（含空列表）均為業務拒絕（return 2）。
        """
        args = Mock()
        args.ticket_ids = ""
        args.version = "0.31.0"

        result = execute_batch_claim(args, "0.31.0")

        assert result == 2

    def test_batch_claim_with_whitespace(self):
        """
        Given: Ticket ID 列表包含空白字符
        When: 執行 batch-claim 操作
        Then: 應正確解析並處理空白字符
        """
        args = Mock()
        args.ticket_ids = " 0.31.0-W4-001 , 0.31.0-W4-002 , 0.31.0-W4-003 "
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            # 使用 lambda 函式作為 side_effect，正確處理 (version, ticket_id) 參數
            def load_side_effect(version, ticket_id, auto_print_error=False):
                return ({"id": ticket_id, "status": "pending"}, None)

            mock_load.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket'):
                result = execute_batch_claim(args, "0.31.0")

                assert result == 0


class TestBatchComplete:
    """批量完成 Ticket 測試"""

    def test_batch_complete_all_success(self):
        """
        Given: 多個 in_progress 狀態的 Ticket，且所有驗收條件完成
        When: 執行 batch-complete 操作
        Then: 應返回 0，所有 Ticket 都更改為 completed
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001,0.31.0-W4-002"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            # 使用 lambda 函式作為 side_effect，正確處理 (version, ticket_id) 參數
            def load_side_effect(version, ticket_id, auto_print_error=False):
                return (
                    {
                        "id": ticket_id,
                        "status": "in_progress",
                        "acceptance": ["[x] 完成項 1", "[x] 完成項 2"],
                    },
                    None
                )

            mock_load.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                result = execute_batch_complete(args, "0.31.0")

                assert result == 0
                assert mock_save.call_count == 2

    def test_batch_complete_with_incomplete_criteria_failure(self):
        """
        Given: 多個 Ticket，其中一些驗收條件未完成
        When: 執行 batch-complete 操作
        Then: 應返回 0（有成功的項目），但顯示無法完成的 Ticket
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001,0.31.0-W4-002"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            # 使用 lambda 函式作為 side_effect，正確處理 (version, ticket_id) 參數
            def load_side_effect(version, ticket_id, auto_print_error=False):
                if ticket_id == "0.31.0-W4-001":
                    return (
                        {
                            "id": ticket_id,
                            "status": "in_progress",
                            "acceptance": ["[x] 完成項"],
                        },
                        None
                    )
                # 第二個 Ticket 有未完成的驗收條件
                return (
                    {
                        "id": ticket_id,
                        "status": "in_progress",
                        "acceptance": ["[ ] 未完成項"],
                    },
                    None
                )

            mock_load.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket'):
                result = execute_batch_complete(args, "0.31.0")

                # 由於有一個成功的項目，返回 0（實現邏輯：success_count > 0）
                assert result == 0

    def test_batch_complete_nonexistent_ticket(self):
        """
        Given: Ticket ID 中存在不存在的 ID
        When: 執行 batch-complete 操作
        Then: 應返回 0（有成功的項目），但提示無法找到該 Ticket
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001,0.31.0-W4-999"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            def load_side_effect(version, ticket_id, auto_print_error=False):
                if ticket_id == "0.31.0-W4-999":
                    return (None, "Not found")  # 返回 error message
                return (
                    {
                        "id": ticket_id,
                        "status": "in_progress",
                        "acceptance": ["[x] 完成項"],
                    },
                    None
                )

            mock_load.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket'):
                result = execute_batch_complete(args, "0.31.0")

                # 由於有一個成功的項目，返回 0（實現邏輯：success_count > 0）
                assert result == 0

    def test_batch_complete_single_ticket(self):
        """
        Given: 只有一個 Ticket 的列表
        When: 執行 batch-complete 操作
        Then: 應返回 0，完成該 Ticket
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            mock_ticket = {
                "id": "0.31.0-W4-001",
                "status": "in_progress",
                "acceptance": ["[x] 完成項"],
            }
            mock_load.return_value = (mock_ticket, None)

            with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                result = execute_batch_complete(args, "0.31.0")

                assert result == 0
                mock_save.assert_called_once()

    def test_batch_complete_invalid_ticket_ids_format(self):
        """
        Given: Ticket ID 格式不正確
        When: 執行 batch-complete 操作
        Then: 應返回 2（業務拒絕：無 ticket 處理成功）

        依 cli-exit-code-rules.md 規則 2：批次操作全失敗屬用戶輸入錯誤 → 2。
        當前實作（track_batch.py:361）：success_count > 0 → 0，否則 → 2。
        """
        args = Mock()
        args.ticket_ids = "invalid-id-1,invalid-id-2"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.save_ticket'):
            result = execute_batch_complete(args, "0.31.0")

            # 全部 ID 無效 → success_count=0 → return 2
            assert result == 2

    def test_batch_complete_duplicate_ids(self):
        """
        Given: Ticket ID 列表中有重複 ID
        When: 執行 batch-complete 操作
        Then: 應只完成一次，或返回警告
        """
        args = Mock()
        args.ticket_ids = "0.31.0-W4-001,0.31.0-W4-001,0.31.0-W4-002"
        args.version = "0.31.0"

        with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
            # 使用 lambda 函式作為 side_effect，正確處理 (version, ticket_id) 參數
            def load_side_effect(version, ticket_id, auto_print_error=False):
                return (
                    {
                        "id": ticket_id,
                        "status": "in_progress",
                        "acceptance": ["[x] 完成項"],
                    },
                    None
                )

            mock_load.side_effect = load_side_effect

            with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                result = execute_batch_complete(args, "0.31.0")

                # 應該處理重複 ID（會嘗試保存 3 次）
                assert result == 0


class TestBatchCompleteEnhanced:
    """批量完成增強功能測試（--wave, --parent, --dry-run）"""

    def test_batch_complete_by_wave(self):
        """
        Given: 使用 --wave 參數指定 Wave 28
        When: 執行 batch-complete --wave 28
        Then: 應蒐集並完成所有 wave=28 且 status="in_progress" 的 Ticket
        """
        args = Mock()
        args.ticket_ids = ""  # 未提供逗號分隔 ID
        args.wave = 28
        args.status = "in_progress"
        args.dry_run = False
        args.parent = None

        # Mock list_tickets 回傳符合條件的 Ticket
        mock_tickets = [
            {"id": "0.31.0-W28-001", "wave": 28, "status": "in_progress", "acceptance": ["[x] 完成"]},
            {"id": "0.31.0-W28-002", "wave": 28, "status": "in_progress", "acceptance": ["[x] 完成"]},
            {"id": "0.31.0-W27-001", "wave": 27, "status": "in_progress", "acceptance": ["[x] 完成"]},  # 不符合
        ]

        with patch('ticket_system.commands.track_batch.list_tickets') as mock_list:
            mock_list.return_value = mock_tickets

            with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
                def load_side_effect(version, ticket_id, auto_print_error=False):
                    for ticket in mock_tickets:
                        if ticket["id"] == ticket_id:
                            return (ticket, None)
                    return (None, "Not found")

                mock_load.side_effect = load_side_effect

                with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                    result = execute_batch_complete(args, "0.31.0")

                    assert result == 0
                    # 應該只保存 2 個（符合 wave=28 的）
                    assert mock_save.call_count == 2

    def test_batch_complete_by_parent(self):
        """
        Given: 使用 --parent 參數指定父任務 ID
        When: 執行 batch-complete --parent 0.31.0-W28-001
        Then: 應蒐集並完成該父任務的所有子任務
        """
        args = Mock()
        args.ticket_ids = ""
        args.wave = None
        args.parent = "0.31.0-W28-001"
        args.dry_run = False

        # Mock list_tickets 回傳包含子任務的列表
        mock_tickets = [
            {"id": "0.31.0-W28-001", "parent": None, "status": "in_progress", "acceptance": ["[x]"]},
            {"id": "0.31.0-W28-001.1", "parent": "0.31.0-W28-001", "status": "in_progress", "acceptance": ["[x]"]},
            {"id": "0.31.0-W28-001.2", "parent": "0.31.0-W28-001", "status": "in_progress", "acceptance": ["[x]"]},
            {"id": "0.31.0-W28-002.1", "parent": "0.31.0-W28-002", "status": "in_progress", "acceptance": ["[x]"]},
        ]

        with patch('ticket_system.commands.track_batch.list_tickets') as mock_list:
            mock_list.return_value = mock_tickets

            with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
                def load_side_effect(version, ticket_id, auto_print_error=False):
                    for ticket in mock_tickets:
                        if ticket["id"] == ticket_id:
                            return (ticket, None)
                    return (None, "Not found")

                mock_load.side_effect = load_side_effect

                with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                    result = execute_batch_complete(args, "0.31.0")

                    assert result == 0
                    # 應該只保存 2 個子任務
                    assert mock_save.call_count == 2

    def test_batch_complete_by_parent_with_chain_format(self):
        """
        Given: 使用 --parent 參數指定父任務 ID，子任務使用新格式（chain.parent）
        When: 執行 batch-complete --parent 0.31.1-W1-001
        Then: 應正確識別新格式的子任務並完成
        """
        args = Mock()
        args.ticket_ids = ""
        args.wave = None
        args.parent = "0.31.1-W1-001"
        args.dry_run = False

        # Mock list_tickets 回傳包含新格式子任務的列表
        mock_tickets = [
            {"id": "0.31.1-W1-001", "parent": None, "status": "in_progress", "acceptance": ["[x]"]},
            # 新格式：parent 在 chain 中，而不是頂層
            {
                "id": "0.31.1-W1-001.1",
                "parent": None,
                "chain": {"parent": "0.31.1-W1-001", "root": "0.31.1-W1-001"},
                "status": "in_progress",
                "acceptance": ["[x]"]
            },
            {
                "id": "0.31.1-W1-001.2",
                "parent": None,
                "chain": {"parent": "0.31.1-W1-001", "root": "0.31.1-W1-001"},
                "status": "in_progress",
                "acceptance": ["[x]"]
            },
            # 無關的任務（不同父任務）
            {"id": "0.31.1-W1-002.1", "parent": None, "chain": {"parent": "0.31.1-W1-002", "root": "0.31.1-W1-002"}, "status": "in_progress", "acceptance": ["[x]"]},
        ]

        with patch('ticket_system.commands.track_batch.list_tickets') as mock_list:
            mock_list.return_value = mock_tickets

            with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
                def load_side_effect(version, ticket_id, auto_print_error=False):
                    for ticket in mock_tickets:
                        if ticket["id"] == ticket_id:
                            return (ticket, None)
                    return (None, "Not found")

                mock_load.side_effect = load_side_effect

                with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                    result = execute_batch_complete(args, "0.31.1")

                    assert result == 0
                    # 應該只保存 2 個子任務（0.31.1-W1-001.1 和 0.31.1-W1-001.2）
                    assert mock_save.call_count == 2

    def test_batch_complete_dry_run(self):
        """
        Given: 使用 --dry-run 參數
        When: 執行 batch-complete --wave 28 --dry-run
        Then: 應只顯示清單，不實際更改任何 Ticket
        """
        args = Mock()
        args.ticket_ids = ""
        args.wave = 28
        args.status = "in_progress"
        args.dry_run = True
        args.parent = None

        mock_tickets = [
            {"id": "0.31.0-W28-001", "title": "測試 Ticket 1", "wave": 28, "status": "in_progress"},
            {"id": "0.31.0-W28-002", "title": "測試 Ticket 2", "wave": 28, "status": "in_progress"},
        ]

        with patch('ticket_system.commands.track_batch.list_tickets') as mock_list:
            mock_list.return_value = mock_tickets

            with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
                def load_side_effect(version, ticket_id, auto_print_error=False):
                    for ticket in mock_tickets:
                        if ticket["id"] == ticket_id:
                            return (ticket, None)
                    return (None, "Not found")

                mock_load.side_effect = load_side_effect

                with patch('ticket_system.commands.track_batch.save_ticket') as mock_save:
                    result = execute_batch_complete(args, "0.31.0")

                    # Dry-run 應返回 0 但不保存
                    assert result == 0
                    assert mock_save.call_count == 0

    def test_batch_complete_no_tickets_found(self):
        """
        Given: 使用 --wave 參數但找不到符合條件的 Ticket
        When: 執行 batch-complete --wave 99
        Then: 應返回 2（業務拒絕：搜尋條件下無匹配 Ticket）

        依 cli-exit-code-rules.md 規則 2：搜尋無結果屬用戶輸入錯誤路徑 → 2。
        """
        args = Mock()
        args.ticket_ids = ""
        args.wave = 99  # 不存在的 Wave
        args.status = "in_progress"
        args.dry_run = False
        args.parent = None

        mock_tickets = [
            {"id": "0.31.0-W28-001", "wave": 28, "status": "in_progress"},
        ]

        with patch('ticket_system.commands.track_batch.list_tickets') as mock_list:
            mock_list.return_value = mock_tickets

            with patch('ticket_system.commands.track_batch.load_and_validate_ticket') as mock_load:
                mock_load.return_value = None

                with patch('ticket_system.commands.track_batch.save_ticket'):
                    result = execute_batch_complete(args, "0.31.0")

                    assert result == 2
