"""
Tests for django_cronjob_utils decorators.
"""
from datetime import date
from django.test import TestCase
from django_cronjob_utils.decorators import register_task
from django_cronjob_utils.base import CronTask
from django_cronjob_utils.registry import TaskRegistry, ExecutionPattern
from django_cronjob_utils.exceptions import TaskNotFoundError


class DecoratorTests(TestCase):
    """Tests for @register_task decorator."""
    
    def setUp(self):
        """Clear registry before each test."""
        TaskRegistry._tasks.clear()
        TaskRegistry._codes.clear()
        TaskRegistry._configs.clear()
    
    def test_register_task_decorator(self):
        """Test @register_task decorator."""
        @register_task('test-task', 'A001')
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False, 'message': 'Success'}
        
        self.assertTrue(TaskRegistry.is_registered('test-task'))
        self.assertEqual(TestTask.task_name, 'test-task')
        self.assertEqual(TestTask.task_code, 'A001')
    
    def test_register_task_with_config(self):
        """Test @register_task with configuration."""
        @register_task(
            'test-task',
            'A001',
            execution_pattern=ExecutionPattern.ALWAYS,
            retry_on_failure=True,
            max_retries=3,
            timeout=3600
        )
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False, 'message': 'Success'}
        
        self.assertEqual(TestTask.execution_pattern, ExecutionPattern.ALWAYS)
        self.assertTrue(TestTask.retry_on_failure)
        self.assertEqual(TestTask.max_retries, 3)
        self.assertEqual(TestTask.timeout, 3600)
    
    def test_register_task_multiple_tasks(self):
        """Test registering multiple tasks with decorator."""
        @register_task('task-1', 'A001')
        class Task1(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        @register_task('task-2', 'A002')
        class Task2(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        self.assertEqual(len(TaskRegistry.list_tasks()), 2)
        self.assertIn('task-1', TaskRegistry.list_tasks())
        self.assertIn('task-2', TaskRegistry.list_tasks())
    
    def test_register_task_decorator_preserves_class(self):
        """Test decorator preserves class functionality."""
        @register_task('test-task', 'A001')
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False, 'message': 'Success'}
        
        # Class should still be instantiable
        task = TestTask(execution_date=date(2024, 1, 15))
        self.assertEqual(task.execution_date, date(2024, 1, 15))
        self.assertEqual(task.task_name, 'test-task')
        self.assertEqual(task.task_code, 'A001')
    
    def test_register_task_duplicate_name(self):
        """Test decorator raises error on duplicate name."""
        @register_task('test-task', 'A001')
        class Task1(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        with self.assertRaises(ValueError):
            @register_task('test-task', 'A002')
            class Task2(CronTask):
                def execute(self, date: date) -> dict:
                    return {'error': False}
    
    def test_register_task_duplicate_code(self):
        """Test decorator raises error on duplicate code."""
        @register_task('task-1', 'A001')
        class Task1(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        with self.assertRaises(ValueError):
            @register_task('task-2', 'A001')
            class Task2(CronTask):
                def execute(self, date: date) -> dict:
                    return {'error': False}
    
    def test_register_task_default_config(self):
        """Test decorator applies default configuration."""
        @register_task('test-task', 'A001')
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        self.assertEqual(TestTask.execution_pattern, ExecutionPattern.STANDARD)
        self.assertFalse(TestTask.retry_on_failure)
        self.assertEqual(TestTask.max_retries, 0)
        self.assertEqual(TestTask.retry_delay, 300)
        self.assertIsNone(TestTask.timeout)
    
    def test_register_task_partial_config(self):
        """Test decorator with partial configuration."""
        @register_task('test-task', 'A001', execution_pattern=ExecutionPattern.RERUN_ON_FAILURE)
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        self.assertEqual(TestTask.execution_pattern, ExecutionPattern.RERUN_ON_FAILURE)
        # Should use defaults for others
        self.assertFalse(TestTask.retry_on_failure)
        self.assertEqual(TestTask.max_retries, 0)
    
    def test_register_task_all_patterns(self):
        """Test decorator with all execution patterns."""
        patterns = [
            ExecutionPattern.STANDARD,
            ExecutionPattern.ALWAYS,
            ExecutionPattern.RERUN_ON_FAILURE,
            ExecutionPattern.RATE_LIMITED
        ]
        
        for i, pattern in enumerate(patterns):
            task_name = f'task-{i}'
            task_code = f'A00{i}'
            
            @register_task(task_name, task_code, execution_pattern=pattern)
            class TestTask(CronTask):
                def execute(self, date: date) -> dict:
                    return {'error': False}
            
            self.assertEqual(TestTask.execution_pattern, pattern)
    
    def test_register_task_retry_config(self):
        """Test decorator with retry configuration."""
        @register_task(
            'test-task',
            'A001',
            retry_on_failure=True,
            max_retries=5,
            retry_delay=600
        )
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        self.assertTrue(TestTask.retry_on_failure)
        self.assertEqual(TestTask.max_retries, 5)
        self.assertEqual(TestTask.retry_delay, 600)
    
    def test_register_task_timeout_config(self):
        """Test decorator with timeout configuration."""
        @register_task('test-task', 'A001', timeout=7200)
        class TestTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        self.assertEqual(TestTask.timeout, 7200)
    
    def test_register_task_class_inheritance(self):
        """Test decorator works with class inheritance."""
        class BaseTask(CronTask):
            def execute(self, date: date) -> dict:
                return {'error': False}
        
        @register_task('test-task', 'A001')
        class DerivedTask(BaseTask):
            pass
        
        self.assertTrue(TaskRegistry.is_registered('test-task'))
        self.assertEqual(DerivedTask.task_name, 'test-task')
        self.assertEqual(DerivedTask.task_code, 'A001')
