"""Shared helpers for the dashboard tickets API."""

from __future__ import annotations

from collections.abc import Callable
from typing import Any

import structlog

from proxym.dashboard.tools._helpers import _job_snapshot
from proxym.dashboard.tickets import JobExecution, Ticket, TicketManager, TicketStatus

logger = structlog.get_logger()


def _prepare_environment_snapshot(
    tool_def: Any,
    instance: str,
    get_tool_environment_snapshot: Callable[[str], dict[str, object] | None],
) -> dict[str, object]:
    environment_snapshot = dict(get_tool_environment_snapshot(tool_def.name) or {})
    if instance:
        environment_snapshot["tool_instance_name"] = instance
        tool_instance = tool_def.get_instance(instance)
        if tool_instance:
            environment_snapshot["tool_instance"] = tool_instance.summary()
    return environment_snapshot


def _resolve_project_dir(
    ticket: Ticket,
    environment_snapshot: dict[str, object] | None,
    project_dir: str,
    default_project_dir: Callable[[str | None], str],
) -> str:
    # Priority: explicit > ticket's project > tool's environment > fallback
    return (
        project_dir
        or default_project_dir(ticket.project_id)
        or (environment_snapshot.get("path") if environment_snapshot else "")
    )


def build_job_execution(job: Any, environment_snapshot: dict[str, object] | None) -> JobExecution:
    env = environment_snapshot or {}
    raw_tool_instance = env.get("tool_instance")
    tool_instance = raw_tool_instance if isinstance(raw_tool_instance, dict) else {}
    return JobExecution(
        job_id=job.id,
        tool=job.tool_name,
        mode=job.mode,
        model=job.model,
        project_dir=job.project_dir,
        environment_id=str(env.get("id", "")),
        environment_name=str(env.get("name", "")),
        environment_kind=str(env.get("kind", "")),
        environment_target=str(env.get("target", "")),
        environment_tool_instance_name=str(env.get("tool_instance_name", "")),
        environment_tool_instance=tool_instance,
        prompt_excerpt=job.prompt[:400].strip(),
        prompt_length=len(job.prompt),
        status=job.status.value,
        success=None,
        exit_code=None,
    )


def build_ticket_detail(ticket_store: TicketManager, ticket: Ticket, tool_executor_get: Callable[[], ToolExecutor]) -> dict[str, Any]:
    result = ticket_store.ticket_detail(ticket)
    result["job_history"] = [
        _job_snapshot(job) for job in tool_executor_get().get_jobs_for_ticket(ticket.id)
    ]
    return result


def build_ticket_export_rows(
    ticket_store: TicketManager,
    tickets: list[Ticket],
    *,
    detail: bool,
    view: str,
    tool_executor_get: Callable[[], ToolExecutor],
) -> list[dict[str, Any]]:
    rows = []
    for ticket in tickets:
        row = ticket_store.ticket_detail(ticket) if detail else ticket_store.ticket_overview(ticket)
        if detail:
            row["job_history"] = [
                _job_snapshot(job) for job in tool_executor_get().get_jobs_for_ticket(ticket.id)
            ]
        if view == "board":
            row["board_column"] = row.get("status", "")
        rows.append(row)
    return rows


def _ticket_has_active_job(ticket_id: str, tool_executor_get: Callable[[], Any]) -> bool:
    from proxym.tools.executor import JobStatus
    active_statuses = {JobStatus.QUEUED, JobStatus.RUNNING}
    return any(
        job.status in active_statuses for job in tool_executor_get().get_jobs_for_ticket(ticket_id)
    )


async def enqueue_ticket_job(
    ticket: Ticket,
    tool_def: Any,
    *,
    mode: str = "",
    model: str = "",
    instance: str = "",
    project_dir: str = "",
    ticket_store: TicketManager,
    tool_executor_get: Callable[[], Any],
    get_tool_environment_snapshot: Callable[[str], dict[str, object] | None],
    default_project_dir: Callable[[str | None], str],
) -> tuple[Any, JobExecution]:
    from proxym.tools.executor import ToolExecutor
    environment_snapshot = _prepare_environment_snapshot(
        tool_def,
        instance,
        get_tool_environment_snapshot,
    )
    resolved_project_dir = _resolve_project_dir(
        ticket,
        environment_snapshot,
        project_dir,
        default_project_dir,
    )

    executor = tool_executor_get()
    executor.start()
    job = executor.enqueue(
        ticket,
        tool_def,
        resolved_project_dir,
        mode,
        model,
        environment=environment_snapshot,
    )
    execution = build_job_execution(job, environment_snapshot)
    ticket_store.update_ticket(ticket.id, {"last_execution": execution})
    return job, execution


async def maybe_auto_dispatch_ticket(
    ticket_id: str,
    *,
    ticket_store: TicketManager,
    tool_registry_get: Callable[[], Any],
    tool_executor_get: Callable[[], Any],
    get_tool_environment_snapshot: Callable[[str], dict[str, object] | None],
    default_project_dir: Callable[[str | None], str],
) -> bool:
    from proxym.tools.executor import ToolExecutor
    ticket = ticket_store.get_ticket(ticket_id)
    logger.info("auto_dispatch_check", ticket_id=ticket_id, ticket_exists=ticket is not None, status=ticket.status.value if ticket else None)
    if not ticket or ticket.status != TicketStatus.IN_PROGRESS:
        logger.warning("auto_dispatch_failed_status", ticket_id=ticket_id, status=ticket.status.value if ticket else None)
        return False

    assignment = ticket.assigned_tool
    logger.info("auto_dispatch_assignment", ticket_id=ticket_id, assignment=assignment.tool if assignment else None)
    if not assignment or assignment.tool == "human":
        logger.warning("auto_dispatch_failed_assignment", ticket_id=ticket_id, has_assignment=assignment is not None)
        return False

    has_active = _ticket_has_active_job(ticket_id, tool_executor_get)
    logger.info("auto_dispatch_active_job", ticket_id=ticket_id, has_active_job=has_active)
    if has_active:
        return False

    tool_def = tool_registry_get().get_tool(assignment.tool)
    logger.info("auto_dispatch_tool", ticket_id=ticket_id, tool_def_exists=tool_def is not None)
    if not tool_def:
        return False

    try:
        logger.info("auto_dispatch_enqueue", ticket_id=ticket_id, tool=assignment.tool)
        await enqueue_ticket_job(
            ticket,
            tool_def,
            mode=assignment.agent_mode or "",
            model=assignment.model or "",
            instance=assignment.instance or "",
            ticket_store=ticket_store,
            tool_executor_get=tool_executor_get,
            get_tool_environment_snapshot=get_tool_environment_snapshot,
            default_project_dir=default_project_dir,
        )
        return True
    except Exception as exc:
        logger.warning("auto_dispatch_failed", ticket=ticket.id, error=str(exc))
        return False
