"""
批次作業測試 (6.4)
測試各批次作業的邏輯
"""
import datetime
import pytest
from unittest.mock import patch, MagicMock


class TestBatchLog:
    """BatchLog model 測試"""

    def test_create_batch_log(self, db):
        """成功建立批次日誌"""
        from app.models.batch_log import BatchLog
        log = BatchLog(
            batch_name="test_batch",
            status="success",
            message="測試批次執行成功",
            started_at=datetime.datetime.utcnow(),
            finished_at=datetime.datetime.utcnow(),
            duration_seconds=5,
        )
        db.add(log)
        db.commit()
        db.refresh(log)

        assert log.id is not None
        assert log.batch_name == "test_batch"
        assert log.status == "success"

    def test_batch_log_repr(self, db):
        """BatchLog repr 格式"""
        from app.models.batch_log import BatchLog
        log = BatchLog(
            batch_name="daily_quota_check",
            status="success",
            started_at=datetime.datetime(2026, 2, 18, 2, 0, 0),
        )
        db.add(log)
        db.commit()
        assert "daily_quota_check" in repr(log)
        assert "success" in repr(log)


class TestDailyQuotaCheck:
    """每日配額檢查批次測試"""

    def test_run_with_empty_db(self, db):
        """空資料庫時應能正常執行"""
        # side_effect 使每次呼叫 get_db() 都回傳新 iter，避免 iterator 用盡問題
        with patch("app.db.session.get_db", side_effect=lambda: iter([db])):
            from app.batch.daily_quota_check import run_daily_quota_check
            result = run_daily_quota_check()

        assert result["status"] == "success"
        assert result["summary"]["email_checked"] == 0
        assert result["summary"]["drive_checked"] == 0

    def test_run_with_email_accounts(self, db, sample_employee):
        """有郵件帳號時應正確計數"""
        from app.models.email_account import EmailAccount
        # 建立測試郵件帳號
        account = EmailAccount(
            tenant_id=1,
            employee_id=sample_employee.id,
            email_address="test.user@porscheworld.tw",
            quota_mb=5120,
            is_active=True,
        )
        db.add(account)
        db.commit()

        with patch("app.db.session.get_db", side_effect=lambda: iter([db])):
            from app.batch.daily_quota_check import run_daily_quota_check
            result = run_daily_quota_check()

        assert result["status"] == "success"
        assert result["summary"]["email_checked"] == 1


class TestSyncKeycloakUsers:
    """Keycloak 同步批次測試"""

    def test_run_with_no_keycloak_connection(self, db, sample_employee):
        """Keycloak 無法連線時，batch 應以 failed 狀態記錄"""
        with patch("app.db.session.get_db", side_effect=lambda: iter([db])):
            with patch("app.services.keycloak_admin_client.get_keycloak_admin_client") as mock_kc:
                # 模擬 Keycloak 連線失敗
                mock_client = MagicMock()
                mock_client.get_user_by_username.return_value = None
                mock_kc.return_value = mock_client

                from app.batch.sync_keycloak_users import run_sync_keycloak_users
                result = run_sync_keycloak_users()

        # 員工在 Keycloak 中不存在 → 記為 not_found_in_keycloak
        assert result["status"] == "success"
        assert result["summary"]["not_found_in_keycloak"] >= 0

    def test_sync_disabled_employee(self, db, sample_employee):
        """離職員工應同步停用 Keycloak"""
        # 將員工設為離職
        sample_employee.status = "terminated"
        db.commit()

        with patch("app.db.session.get_db", side_effect=lambda: iter([db])):
            with patch("app.services.keycloak_admin_client.get_keycloak_admin_client") as mock_kc:
                mock_client = MagicMock()
                # 模擬 Keycloak 中帳號仍是 enabled
                mock_client.get_user_by_username.return_value = {
                    "id": "kc-uuid-123",
                    "enabled": True,
                }
                mock_client.update_user.return_value = True
                mock_kc.return_value = mock_client

                from app.batch.sync_keycloak_users import run_sync_keycloak_users
                result = run_sync_keycloak_users()

        # 應同步停用
        assert result["summary"]["synced"] == 1
        mock_client.update_user.assert_called_once_with(
            "kc-uuid-123", {"enabled": False}
        )


class TestArchiveAuditLogs:
    """審計日誌歸檔批次測試"""

    def test_archive_no_old_logs(self, db):
        """沒有舊日誌時應正常執行"""
        with patch("app.db.session.get_db", side_effect=lambda: iter([db])):
            from app.batch.archive_audit_logs import run_archive_audit_logs
            result = run_archive_audit_logs(dry_run=True)

        assert result["status"] == "success"
        assert result["archived"] == 0

    def test_archive_old_logs_dry_run(self, db, sample_employee):
        """dry_run 模式下不刪除資料"""
        from app.models.audit_log import AuditLog
        import json
        # 建立 91 天前的舊日誌
        old_date = datetime.datetime.utcnow() - datetime.timedelta(days=91)
        for i in range(3):
            log = AuditLog(
                tenant_id=1,
                action="test_action",
                resource_type="employee",
                resource_id=sample_employee.id,
                performed_by="test_system",
                performed_at=old_date,
                details=json.dumps({"test": i}),
            )
            db.add(log)
        db.commit()

        # 確認有 3 筆舊日誌
        count_before = db.query(AuditLog).count()
        assert count_before == 3

        with patch("app.db.session.get_db", side_effect=lambda: iter([db])):
            with patch("os.makedirs"):
                with patch("builtins.open", MagicMock()):
                    with patch("csv.DictWriter") as mock_writer:
                        mock_writer.return_value.__enter__ = MagicMock()
                        mock_writer.return_value.__exit__ = MagicMock()
                        from app.batch.archive_audit_logs import run_archive_audit_logs
                        result = run_archive_audit_logs(dry_run=True)

        # dry_run 模式下不刪除
        assert result["archived"] == 3
        assert result["deleted"] == 0
