"""API routes for the Automation plugin — /api/v1/automation.

Endpoints: automation definitions CRUD, execute, dry-run, runs, versions.
"""

from __future__ import annotations

import logging
import uuid
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.db import get_db, set_tenant_context
from app.deps import get_current_user, require_permission
from app.plugins.builtins.automation.models import (
    AutomationDefinition,
    AutomationRun,
    AutomationVersion,
)
from app.plugins.builtins.automation.schemas import (
    AutomationDefinitionCreate,
    AutomationDefinitionListResponse,
    AutomationDefinitionResponse,
    AutomationDefinitionUpdate,
    AutomationRunListResponse,
    AutomationRunResponse,
    AutomationSettingsResponse,
    AutomationSettingsUpdate,
    AutomationVersionListResponse,
    AutomationVersionResponse,
    MiniAppCreate,
    MiniAppResponse,
)
from app.plugins.builtins.automation.services import (
    AutomationService,
    RunLogService,
)

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/automation", tags=["automation"])


# ─── Helper Functions ───


def _automation_to_response(a: AutomationDefinition) -> AutomationDefinitionResponse:
    """Convert AutomationDefinition model to response schema."""
    return AutomationDefinitionResponse(
        id=str(a.id),
        name=a.name,
        description=a.description or "",
        trigger_type=a.trigger_type,
        trigger_config=a.trigger_config or {},
        conditions=a.conditions or [],
        actions=a.actions or [],
        is_active=a.is_active,
        dry_run=a.dry_run,
        created_by=str(a.created_by) if a.created_by else None,
        created_at=a.created_at.isoformat() if a.created_at else None,
        updated_at=a.updated_at.isoformat() if a.updated_at else None,
    )


def _run_to_response(r: AutomationRun) -> AutomationRunResponse:
    """Convert AutomationRun model to response schema."""
    return AutomationRunResponse(
        id=str(r.id),
        automation_id=str(r.automation_id),
        status=r.status,
        started_at=r.started_at.isoformat() if r.started_at else "",
        completed_at=r.completed_at.isoformat() if r.completed_at else None,
        duration_seconds=r.duration_seconds,
        result=r.result,
        error=r.error,
        trigger_type=r.trigger_type,
        trigger_data=r.trigger_data or {},
        dry_run=r.dry_run,
        created_at=r.created_at.isoformat() if r.created_at else None,
    )


def _version_to_response(v: AutomationVersion) -> AutomationVersionResponse:
    """Convert AutomationVersion model to response schema."""
    return AutomationVersionResponse(
        id=str(v.id),
        automation_id=str(v.automation_id),
        version_number=v.version_number,
        snapshot=v.snapshot or {},
        changed_by=str(v.changed_by) if v.changed_by else None,
        created_at=v.created_at.isoformat() if v.created_at else None,
    )


# ─── CRUD Endpoints ───


@router.get(
    "",
    dependencies=[Depends(require_permission("automation:read"))],
    response_model=AutomationDefinitionListResponse,
)
async def list_automations(
    trigger_type: str | None = Query(None),
    is_active: bool | None = Query(None),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List automation definitions with optional filters."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    items, total = await AutomationService.list(
        db, tenant_id, trigger_type=trigger_type, is_active=is_active,
        limit=limit, offset=offset,
    )
    return AutomationDefinitionListResponse(
        items=[_automation_to_response(a) for a in items],
        total=total,
    )


@router.post(
    "/",
    dependencies=[Depends(require_permission("automation:write"))],
    response_model=AutomationDefinitionResponse,
    status_code=201,
)
async def create_automation(
    data: AutomationDefinitionCreate,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Create a new automation definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])
    automation = await AutomationService.create(
        db, tenant_id, data.model_dump(), user_id=user_id
    )
    return _automation_to_response(automation)


# ─── Recent Runs ───


@router.get(
    "/runs/recent",
    dependencies=[Depends(require_permission("automation:read"))],
    response_model=AutomationRunListResponse,
)
async def list_recent_automation_runs(
    limit: int = Query(20, ge=1, le=100),
    status: str | None = Query(None),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List recent automation runs across all automations (for dashboard)."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    items, total = await RunLogService.list_automation_runs(
        db, tenant_id, automation_id=None, status=status, limit=limit, offset=0
    )
    return AutomationRunListResponse(
        items=[_run_to_response(r) for r in items],
        total=total,
    )


# ─── MiniApps ───


@router.get(
    "/miniapps",
    dependencies=[Depends(require_permission("automation:read"))],
    response_model=list[MiniAppResponse],
)
async def list_miniapps(
    current_user: dict[str, Any] = Depends(get_current_user),
):
    """List custom MiniApps from plugin config."""
    from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry
    registry = MiniAppRegistry()
    return registry.list_apps()


@router.post(
    "/miniapps",
    dependencies=[Depends(require_permission("automation:write"))],
    response_model=MiniAppResponse,
    status_code=201,
)
async def create_miniapp(
    data: MiniAppCreate,
    current_user: dict[str, Any] = Depends(get_current_user),
):
    """Create a custom MiniApp definition."""
    from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry
    registry = MiniAppRegistry()
    registry.register(
        app_id=data.app_id,
        name=data.name,
        icon=data.icon,
        description=data.description,
        plugin_name="automation",
        render_schema=data.render_schema,
    )
    return MiniAppResponse(
        app_id=data.app_id,
        name=data.name,
        icon=data.icon,
        description=data.description,
        plugin_name="automation",
        render_schema=data.render_schema,
    )


@router.delete(
    "/miniapps/{app_id}",
    dependencies=[Depends(require_permission("automation:delete"))],
)
async def delete_miniapp(
    app_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
):
    """Delete a custom MiniApp definition."""
    from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry
    registry = MiniAppRegistry()
    registry.unregister(app_id)
    return {"status": "ok"}


# ─── Settings ───


@router.get(
    "/settings",
    dependencies=[Depends(require_permission("automation:configure"))],
    response_model=AutomationSettingsResponse,
)
async def get_automation_settings(
    current_user: dict[str, Any] = Depends(get_current_user),
):
    """Get automation default settings."""
    # Return default settings (could be stored in plugin config in the future)
    return AutomationSettingsResponse(
        default_llm_model="ollama/deepseek-v4-flash",
        heartbeat_default_interval=300,
        max_concurrent_agents=5,
        log_level="INFO",
    )


@router.patch(
    "/settings",
    dependencies=[Depends(require_permission("automation:configure"))],
    response_model=AutomationSettingsResponse,
)
async def update_automation_settings(
    data: AutomationSettingsUpdate,
    current_user: dict[str, Any] = Depends(get_current_user),
):
    """Update automation settings (stored in plugin config)."""
    # TODO: Persist settings to plugin config table
    # For now, return the updated values as a preview
    settings = AutomationSettingsResponse(
        default_llm_model=data.default_llm_model or "ollama/deepseek-v4-flash",
        heartbeat_default_interval=data.heartbeat_default_interval or 300,
        max_concurrent_agents=data.max_concurrent_agents or 5,
        log_level=data.log_level or "INFO",
    )
    return settings
@router.get(
    "/{automation_id}",
    dependencies=[Depends(require_permission("automation:read"))],
    response_model=AutomationDefinitionResponse,
)
async def get_automation(
    automation_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Get a single automation definition by ID."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    automation = await AutomationService.get_by_id(db, tenant_id, aid)
    if automation is None:
        raise HTTPException(status_code=404, detail="Automation not found")
    return _automation_to_response(automation)


@router.patch(
    "/{automation_id}",
    dependencies=[Depends(require_permission("automation:write"))],
    response_model=AutomationDefinitionResponse,
)
async def update_automation(
    automation_id: str,
    data: AutomationDefinitionUpdate,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Update an existing automation definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    automation = await AutomationService.update(
        db, tenant_id, aid, data.model_dump(exclude_none=True), user_id=user_id
    )
    if automation is None:
        raise HTTPException(status_code=404, detail="Automation not found")
    return _automation_to_response(automation)


@router.delete(
    "/{automation_id}",
    dependencies=[Depends(require_permission("automation:delete"))],
)
async def delete_automation(
    automation_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Delete an automation definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    success = await AutomationService.delete(db, tenant_id, aid)
    if not success:
        raise HTTPException(status_code=404, detail="Automation not found")
    return {"status": "ok"}


# ─── Execute / Dry-Run ───


@router.post(
    "/{automation_id}/execute",
    dependencies=[Depends(require_permission("automation:execute"))],
)
async def execute_automation(
    automation_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Execute an automation definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    automation = await AutomationService.get_by_id(db, tenant_id, aid)
    if automation is None:
        raise HTTPException(status_code=404, detail="Automation not found")
    if not automation.is_active:
        raise HTTPException(status_code=400, detail="Automation is not active")

    # Create run log entry
    run = AutomationRun(
        tenant_id=tenant_id,
        automation_id=aid,
        status="running",
        started_at=__import__("datetime").datetime.now(__import__("datetime").timezone.utc),
        trigger_type="manual",
        trigger_data={"triggered_by": str(uuid.UUID(current_user["user_id"]))},
        dry_run=False,
    )
    db.add(run)
    await db.flush()

    # TODO: Execute automation actions asynchronously
    run.status = "completed"
    run.completed_at = __import__("datetime").datetime.now(__import__("datetime").timezone.utc)
    run.duration_seconds = 0.0
    run.result = "Executed successfully (stub)"
    await db.flush()

    return {"status": "ok", "run_id": str(run.id)}


@router.post(
    "/{automation_id}/dry-run",
    dependencies=[Depends(require_permission("automation:execute"))],
)
async def dry_run_automation(
    automation_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Dry-run an automation definition (validate without executing)."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    automation = await AutomationService.get_by_id(db, tenant_id, aid)
    if automation is None:
        raise HTTPException(status_code=404, detail="Automation not found")

    # Execute dry-run via execution engine
    from app.plugins.builtins.automation.execution_engine import run_automation as execute_automation_engine

    result = await execute_automation_engine(
        ctx={},
        automation_id=str(aid),
        trigger_type="manual",
        trigger_data={
            "triggered_by": str(uuid.UUID(current_user["user_id"])),
            "dry_run": True,
        },
    )

    # Create dry-run log entry
    run = AutomationRun(
        tenant_id=tenant_id,
        automation_id=aid,
        status=result.get("status", "dry_run"),
        started_at=__import__("datetime").datetime.now(__import__("datetime").timezone.utc),
        completed_at=__import__("datetime").datetime.now(__import__("datetime").timezone.utc),
        duration_seconds=0.0,
        trigger_type="manual",
        trigger_data={"triggered_by": str(uuid.UUID(current_user["user_id"])), "dry_run": True},
        dry_run=True,
        result=str(result),
    )
    db.add(run)
    await db.flush()

    return {
        "status": "ok",
        "run_id": str(run.id),
        "automation": _automation_to_response(automation),
        "validation": {
            "valid": True,
            "conditions_count": len(automation.conditions or []),
            "actions_count": len(automation.actions or []),
        },
        "dry_run_result": result,
    }


# ─── Runs ───


@router.get(
    "/{automation_id}/runs",
    dependencies=[Depends(require_permission("automation:read"))],
    response_model=AutomationRunListResponse,
)
async def list_automation_runs(
    automation_id: str,
    status: str | None = Query(None),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List run logs for an automation definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    items, total = await RunLogService.list_automation_runs(
        db, tenant_id, automation_id=aid, status=status, limit=limit, offset=offset
    )
    return AutomationRunListResponse(
        items=[_run_to_response(r) for r in items],
        total=total,
    )


# ─── Versions ───


@router.get(
    "/{automation_id}/versions",
    dependencies=[Depends(require_permission("automation:read"))],
    response_model=AutomationVersionListResponse,
)
async def list_automation_versions(
    automation_id: str,
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """List version history for an automation definition."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        aid = uuid.UUID(automation_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid automation ID")

    items, total = await AutomationService.get_versions(
        db, tenant_id, aid, limit=limit, offset=offset
    )
    return AutomationVersionListResponse(
        items=[_version_to_response(v) for v in items],
        total=total,
    )


@router.post(
    "/{automation_id}/versions/{version_id}/restore",
    dependencies=[Depends(require_permission("automation:write"))],
    response_model=AutomationDefinitionResponse,
)
async def restore_automation_version(
    automation_id: str,
    version_id: str,
    current_user: dict[str, Any] = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Restore an automation definition from a specific version."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    user_id = uuid.UUID(current_user["user_id"])
    try:
        aid = uuid.UUID(automation_id)
        vid = uuid.UUID(version_id)
    except (ValueError, TypeError):
        raise HTTPException(status_code=400, detail="Invalid ID")

    automation = await AutomationService.restore_version(
        db, tenant_id, aid, vid, user_id=user_id
    )
    if automation is None:
        raise HTTPException(status_code=404, detail="Automation or version not found")
    return _automation_to_response(automation)


