# Copyright (c) 2023-present Pi Dash Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.

"""Prompt composition.

`composer.build_first_turn(issue, run)` is the only caller-facing entrypoint.
Views and orchestration never render directly.
"""

from __future__ import annotations

from typing import Optional

from pi_dash.db.models.issue import Issue
from pi_dash.db.models.workspace import Workspace
from pi_dash.prompting.context import build_context
from pi_dash.prompting.models import PromptTemplate
from pi_dash.prompting.renderer import render
from pi_dash.runner.models import AgentRun


class PromptTemplateNotFound(Exception):
    """No active PromptTemplate (workspace-scoped or global) was available."""


def load_template(
    workspace: Optional[Workspace],
    name: str = PromptTemplate.DEFAULT_NAME,
) -> PromptTemplate:
    """Return the active template for ``(workspace, name)`` or fall back to the
    global (``workspace IS NULL``) row. Raises if neither exists.
    """
    if workspace is not None:
        ws_match = (
            PromptTemplate.objects.filter(
                workspace=workspace, name=name, is_active=True
            )
            .order_by("-updated_at")
            .first()
        )
        if ws_match is not None:
            return ws_match

    global_match = (
        PromptTemplate.objects.filter(
            workspace__isnull=True, name=name, is_active=True
        )
        .order_by("-updated_at")
        .first()
    )
    if global_match is None:
        raise PromptTemplateNotFound(
            f"No active PromptTemplate for name={name!r}; "
            "did the seed migration run?"
        )
    return global_match


def build_first_turn(issue: Issue, run: AgentRun) -> str:
    """Render the prompt for ``run`` executing ``issue``.

    Every run gets the full template. Follow-up runs are not "continuations"
    of a prior session — they are fresh agent invocations that reconstruct
    state from the workpad comment, the issue's comment thread, and the repo.
    See ``.ai_design/ticking_optimization/design.md``.

    Selects the prompt template by the issue's current state via the
    phase registry: the In Progress phase uses the ``coding-task``
    template; the In Review phase (introduced in PR B) uses the
    ``review`` template. States outside any registered ticking phase
    fall back to the default template.
    """
    from pi_dash.orchestration.agent_phases import template_name_for

    template = load_template(issue.workspace, name=template_name_for(issue.state))
    context = build_context(issue, run)
    return render(template.body, context)
