"""
Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Example job classes for queue management.
"""

import json
import os
import random
import time
import asyncio

import requests

from mcp_proxy_adapter.integrations.queuemgr_integration import QueueJobBase
from mcp_proxy_adapter.commands.command_registry import registry


class DataProcessingJob(QueueJobBase):
    """Example data processing job."""

    def run(self) -> None:
        """Execute data processing job."""
        self.logger.info(
            f"DataProcessingJob {self.job_id}: Starting data processing"
        )

        # Simulate processing
        data = self.mcp_params.get("data", {})
        operation = self.mcp_params.get("operation", "process")

        time.sleep(2)  # Simulate work

        result = {
            "job_id": self.job_id,
            "operation": operation,
            "processed_at": time.time(),
            "data_size": len(json.dumps(data)),
            "status": "completed",
        }

        self.set_mcp_result(result)


class FileOperationJob(QueueJobBase):
    """Example file operation job."""

    def run(self) -> None:
        """Execute file operation job."""
        self.logger.info(
            f"FileOperationJob {self.job_id}: Starting file operation"
        )

        file_path = self.mcp_params.get("file_path", "")
        operation = self.mcp_params.get("operation", "read")

        try:
            if operation == "read" and os.path.exists(file_path):
                with open(file_path, "r") as f:
                    content = f.read()

                result = {
                    "job_id": self.job_id,
                    "operation": operation,
                    "file_path": file_path,
                    "file_size": len(content),
                    "status": "completed",
                }
            else:
                result = {
                    "job_id": self.job_id,
                    "operation": operation,
                    "file_path": file_path,
                    "error": f"File not found or invalid operation: {operation}",
                    "status": "failed",
                }

            self.set_mcp_result(result, result["status"])

        except Exception as e:
            self.set_mcp_error(f"File operation failed: {str(e)}")


class ApiCallJob(QueueJobBase):
    """Example API call job."""

    def run(self) -> None:
        """Execute API call job."""
        self.logger.info(f"ApiCallJob {self.job_id}: Starting API call")

        url = self.mcp_params.get("url", "")
        method = self.mcp_params.get("method", "GET")
        headers = self.mcp_params.get("headers", {})
        timeout = self.mcp_params.get("timeout", 30)

        try:
            response = requests.request(
                method=method, url=url, headers=headers, timeout=timeout
            )

            result = {
                "job_id": self.job_id,
                "url": url,
                "method": method,
                "status_code": response.status_code,
                "response_size": len(response.content),
                "status": "completed",
            }

            self.set_mcp_result(result)

        except Exception as e:
            self.set_mcp_error(f"API call failed: {str(e)}")


class CustomJob(QueueJobBase):
    """Example custom job."""

    def run(self) -> None:
        """Execute custom job."""
        self.logger.info(f"CustomJob {self.job_id}: Starting custom job")

        # Custom job logic here
        time.sleep(1)  # Simulate work

        result = {
            "job_id": self.job_id,
            "custom_data": self.mcp_params.get("custom_data", {}),
            "status": "completed",
        }

        self.set_mcp_result(result)


class LongRunningJob(QueueJobBase):
    """Example long-running job with progress updates."""

    def run(self) -> None:
        """Execute long-running job with progress updates."""
        self.logger.info(
            f"LongRunningJob {self.job_id}: Starting long-running job"
        )

        duration = self.mcp_params.get("duration", 10)  # Default 10 seconds
        task_type = self.mcp_params.get("task_type", "data_processing")

        self.set_status("running")
        self.set_description(f"Processing {task_type} task...")

        # Simulate long-running work with progress updates
        for i in range(duration):
            # Update progress
            progress = int((i + 1) / duration * 100)
            self.set_progress(progress)
            self.set_description(
                f"Processing {task_type} task... {progress}% complete"
            )

            # Simulate work
            time.sleep(1)

            # Simulate occasional errors (5% chance)
            if random.random() < 0.05:
                self.set_mcp_error(f"Simulated error at {progress}%", "failed")
                return

        # Complete successfully
        result = {
            "job_id": self.job_id,
            "task_type": task_type,
            "duration": duration,
            "completed_at": time.time(),
            "status": "completed",
        }

        self.set_mcp_result(result)


class BatchProcessingJob(QueueJobBase):
    """Example batch processing job."""

    def run(self) -> None:
        """Execute batch processing job."""
        self.logger.info(
            f"BatchProcessingJob {self.job_id}: Starting batch processing"
        )

        batch_size = self.mcp_params.get("batch_size", 100)
        items = self.mcp_params.get("items", [])

        self.set_status("running")
        self.set_description(f"Processing batch of {len(items)} items...")

        processed_items = []

        for i, item in enumerate(items):
            # Update progress
            progress = int((i + 1) / len(items) * 100)
            self.set_progress(progress)
            self.set_description(
                f"Processing item {i+1}/{len(items)}... {progress}% complete"
            )

            # Simulate processing each item
            time.sleep(0.1)  # 100ms per item

            # Simulate processing result
            processed_item = {
                "original": item,
                "processed": f"processed_{item}",
                "timestamp": time.time(),
            }
            processed_items.append(processed_item)

            # Simulate occasional processing errors (2% chance)
            if random.random() < 0.02:
                self.set_mcp_error(
                    f"Processing failed at item {i+1}: {item}", "failed"
                )
                return

        # Complete successfully
        result = {
            "job_id": self.job_id,
            "batch_size": batch_size,
            "processed_count": len(processed_items),
            "processed_items": processed_items,
            "completed_at": time.time(),
            "status": "completed",
        }

        self.set_mcp_result(result)


class PeriodicLoggingJob(QueueJobBase):
    """Job that writes messages to stdout every minute for testing log retrieval."""

    def run(self) -> None:
        """Execute periodic logging job."""
        import sys
        from datetime import datetime

        self.logger.info(
            f"PeriodicLoggingJob {self.job_id}: Starting periodic logging"
        )

        # Get parameters
        duration_minutes = self.mcp_params.get("duration_minutes", 5)
        message_prefix = self.mcp_params.get("message_prefix", "Log message")
        interval_seconds = self.mcp_params.get("interval_seconds", 60)

        # Write initial message
        start_time = datetime.now()
        print(f"[{start_time.strftime('%Y-%m-%d %H:%M:%S')}] {message_prefix} - Job started", file=sys.stdout, flush=True)
        print(f"[{start_time.strftime('%Y-%m-%d %H:%M:%S')}] Job ID: {self.job_id}", file=sys.stdout, flush=True)
        print(f"[{start_time.strftime('%Y-%m-%d %H:%M:%S')}] Duration: {duration_minutes} minutes, Interval: {interval_seconds} seconds", file=sys.stdout, flush=True)

        # Write periodic messages
        message_count = 0
        end_time = start_time.timestamp() + (duration_minutes * 60)

        while time.time() < end_time:
            current_time = datetime.now()
            message_count += 1
            message = f"[{current_time.strftime('%Y-%m-%d %H:%M:%S')}] {message_prefix} #{message_count} - Job {self.job_id} is running"
            print(message, file=sys.stdout, flush=True)
            
            # Also write to stderr occasionally (every 3rd message)
            if message_count % 3 == 0:
                error_message = f"[{current_time.strftime('%Y-%m-%d %H:%M:%S')}] DEBUG: Message #{message_count} (stderr output)"
                print(error_message, file=sys.stderr, flush=True)

            # Update progress
            elapsed = time.time() - start_time.timestamp()
            progress = int((elapsed / (duration_minutes * 60)) * 100)
            self.set_progress(min(progress, 99))
            self.set_description(f"Logging messages... {message_count} messages sent")

            # Wait for next interval
            time.sleep(interval_seconds)

        # Write final message
        end_time_obj = datetime.now()
        print(f"[{end_time_obj.strftime('%Y-%m-%d %H:%M:%S')}] {message_prefix} - Job completed", file=sys.stdout, flush=True)
        print(f"[{end_time_obj.strftime('%Y-%m-%d %H:%M:%S')}] Total messages: {message_count}", file=sys.stdout, flush=True)

        result = {
            "job_id": self.job_id,
            "started_at": start_time.isoformat(),
            "completed_at": end_time_obj.isoformat(),
            "duration_minutes": duration_minutes,
            "interval_seconds": interval_seconds,
            "total_messages": message_count,
            "status": "completed",
        }

        self.set_progress(100)
        self.set_description(f"Completed: {message_count} messages logged")
        self.set_mcp_result(result)


class CommandExecutionJob(QueueJobBase):
    """Job that executes any MCP command in the queue."""

    def run(self) -> None:
        """Execute command in queue with progress tracking."""
        import asyncio
        from mcp_proxy_adapter.commands.command_registry import registry
        
        self.logger.info(
            f"CommandExecutionJob {self.job_id}: Starting command execution"
        )
        
        command_name = self.mcp_params.get("command")
        command_params = self.mcp_params.get("params", {}) or {}
        context = self.mcp_params.get("context", {})
        
        if not command_name:
            self.set_mcp_error("Command name is required")
            return
        
        try:
            # Get command class
            command_class = registry.get_command(command_name)
            
            # Set initial status
            self.set_status("running")
            self.set_description(f"Executing command: {command_name}")
            self.set_progress(0)
            
            # Execute command asynchronously with context and progress tracking
            async def _execute_with_progress():
                # Check if command has duration parameter for progress tracking
                duration = command_params.get("duration", 0)
                steps = command_params.get("steps", 10)
                
                if duration > 0 and steps > 0:
                    # Long-running command with progress updates
                    # Execute command in background and update progress in parallel
                    self.set_progress(5)
                    self.set_description(f"Starting {command_name}...")
                    
                    # Create task for command execution
                    command_task = asyncio.create_task(
                        command_class.run(**command_params, context=context)
                    )
                    
                    # Update progress while command is running
                    step_duration = max(0.1, duration / steps)
                    start_time = asyncio.get_event_loop().time()
                    
                    for i in range(steps):
                        # Wait a bit
                        await asyncio.sleep(step_duration)
                        
                        # Calculate progress based on time elapsed
                        elapsed = asyncio.get_event_loop().time() - start_time
                        progress = min(90, int((elapsed / duration) * 90))  # Leave 10% for completion
                        
                        self.set_progress(progress)
                        self.set_description(
                            f"Executing {command_name}: step {i+1}/{steps} ({progress}%)"
                        )
                        
                        # Check if command is done
                        if command_task.done():
                            break
                    
                    # Wait for command to complete
                    self.set_progress(95)
                    self.set_description(f"Finalizing {command_name}...")
                    result = await command_task
                    self.set_progress(100)
                    self.set_description(f"Command {command_name} completed")
                    return result
                else:
                    # Regular command execution
                    self.set_progress(50)
                    self.set_description(f"Executing command: {command_name}")
                    result = await command_class.run(**command_params, context=context)
                    self.set_progress(100)
                    self.set_description(f"Command {command_name} completed")
                    return result
            
            # Run in event loop - handle both cases
            try:
                # Try to get existing event loop
                loop = asyncio.get_running_loop()
                # If we're in an async context, we need to use a different approach
                # Create a new task in the existing loop
                import concurrent.futures
                with concurrent.futures.ThreadPoolExecutor() as executor:
                    future = executor.submit(
                        lambda: asyncio.run(_execute_with_progress())
                    )
                    result_obj = future.result(timeout=300)  # 5 minute timeout
            except RuntimeError:
                # No event loop running, create new one
                result_obj = asyncio.run(_execute_with_progress())
            
            result_dict = result_obj.to_dict() if hasattr(result_obj, "to_dict") else {"result": str(result_obj)}
            
            self.set_mcp_result({
                "job_id": self.job_id,
                "command": command_name,
                "result": result_dict,
                "status": "completed",
            })
        except Exception as e:
            self.logger.exception(f"Command execution failed: {e}")
            self.set_mcp_error(f"Command execution failed: {str(e)}")


class FileDownloadJob(QueueJobBase):
    """Example file download job with progress tracking."""

    def run(self) -> None:
        """Execute file download job."""
        self.logger.info(
            f"FileDownloadJob {self.job_id}: Starting file download"
        )

        url = self.mcp_params.get("url", "https://example.com/file.zip")
        file_size = self.mcp_params.get(
            "file_size", 1024 * 1024
        )  # Default 1MB

        self.set_status("running")
        self.set_description(f"Downloading {url}...")

        # Simulate download with progress updates
        downloaded = 0
        chunk_size = 64 * 1024  # 64KB chunks

        while downloaded < file_size:
            # Simulate download chunk
            chunk = min(chunk_size, file_size - downloaded)
            time.sleep(0.1)  # Simulate network delay

            downloaded += chunk
            progress = int(downloaded / file_size * 100)

            self.set_progress(progress)
            self.set_description(
                f"Downloading {url}... {progress}% complete "
                f"({downloaded}/{file_size} bytes)"
            )

            # Simulate occasional network errors (3% chance)
            if random.random() < 0.03:
                self.set_mcp_error(
                    f"Network error during download at {progress}%", "failed"
                )
                return

        # Complete successfully
        result = {
            "job_id": self.job_id,
            "url": url,
            "file_size": file_size,
            "downloaded_bytes": downloaded,
            "completed_at": time.time(),
            "status": "completed",
        }

        self.set_mcp_result(result)

