"""
Celery tasks for executing scheduled tasks.
"""

from celery import shared_task
from django.utils import timezone
from django.db import transaction
import time
import logging

logger = logging.getLogger(__name__)


@shared_task(bind=True, max_retries=3)
def execute_scheduled_task(self, scheduled_task_id: int):
    """
    Execute a scheduled task by running its configured plugin.

    Args:
        scheduled_task_id: ID of the ScheduledTask to execute

    Returns:
        dict: Execution result
    """
    from .models import ScheduledTask, TaskExecutionLog
    from smoothschedule.scheduling.automations.registry import AutomationExecutionError as PluginExecutionError
    from django.contrib.contenttypes.models import ContentType

    try:
        scheduled_task = ScheduledTask.objects.select_related('created_by').get(
            id=scheduled_task_id
        )
    except ScheduledTask.DoesNotExist:
        logger.error(f"ScheduledTask {scheduled_task_id} not found")
        return {'success': False, 'error': 'Task not found'}

    # Check if task is active
    if scheduled_task.status != ScheduledTask.Status.ACTIVE:
        logger.info(f"Skipping task {scheduled_task.name} - status is {scheduled_task.status}")
        return {'success': False, 'error': 'Task is not active'}

    # Create execution log
    execution_log = TaskExecutionLog.objects.create(
        scheduled_task=scheduled_task,
        status=TaskExecutionLog.Status.SUCCESS,  # Will update if fails
    )

    start_time = time.time()

    try:
        # Get plugin instance
        plugin = scheduled_task.get_plugin_instance()
        if not plugin:
            raise PluginExecutionError(f"Plugin '{scheduled_task.plugin_name}' not found")

        # Get business/tenant context
        # This is multi-tenant aware - the plugin will execute in the context
        # of whichever tenant schema this task belongs to
        from django.db import connection
        business = None
        if hasattr(connection, 'tenant'):
            business = connection.tenant

        # Build execution context
        context = {
            'business': business,
            'scheduled_task': scheduled_task,
            'execution_time': timezone.now(),
            'user': scheduled_task.created_by,
        }

        # Check if plugin can execute
        can_execute, reason = plugin.can_execute(context)
        if not can_execute:
            execution_log.status = TaskExecutionLog.Status.SKIPPED
            execution_log.error_message = reason or "Plugin cannot execute"
            execution_log.save()

            logger.info(f"Skipping task {scheduled_task.name}: {reason}")
            return {'success': False, 'skipped': True, 'reason': reason}

        # Execute plugin
        logger.info(f"Executing task {scheduled_task.name} with plugin {scheduled_task.plugin_name}")
        result = plugin.execute(context)

        # Calculate execution time
        execution_time_ms = int((time.time() - start_time) * 1000)

        # Update execution log
        execution_log.status = TaskExecutionLog.Status.SUCCESS
        execution_log.result = result
        execution_log.completed_at = timezone.now()
        execution_log.execution_time_ms = execution_time_ms
        execution_log.save()

        # Update scheduled task
        with transaction.atomic():
            scheduled_task.last_run_at = timezone.now()
            scheduled_task.last_run_status = 'success'
            scheduled_task.last_run_result = result
            scheduled_task.save()

            # Update next run time
            if scheduled_task.schedule_type != ScheduledTask.ScheduleType.ONE_TIME:
                scheduled_task.update_next_run_time()
            else:
                # One-time tasks get disabled after execution
                scheduled_task.status = ScheduledTask.Status.DISABLED
                scheduled_task.save()

        # Call plugin's success callback
        try:
            plugin.on_success(result)
        except Exception as callback_error:
            logger.error(f"Plugin success callback failed: {callback_error}", exc_info=True)

        logger.info(f"Task {scheduled_task.name} completed successfully in {execution_time_ms}ms")
        return result

    except Exception as error:
        # Calculate execution time
        execution_time_ms = int((time.time() - start_time) * 1000)

        # Update execution log
        execution_log.status = TaskExecutionLog.Status.FAILED
        execution_log.error_message = str(error)
        execution_log.completed_at = timezone.now()
        execution_log.execution_time_ms = execution_time_ms
        execution_log.save()

        # Update scheduled task
        with transaction.atomic():
            scheduled_task.last_run_at = timezone.now()
            scheduled_task.last_run_status = 'failed'
            scheduled_task.last_run_result = {'error': str(error)}
            scheduled_task.save()

            # Still update next run time for recurring tasks
            if scheduled_task.schedule_type != ScheduledTask.ScheduleType.ONE_TIME:
                scheduled_task.update_next_run_time()

        # Call plugin's failure callback
        plugin = scheduled_task.get_plugin_instance()
        if plugin:
            try:
                plugin.on_failure(error)
            except Exception as callback_error:
                logger.error(f"Plugin failure callback failed: {callback_error}", exc_info=True)

        logger.error(f"Task {scheduled_task.name} failed: {error}", exc_info=True)

        # Retry with exponential backoff
        raise self.retry(exc=error, countdown=60 * (2 ** self.request.retries))


@shared_task
def cleanup_old_execution_logs(days_to_keep: int = 30):
    """
    Clean up old task execution logs.

    Args:
        days_to_keep: Keep logs from the last N days (default: 30)

    Returns:
        int: Number of logs deleted
    """
    from .models import TaskExecutionLog
    from datetime import timedelta

    cutoff_date = timezone.now() - timedelta(days=days_to_keep)

    deleted_count, _ = TaskExecutionLog.objects.filter(
        started_at__lt=cutoff_date
    ).delete()

    logger.info(f"Deleted {deleted_count} task execution logs older than {days_to_keep} days")
    return deleted_count


@shared_task
def check_and_schedule_tasks():
    """
    Check for tasks that need to be scheduled and queue them.

    This task runs periodically to find active scheduled tasks
    whose next_run_at is in the past or near future.
    """
    from .models import ScheduledTask
    from datetime import timedelta

    now = timezone.now()
    check_window = now + timedelta(minutes=5)  # Schedule tasks due in next 5 minutes

    tasks_to_run = ScheduledTask.objects.filter(
        status=ScheduledTask.Status.ACTIVE,
        next_run_at__lte=check_window,
        next_run_at__isnull=False,
    )

    scheduled_count = 0
    for task in tasks_to_run:
        # Only schedule if not already past due by too much (prevents backlog)
        if task.next_run_at < now - timedelta(hours=1):
            logger.warning(f"Task {task.name} is overdue by more than 1 hour, skipping")
            task.update_next_run_time()
            continue

        # Schedule the task
        execute_scheduled_task.apply_async(
            args=[task.id],
            eta=task.next_run_at,
        )
        scheduled_count += 1
        logger.info(f"Scheduled task {task.name} to run at {task.next_run_at}")

    return {'scheduled_count': scheduled_count}


@shared_task(bind=True, max_retries=3)
def execute_event_plugin(self, event_plugin_id: int, event_id: int = None):
    """
    Execute a plugin for a specific event at a scheduled time.

    This task is scheduled by django-celery-beat when EventPlugins are created
    with time-based triggers (before_start, at_start, after_start, after_end).

    Args:
        event_plugin_id: ID of the EventPlugin to execute
        event_id: Optional event ID for validation

    Returns:
        dict: Execution result
    """
    from .models import EventPlugin, Event
    from smoothschedule.scheduling.automations.registry import AutomationExecutionError as PluginExecutionError

    start_time = time.time()

    try:
        event_plugin = EventPlugin.objects.select_related(
            'event', 'plugin_installation', 'plugin_installation__template'
        ).get(id=event_plugin_id)
    except EventPlugin.DoesNotExist:
        logger.error(f"EventPlugin {event_plugin_id} not found")
        return {'success': False, 'error': 'EventPlugin not found'}

    # Validate event if provided
    if event_id and event_plugin.event_id != event_id:
        logger.error(f"Event mismatch: expected {event_id}, got {event_plugin.event_id}")
        return {'success': False, 'error': 'Event mismatch'}

    event = event_plugin.event

    # Check if plugin is still active
    if not event_plugin.is_active:
        logger.info(f"Skipping EventPlugin {event_plugin_id} - not active")
        return {'success': False, 'skipped': True, 'reason': 'Plugin not active'}

    # Check if event is in a valid state (not cancelled)
    if event.status == Event.Status.CANCELLED:
        logger.info(f"Skipping EventPlugin {event_plugin_id} - event is cancelled")
        return {'success': False, 'skipped': True, 'reason': 'Event cancelled'}

    plugin_name = event_plugin.plugin_installation.template.name if event_plugin.plugin_installation.template else 'Unknown'

    try:
        # Get the plugin instance from the installation
        plugin_installation = event_plugin.plugin_installation
        plugin = plugin_installation.get_plugin_instance()

        if not plugin:
            raise PluginExecutionError(f"Plugin '{plugin_name}' not found or not loaded")

        # Get business/tenant context
        from django.db import connection
        business = None
        if hasattr(connection, 'tenant'):
            business = connection.tenant

        # Build execution context with event-specific data
        context = {
            'business': business,
            'event': event,
            'event_plugin': event_plugin,
            'trigger': event_plugin.trigger,
            'execution_time': timezone.now(),
            'plugin_installation': plugin_installation,
            # Include participants for the plugin to use
            'participants': list(event.participants.select_related('resource', 'customer').all()),
        }

        # Check if plugin can execute
        can_execute, reason = plugin.can_execute(context)
        if not can_execute:
            logger.info(f"Skipping EventPlugin {event_plugin_id}: {reason}")
            return {'success': False, 'skipped': True, 'reason': reason}

        # Execute plugin
        logger.info(f"Executing EventPlugin {event_plugin_id} ({plugin_name}) for event '{event.title}'")
        result = plugin.execute(context)

        execution_time_ms = int((time.time() - start_time) * 1000)

        # Call success callback
        try:
            plugin.on_success(result)
        except Exception as callback_error:
            logger.error(f"Plugin success callback failed: {callback_error}", exc_info=True)

        logger.info(f"EventPlugin {event_plugin_id} completed successfully in {execution_time_ms}ms")
        return {
            'success': True,
            'result': result,
            'execution_time_ms': execution_time_ms,
            'event_id': event.id,
            'plugin_name': plugin_name,
        }

    except Exception as error:
        execution_time_ms = int((time.time() - start_time) * 1000)

        # Call failure callback if plugin exists
        try:
            plugin_installation = event_plugin.plugin_installation
            plugin = plugin_installation.get_plugin_instance()
            if plugin:
                plugin.on_failure(error)
        except Exception as callback_error:
            logger.error(f"Plugin failure callback failed: {callback_error}", exc_info=True)

        logger.error(f"EventPlugin {event_plugin_id} failed: {error}", exc_info=True)

        # Retry with exponential backoff
        raise self.retry(exc=error, countdown=60 * (2 ** self.request.retries))


def cancel_event_plugin_task(event_plugin_id: int):
    """
    Cancel a scheduled Celery task for an EventPlugin.

    This is called when:
    - An EventPlugin is deleted
    - An EventPlugin is deactivated
    - An Event is cancelled/deleted

    Args:
        event_plugin_id: ID of the EventPlugin whose task should be cancelled
    """
    try:
        from django_celery_beat.models import PeriodicTask

        task_name = f"event_plugin_{event_plugin_id}"

        deleted_count, _ = PeriodicTask.objects.filter(name=task_name).delete()

        if deleted_count > 0:
            logger.info(f"Cancelled Celery task '{task_name}'")
        else:
            logger.debug(f"No Celery task found for '{task_name}'")

        return deleted_count > 0

    except ImportError:
        logger.warning("django-celery-beat not installed, cannot cancel task")
        return False
    except Exception as e:
        logger.error(f"Failed to cancel event plugin task: {e}")
        return False


def cancel_event_tasks(event_id: int):
    """
    Cancel all scheduled Celery tasks for an event.

    Called when an event is deleted or cancelled.

    Args:
        event_id: ID of the Event whose plugin tasks should be cancelled
    """
    from .models import EventPlugin

    event_plugins = EventPlugin.objects.filter(event_id=event_id)
    cancelled_count = 0

    for ep in event_plugins:
        if cancel_event_plugin_task(ep.id):
            cancelled_count += 1

    logger.info(f"Cancelled {cancelled_count} Celery tasks for event {event_id}")
    return cancelled_count


@shared_task
def reseed_demo_tenant():
    """
    Daily reseed of demo tenant for sales demonstrations.

    Runs at midnight UTC via Celery beat to keep demo appointments fresh.
    """
    from django.core.management import call_command

    logger.info("Starting daily demo tenant reseed...")
    try:
        call_command('reseed_demo', '--quiet')
        logger.info("Demo tenant reseed completed successfully")
        return {'success': True}
    except Exception as e:
        logger.error(f"Demo tenant reseed failed: {e}")
        return {'success': False, 'error': str(e)}
