"""Automation & Agents plugin — Agent Builder, Automation Builder, Cron-Scheduler, Agent Runner.

Houses the core automation engine: AI agent definitions, event/schedule/manual
workflow automations, cron job scheduling, and execution logging.

Supports plugin contributions: when other plugins are activated, their
agent_definitions, automation_templates, cron_jobs, and heartbeat_configs
from the manifest are registered. On deactivation, they are removed.
"""

from __future__ import annotations

import logging
from typing import Any

from app.plugins.base import BasePlugin
from app.plugins.manifest import (
    FrontendMenuItem,
    FrontendPageRoute,
    FrontendSettingsPage,
    PluginManifest,
    PluginRouteDef,
)

logger = logging.getLogger(__name__)


class AutomationPlugin(BasePlugin):
    """Automation & Agents plugin — Agent Builder, Automation Builder, Cron-Scheduler, Agent Runner."""

    manifest = PluginManifest(
        name="automation",
        version="1.0.0",
        display_name="Automation & Agents",
        description=(
            "Agent Builder, Automation Builder, Cron-Scheduler, and Agent Runner. "
            "Define AI agents with LLM models and tools, create event/schedule/manual "
            "automations with conditions and actions, schedule cron jobs, and track execution logs."
        ),
        dependencies=[],
        routes=[
            PluginRouteDef(
                path="/api/v1/automation",
                module="app.plugins.builtins.automation.routes",
                router_attr="router",
            ),
            PluginRouteDef(
                path="/api/v1/agents",
                module="app.plugins.builtins.automation.agent_routes",
                router_attr="router",
            ),
        ],
        events=[
            "contact.created",
            "contact.updated",
            "mail.received",
            "workflow.timeout",
        ],
        migrations=["0001_initial.sql"],
        permissions=[
            "automation:read",
            "automation:write",
            "automation:delete",
            "automation:execute",
            "automation:configure",
            "agents:read",
            "agents:write",
            "agents:delete",
            "agents:execute",
        ],
        is_core=False,
        menu_items=[
            FrontendMenuItem(
                label_key="nav.automation",
                label="Automation",
                path="/automation",
                icon="Zap",
                order=50,
            ),
            FrontendMenuItem(
                label_key="nav.agents",
                label="Agents",
                path="/agents",
                icon="Bot",
                order=51,
            ),
        ],
        page_routes=[
            FrontendPageRoute(
                path="/automation",
                component="@/pages/AutomationDashboard",
                order=50,
            ),
            FrontendPageRoute(
                path="/agents",
                component="@/pages/AgentDashboard",
                order=51,
            ),
        ],
        settings_pages=[
            FrontendSettingsPage(
                path="automation",
                label_key="settings.automation",
                label="Automation",
                component="@/pages/AutomationSettings",
                icon="Settings",
                order=60,
            ),
        ],
    )

    def __init__(self) -> None:
        super().__init__()
        # Track contributed definitions by source plugin name
        self._contributed_agents: dict[str, list[str]] = {}  # plugin_name -> [agent_name, ...]
        self._contributed_automations: dict[str, list[str]] = {}  # plugin_name -> [automation_name, ...]
        self._contributed_cron_jobs: dict[str, list[str]] = {}  # plugin_name -> [cron_job_name, ...]
        self._contributed_heartbeats: dict[str, list[str]] = {}  # plugin_name -> [agent_name, ...]

    async def on_activate(self, db, service_container, event_bus) -> None:
        """Register event listeners on activation."""
        await super().on_activate(db, service_container, event_bus)
        # Register agent communication tool
        try:
            from app.plugins.builtins.automation.agent_comm import register_agent_comm_tool
            register_agent_comm_tool()
        except Exception:
            logger.exception("Failed to register agent communication tool")
        # Register MiniApps from manifest
        try:
            from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry
            registry = MiniAppRegistry()
            for miniapp in self.manifest.miniapps:
                registry.register(
                    app_id=miniapp.app_id,
                    name=miniapp.name,
                    icon=miniapp.icon,
                    description=miniapp.description,
                    plugin_name=self.manifest.name,
                    render_schema=miniapp.render_schema,
                )
                logger.info("Registered MiniApp '%s' from manifest", miniapp.app_id)
        except Exception:
            logger.exception("Failed to register MiniApps from manifest")
        logger.info("Automation plugin activated")

    async def on_deactivate(self, db, service_container, event_bus) -> None:
        """Clean up on deactivation."""
        await super().on_deactivate(db, service_container, event_bus)
        # Unregister agent communication tool
        try:
            from app.plugins.builtins.automation.agent_comm import unregister_agent_comm_tool
            unregister_agent_comm_tool()
        except Exception:
            logger.exception("Failed to unregister agent communication tool")
        # Unregister MiniApps
        try:
            from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry
            registry = MiniAppRegistry()
            registry.unregister_plugin(self.manifest.name)
            logger.info("Unregistered MiniApps for plugin '%s'", self.manifest.name)
        except Exception:
            logger.exception("Failed to unregister MiniApps")
        logger.info("Automation plugin deactivated")

    # ─── Plugin Contribution Registration ───

    async def register_plugin_contributions(self, db, plugin_name: str, manifest) -> None:
        """Register agent definitions, automation templates, cron jobs, and heartbeat configs
        from another plugin's manifest. Uses plugin name prefixing for conflict resolution."""
        from app.plugins.builtins.automation.services import AgentService, AutomationService, CronJobService
        from app.plugins.builtins.automation.models import AutomationCronJob
        from sqlalchemy import select

        # Register agent definitions
        agent_names: list[str] = []
        for agent_def in manifest.agent_definitions:
            prefixed_name = f"{plugin_name}.{agent_def.name}"
            agent_names.append(prefixed_name)
            # Check if already exists (idempotent)
            existing = await AgentService.get_by_name(db, agent_def.tenant_id, prefixed_name) if hasattr(AgentService, 'get_by_name') else None
            if existing is None:
                try:
                    await AgentService.create(db, agent_def.tenant_id, {
                        "name": prefixed_name,
                        "description": agent_def.description,
                        "llm_model": agent_def.llm_model,
                        "system_prompt": agent_def.system_prompt,
                        "tool_ids": agent_def.tool_ids,
                        "heartbeat_interval_seconds": agent_def.heartbeat_interval_seconds,
                        "mode": agent_def.mode,
                        "max_executions_per_hour": agent_def.max_executions_per_hour,
                        "max_duration_seconds": agent_def.max_duration_seconds,
                        "budget_limit_usd": agent_def.budget_limit_usd,
                        "is_active": True,
                    })
                    logger.info("Registered contributed agent '%s' from plugin '%s'", prefixed_name, plugin_name)
                except Exception:
                    logger.exception("Failed to register contributed agent '%s' from plugin '%s'", prefixed_name, plugin_name)
        self._contributed_agents[plugin_name] = agent_names

        # Register automation templates
        automation_names: list[str] = []
        for auto_def in manifest.automation_templates:
            prefixed_name = f"{plugin_name}.{auto_def.name}"
            automation_names.append(prefixed_name)
            existing = await AutomationService.get_by_name(db, auto_def.tenant_id, prefixed_name) if hasattr(AutomationService, 'get_by_name') else None
            if existing is None:
                try:
                    await AutomationService.create(db, auto_def.tenant_id, {
                        "name": prefixed_name,
                        "description": auto_def.description,
                        "trigger_type": auto_def.trigger_type,
                        "trigger_config": auto_def.trigger_config,
                        "conditions": auto_def.conditions,
                        "actions": auto_def.actions,
                        "is_active": True,
                    })
                    logger.info("Registered contributed automation '%s' from plugin '%s'", prefixed_name, plugin_name)
                except Exception:
                    logger.exception("Failed to register contributed automation '%s' from plugin '%s'", prefixed_name, plugin_name)
        self._contributed_automations[plugin_name] = automation_names

        # Register cron jobs
        cron_job_names: list[str] = []
        for cron_def in manifest.cron_jobs:
            prefixed_name = f"{plugin_name}.{cron_def.name}"
            cron_job_names.append(prefixed_name)
            try:
                # Check if cron job already exists
                result = await db.execute(
                    select(AutomationCronJob).where(AutomationCronJob.name == prefixed_name).limit(1)
                )
                existing = result.scalar_one_or_none()
                if existing is None:
                    await CronJobService.create(db, cron_def.tenant_id, {
                        "name": prefixed_name,
                        "cron_expression": cron_def.cron_expression,
                        "job_type": cron_def.job_type,
                        "target_name": cron_def.target_name,
                        "plugin_name": plugin_name,
                        "is_active": True,
                    })
                    logger.info("Registered contributed cron job '%s' from plugin '%s'", prefixed_name, plugin_name)
            except Exception:
                logger.exception("Failed to register contributed cron job '%s' from plugin '%s'", prefixed_name, plugin_name)
        self._contributed_cron_jobs[plugin_name] = cron_job_names

        # Register heartbeat configs
        heartbeat_names: list[str] = []
        for hb_def in manifest.heartbeat_configs:
            prefixed_name = f"{plugin_name}.{hb_def.agent_name}"
            heartbeat_names.append(prefixed_name)
            try:
                # Create or update agent with heartbeat settings
                agent = await AgentService.get_by_name(db, hb_def.tenant_id, prefixed_name) if hasattr(AgentService, 'get_by_name') else None
                if agent is None:
                    await AgentService.create(db, hb_def.tenant_id, {
                        "name": prefixed_name,
                        "description": f"Heartbeat agent contributed by {plugin_name}",
                        "heartbeat_interval_seconds": hb_def.interval_seconds,
                        "mode": "proactive",
                        "is_active": True,
                    })
                    logger.info("Registered heartbeat agent '%s' from plugin '%s'", prefixed_name, plugin_name)
            except Exception:
                logger.exception("Failed to register heartbeat agent '%s' from plugin '%s'", prefixed_name, plugin_name)
        self._contributed_heartbeats[plugin_name] = heartbeat_names

    async def unregister_plugin_contributions(self, db, plugin_name: str) -> None:
        """Remove all contributed definitions from a plugin that is being deactivated."""
        from app.plugins.builtins.automation.services import AgentService, AutomationService, CronJobService

        # Remove contributed agents
        agent_names = self._contributed_agents.pop(plugin_name, [])
        for agent_name in agent_names:
            try:
                agent = await AgentService.get_by_name(db, agent_name)
                if agent:
                    await AgentService.delete(db, agent.tenant_id, agent.id)
                    logger.info("Unregistered contributed agent '%s' from plugin '%s'", agent_name, plugin_name)
            except Exception:
                logger.exception("Failed to unregister contributed agent '%s'", agent_name)

        # Remove contributed automations
        automation_names = self._contributed_automations.pop(plugin_name, [])
        for auto_name in automation_names:
            try:
                auto = await AutomationService.get_by_name(db, auto_name)
                if auto:
                    await AutomationService.delete(db, auto.tenant_id, auto.id)
                    logger.info("Unregistered contributed automation '%s' from plugin '%s'", auto_name, plugin_name)
            except Exception:
                logger.exception("Failed to unregister contributed automation '%s'", auto_name)

        # Remove contributed cron jobs
        cron_job_names = self._contributed_cron_jobs.pop(plugin_name, [])
        for cron_name in cron_job_names:
            try:
                from app.plugins.builtins.automation.models import AutomationCronJob
                from sqlalchemy import select
                result = await db.execute(
                    select(AutomationCronJob).where(AutomationCronJob.name == cron_name).limit(1)
                )
                job = result.scalar_one_or_none()
                if job:
                    await CronJobService.delete(db, job.tenant_id, job.id)
                    logger.info("Unregistered contributed cron job '%s' from plugin '%s'", cron_name, plugin_name)
            except Exception:
                logger.exception("Failed to unregister contributed cron job '%s'", cron_name)

        # Remove contributed heartbeats
        heartbeat_names = self._contributed_heartbeats.pop(plugin_name, [])
        for hb_name in heartbeat_names:
            try:
                agent = await AgentService.get_by_name(db, hb_name)
                if agent:
                    await AgentService.delete(db, agent.tenant_id, agent.id)
                    logger.info("Unregistered heartbeat agent '%s' from plugin '%s'", hb_name, plugin_name)
            except Exception:
                logger.exception("Failed to unregister heartbeat agent '%s'", hb_name)

    # ─── Heartbeat Migration ───

    async def ensure_ai_proactive_heartbeat(self, db) -> None:
        """Migrate the hardcoded ai_proactive heartbeat to a configurable cron job."""
        from app.plugins.builtins.automation.models import AutomationCronJob
        from app.plugins.builtins.automation.services import CronJobService
        from sqlalchemy import select

        # Check if ai_proactive heartbeat cron job already exists
        result = await db.execute(
            select(AutomationCronJob).where(
                AutomationCronJob.name == "ai_proactive.heartbeat"
            ).limit(1)
        )
        existing = result.scalar_one_or_none()
        if existing is not None:
            logger.info("ai_proactive heartbeat cron job already exists, skipping migration")
            return

        # Create the heartbeat cron job
        try:
            await CronJobService.create(db, None, {
                "name": "ai_proactive.heartbeat",
                "cron_expression": "*/5 * * * *",  # Every 5 minutes
                "job_type": "agent_heartbeat",
                "target_name": "ai_proactive.heartbeat_agent",
                "plugin_name": "ai_proactive",
                "is_active": True,
            })
            logger.info("Created ai_proactive heartbeat cron job (every 5 minutes)")
        except Exception:
            logger.exception("Failed to create ai_proactive heartbeat cron job")

    # ─── Event Handlers ───

    async def on_contact_created(self, payload: dict[str, Any]) -> None:
        """Handle contact.created event — trigger matching automations."""
        logger.debug("contact.created event received: %s", payload)

    async def on_contact_updated(self, payload: dict[str, Any]) -> None:
        """Handle contact.updated event — trigger matching automations."""
        logger.debug("contact.updated event received: %s", payload)

    async def on_mail_received(self, payload: dict[str, Any]) -> None:
        """Handle mail.received event — trigger matching automations."""
        logger.debug("mail.received event received: %s", payload)

    async def on_workflow_timeout(self, payload: dict[str, Any]) -> None:
        """Handle workflow.timeout event — trigger matching automations."""
        logger.debug("workflow.timeout event received: %s", payload)