from datetime import date
from unittest.mock import patch

from django.contrib.admin.sites import AdminSite
from django.test import TestCase

from basic_info import models as basic_models
from settlement import handlers
from settlement.admin import DailySettlementConfigAdmin
from settlement.models import (
    DailySettlementConfig,
    NotificationChannelEnum,
    SettlementModuleEnum,
)
from settlement.tasks import run_daily_settlement, run_merchant_daily_settlement


class SettlementTasksTestCase(TestCase):
    def setUp(self):
        self.merchant = basic_models.Merchant.objects.create(
            name='任务测试商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        self.config = DailySettlementConfig.objects.create(
            merchant=self.merchant,
            settlement_modules=[SettlementModuleEnum.PLATE_ORDER],
            notification_enabled=False,
            notification_channel=NotificationChannelEnum.NONE,
        )

    @patch('settlement.tasks.run_merchant_daily_settlement.delay')
    @patch('settlement.tasks.timezone.localdate')
    def test_run_daily_settlement_dispatches_only_configured_merchants(
        self,
        mock_localdate,
        mock_delay,
    ):
        mock_localdate.return_value = date(2026, 2, 9)

        merchant_without_modules = basic_models.Merchant.objects.create(
            name='无模块商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        DailySettlementConfig.objects.create(
            merchant=merchant_without_modules,
            settlement_modules=[],
            notification_enabled=False,
            notification_channel=NotificationChannelEnum.NONE,
        )

        result = run_daily_settlement.run()

        self.assertEqual(result['total_merchants'], 1)
        self.assertEqual(result['settlement_date'], '2026-02-08')
        mock_delay.assert_called_once_with(
            merchant_id=self.merchant.id,
            settlement_date='2026-02-08',
        )

    @patch('settlement.tasks.daily_settlement_completed.send')
    @patch('settlement.tasks.calculate_printing_order_daily_summary')
    @patch('settlement.tasks.calculate_plate_order_daily_summary')
    def test_run_merchant_daily_settlement_success(
        self,
        mock_plate_summary,
        mock_printing_summary,
        mock_signal_send,
    ):
        self.config.settlement_modules = [
            SettlementModuleEnum.PLATE_ORDER,
            SettlementModuleEnum.PRINTING_ORDER,
        ]
        self.config.save(update_fields=['settlement_modules'])

        mock_plate_summary.return_value = {'total_orders': 1}
        mock_printing_summary.return_value = {'total_orders': 2}

        result = run_merchant_daily_settlement.run(
            merchant_id=self.merchant.id,
            settlement_date='2026-02-08',
        )

        self.assertEqual(result['merchant_id'], self.merchant.id)
        self.assertEqual(result['settlement_date'], '2026-02-08')
        self.assertEqual(result['status'], 'success')
        self.assertEqual(result['errors'], {})
        mock_plate_summary.assert_called_once()
        mock_printing_summary.assert_called_once()
        mock_signal_send.assert_called_once()

    @patch('settlement.tasks.daily_settlement_completed.send')
    @patch('settlement.tasks.calculate_printing_order_daily_summary')
    @patch('settlement.tasks.calculate_plate_order_daily_summary')
    def test_run_merchant_daily_settlement_partial_when_one_module_fails(
        self,
        mock_plate_summary,
        mock_printing_summary,
        mock_signal_send,
    ):
        self.config.settlement_modules = [
            SettlementModuleEnum.PLATE_ORDER,
            SettlementModuleEnum.PRINTING_ORDER,
        ]
        self.config.save(update_fields=['settlement_modules'])

        mock_plate_summary.side_effect = RuntimeError('plate failed')
        mock_printing_summary.return_value = {'total_orders': 2}

        result = run_merchant_daily_settlement.run(
            merchant_id=self.merchant.id,
            settlement_date='2026-02-08',
        )

        self.assertEqual(result['status'], 'partial')
        self.assertIn(SettlementModuleEnum.PLATE_ORDER, result['errors'])
        self.assertEqual(result['errors'][SettlementModuleEnum.PLATE_ORDER], 'plate failed')
        mock_signal_send.assert_called_once()

    @patch('settlement.tasks.daily_settlement_completed.send')
    @patch('settlement.tasks.calculate_plate_order_daily_summary')
    def test_run_merchant_daily_settlement_failed_when_all_modules_fail(
        self,
        mock_plate_summary,
        mock_signal_send,
    ):
        self.config.settlement_modules = [SettlementModuleEnum.PLATE_ORDER]
        self.config.save(update_fields=['settlement_modules'])

        mock_plate_summary.side_effect = RuntimeError('plate failed')

        result = run_merchant_daily_settlement.run(
            merchant_id=self.merchant.id,
            settlement_date='2026-02-08',
        )

        self.assertEqual(result['status'], 'failed')
        self.assertIn(SettlementModuleEnum.PLATE_ORDER, result['errors'])
        mock_signal_send.assert_called_once()

    @patch('settlement.tasks.daily_settlement_completed.send')
    @patch('settlement.tasks.calculate_plate_order_daily_summary')
    @patch('settlement.tasks.timezone.localdate')
    def test_run_merchant_daily_settlement_uses_yesterday_when_no_date(
        self,
        mock_localdate,
        mock_plate_summary,
        mock_signal_send,
    ):
        mock_localdate.return_value = date(2026, 2, 9)
        self.config.settlement_modules = [SettlementModuleEnum.PLATE_ORDER]
        self.config.save(update_fields=['settlement_modules'])
        mock_plate_summary.return_value = {'total_orders': 1}

        result = run_merchant_daily_settlement.run(merchant_id=self.merchant.id)

        self.assertEqual(result['settlement_date'], '2026-02-08')
        mock_signal_send.assert_called_once()


class SettlementHandlersTestCase(TestCase):
    def setUp(self):
        self.merchant = basic_models.Merchant.objects.create(
            name='处理器测试商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )

    @patch('settlement.handlers.logger.info')
    def test_handler_logs_when_notification_disabled(self, mock_logger_info):
        DailySettlementConfig.objects.create(
            merchant=self.merchant,
            settlement_modules=[SettlementModuleEnum.PLATE_ORDER],
            notification_enabled=False,
            notification_channel=NotificationChannelEnum.NONE,
        )

        handlers.on_daily_settlement_completed(
            sender=self.__class__,
            merchant=self.merchant,
            settlement_date=date(2026, 2, 8),
            status='success',
            modules=[SettlementModuleEnum.PLATE_ORDER],
            errors={},
            task_id='task-1',
        )

        self.assertTrue(mock_logger_info.called)

    @patch('settlement.handlers.logger.info')
    def test_handler_logs_wecom_when_notification_enabled(self, mock_logger_info):
        DailySettlementConfig.objects.create(
            merchant=self.merchant,
            settlement_modules=[SettlementModuleEnum.PLATE_ORDER],
            notification_enabled=True,
            notification_channel=NotificationChannelEnum.WECOM,
        )

        handlers.on_daily_settlement_completed(
            sender=self.__class__,
            merchant=self.merchant,
            settlement_date=date(2026, 2, 8),
            status='success',
            modules=[SettlementModuleEnum.PLATE_ORDER],
            errors={},
            task_id='task-2',
        )

        self.assertTrue(mock_logger_info.called)


class SettlementAdminTestCase(TestCase):
    def test_get_readonly_fields(self):
        admin_obj = DailySettlementConfigAdmin(
            DailySettlementConfig,
            admin_site=AdminSite(),
        )

        self.assertEqual(admin_obj.get_readonly_fields(request=None, obj=None), [])

        merchant = basic_models.Merchant.objects.create(
            name='后台测试商户',
            type=basic_models.MerchantTypeEnum.FACTORY,
        )
        config = DailySettlementConfig.objects.create(
            merchant=merchant,
            settlement_modules=[SettlementModuleEnum.PLATE_ORDER],
            notification_enabled=False,
            notification_channel=NotificationChannelEnum.NONE,
        )
        self.assertEqual(admin_obj.get_readonly_fields(request=None, obj=config), ['merchant'])
