# pylint: disable=broad-exception-caught
"""
Temporal Worker Entry Point - Runs a Temporal worker that executes video processing activities and workflows.
"""
import asyncio
import logging
import os
import signal
import sys
from concurrent.futures import ThreadPoolExecutor

from temporalio.client import Client
from temporalio.worker import Worker
from temporalio.worker.workflow_sandbox import SandboxedWorkflowRunner, SandboxRestrictions

from mini_videorag.monitoring.prometheus import create_temporal_runtime_with_metrics
from mini_videorag.processors.factory import ProcessorFactory
from mini_videorag.temporal.activities.processor_activity import ProcessorActivity
from mini_videorag.temporal.workflows.video_pipeline_workflow import VideoPipelineWorkflow
from mini_videorag.temporal.converters import pydantic_data_converter

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)


class TemporalWorker:
    """Temporal worker with proper lifecycle management."""

    def __init__(self):
        """
        Initialize worker configuration.

        Configuration via environment variables:
        - TEMPORAL_URL: Temporal server URL (default: localhost:7233)
        - TASK_QUEUE: Task queue name (default: video-processing)
        - MAX_CONCURRENT_ACTIVITIES: Max parallel activities (default: 10)
        - PROMETHEUS_ENABLED: Enable Prometheus metrics (default: false)
        - PROMETHEUS_PORT: Metrics endpoint port (default: 9091)
        - PROMETHEUS_BIND: Bind address for metrics (default: 0.0.0.0)
        """
        self.temporal_url = os.getenv("TEMPORAL_URL", "localhost:7233")
        self.task_queue = os.getenv("TASK_QUEUE", "video-processing")
        self.max_concurrent_activities = int(os.getenv("MAX_CONCURRENT_ACTIVITIES", "10"))

        # Prometheus configuration
        prometheus_enabled = os.getenv("PROMETHEUS_ENABLED", "false").lower() == "true"
        prometheus_port = int(os.getenv("PROMETHEUS_PORT", "9091"))
        prometheus_bind = os.getenv("PROMETHEUS_BIND", "0.0.0.0")

        self.runtime = create_temporal_runtime_with_metrics(
            enabled=prometheus_enabled,
            port=prometheus_port,
            bind_address=prometheus_bind,
        )

        self.client = None
        self.worker = None
        self.activity_executor = None
        self._shutdown_event = asyncio.Event()

    async def connect(self):
        """
        Connect to Temporal server with optional metrics runtime.

        If Prometheus metrics are enabled, the runtime with metrics will be passed
        to the client connection. Otherwise, the default runtime is used.

        Uses Pydantic data converter for automatic Pydantic model serialization
        across workflow and activity boundaries.

        Returns:
            True if connection successful, False otherwise
        """
        logger.info(f"Connecting to Temporal server at {self.temporal_url}")

        try:
            self.client = await Client.connect(
                self.temporal_url, runtime=self.runtime, data_converter=pydantic_data_converter
            )
            logger.info("Connected to Temporal server with Pydantic data converter")
            return True
        except Exception as e:
            logger.error(f"Failed to connect to Temporal server: {e}", exc_info=True)
            return False

    def initialize_activities(self):
        """Initialize processor factory and activities."""
        logger.info("Initializing ProcessorFactory and activities")

        try:
            factory = ProcessorFactory()

            processor_activity = ProcessorActivity(factory)

            logger.info("ProcessorActivity initialized successfully")
            return [processor_activity.run]

        except Exception as e:
            logger.error(f"Failed to initialize activities: {e}", exc_info=True)
            raise

    def create_sandbox_restrictions(self):
        """
        Create sandbox restrictions for workflow execution.

        Allows passthrough for external libraries that workflows need to import
        but don't actually execute (used only for type hints and dataclasses).
        """
        return SandboxRestrictions.default.with_passthrough_modules(
            "urllib3",
            "botocore",
            "boto3",
            "requests",
            "mini_videorag",
        )

    async def start(self):
        """Start the Temporal worker."""
        if not await self.connect():
            return False

        activities = self.initialize_activities()

        self.activity_executor = ThreadPoolExecutor(
            max_workers=self.max_concurrent_activities, thread_name_prefix="temporal-activity-"
        )
        logger.info(f"Created ThreadPoolExecutor with {self.max_concurrent_activities} workers")

        sandbox_restrictions = self.create_sandbox_restrictions()

        self.worker = Worker(
            self.client,
            task_queue=self.task_queue,
            workflows=[VideoPipelineWorkflow],
            activities=activities,
            activity_executor=self.activity_executor,
            workflow_runner=SandboxedWorkflowRunner(restrictions=sandbox_restrictions),
        )

        logger.info(f"Starting Temporal worker on task queue: {self.task_queue}")
        logger.info(f"Registered workflows: {[VideoPipelineWorkflow.__name__]}")
        logger.info(f"Registered activities: {[a.__name__ for a in activities]}")
        logger.info("Worker is ready to process workflows and activities")

        return True

    async def run(self):
        """Run the worker until shutdown signal."""
        try:
            await self.worker.run()
        except asyncio.CancelledError:
            logger.info("Worker task cancelled")
        except Exception as e:
            logger.error(f"Worker error: {e}", exc_info=True)
            raise

    async def shutdown(self):
        """Shutdown the worker gracefully."""
        logger.info("Shutting down worker...")

        self._shutdown_event.set()

        if self.activity_executor:
            logger.info("Shutting down activity executor...")
            self.activity_executor.shutdown(wait=True, cancel_futures=False)
            logger.info("Activity executor shutdown complete")

        logger.info("Worker shutdown complete")


async def main():
    """Main entry point for worker."""
    worker = TemporalWorker()

    def signal_handler(sig):
        logger.info(f"Received signal {sig}, initiating shutdown...")
        asyncio.create_task(worker.shutdown())

    loop = asyncio.get_event_loop()
    for sig in (signal.SIGTERM, signal.SIGINT):
        loop.add_signal_handler(sig, lambda s=sig: signal_handler(s))

    if not await worker.start():
        logger.error("Failed to start worker")
        return 1

    try:
        await worker.run()
    except KeyboardInterrupt:
        logger.info("Keyboard interrupt received")
    except Exception as e:
        logger.error(f"Worker failed: {e}", exc_info=True)
        return 1
    finally:
        await worker.shutdown()

    return 0


if __name__ == "__main__":
    try:
        exit_code = asyncio.run(main())
        sys.exit(exit_code)
    except KeyboardInterrupt:
        logger.info("Worker stopped by user")
        sys.exit(0)
    except Exception as e:
        logger.error(f"Fatal error: {e}", exc_info=True)
        sys.exit(1)
