"""
Canonical starter workflows for MCP-capable hosts.

These specs are the shared source of truth for low-friction workflows that can
be rendered as MCP prompts today and reused for other host surfaces later.
"""

from __future__ import annotations

from dataclasses import dataclass
from functools import partial
from typing import TYPE_CHECKING, Callable, Iterable, Sequence

if TYPE_CHECKING:
    from mcp.server.fastmcp import FastMCP


PromptRenderer = Callable[..., str | Sequence[str]]


@dataclass(frozen=True)
class WorkflowArgumentSpec:
    name: str
    description: str
    required: bool = False


@dataclass(frozen=True)
class WorkflowPromptSpec:
    name: str
    title: str
    description: str
    providers: tuple[str, ...]
    arguments: tuple[WorkflowArgumentSpec, ...]
    render: PromptRenderer


def _compact(value: str | None) -> str | None:
    text = str(value or "").strip()
    return text or None


def _csv_items(value: str) -> list[str]:
    return [item.strip() for item in str(value).split(",") if item.strip()]


def _default_snapshot_dir(object_name: str) -> str:
    return f"artifacts/mcp-snapshots/{str(object_name).strip().lower()}"


def _render_crm_doctor_sfdc(*, target: str | None = None, provider: str = "sfdc") -> str:
    if provider != "sfdc":
        raise ValueError(f"crm-doctor renderer does not support provider '{provider}'.")
    target_hint = _compact(target)
    target_line = f' with `target="{target_hint}"`' if target_hint else ""
    return (
        "Run the g-gremlin Salesforce setup check.\n\n"
        "Use these tools in order:\n"
        f"1. Call `sfdc.doctor`{target_line}.\n"
        f"2. If the doctor result status is not `error`, call `sfdc.whoami`{target_line}.\n\n"
        "Then respond with:\n"
        "- Overall setup status (`ok`, `warn`, or `error`)\n"
        "- Org identity and target summary\n"
        "- Any CLI/runtime warnings\n"
        "- The single next command the operator should run if setup is blocked\n\n"
        "If auth is missing or broken, tell the user to run "
        "`g-gremlin sfdc connect --org-alias <alias>` and stop after summarizing the blocker."
    )


def _render_crm_doctor_dynamics(*, profile: str | None = None, provider: str = "dynamics") -> str:
    if provider != "dynamics":
        raise ValueError(f"crm-doctor renderer does not support provider '{provider}'.")
    profile_hint = _compact(profile)
    profile_line = f' with `profile="{profile_hint}"`' if profile_hint else ""
    return (
        "Run the g-gremlin Dynamics / Dataverse setup check.\n\n"
        "Use these tools in order:\n"
        f"1. Call `dynamics.doctor`{profile_line}.\n"
        f"2. If credentials are configured and the status is not `error`, call "
        f"`dynamics.list_tables`{profile_line} with `top=5` and `include_managed=false`.\n\n"
        "Then respond with:\n"
        "- Overall setup status (`ok`, `warn`, or `error`)\n"
        "- Active profile and credential summary\n"
        "- Whether read access is working\n"
        "- The single next command the operator should run if setup is blocked\n\n"
        "If credentials are missing, tell the user to run `g-gremlin auth set dynamics`.\n"
        "If they need a non-default profile, remind them they can launch the server with "
        "`--dynamics-profile <profile>` or set `G_GREMLIN_MCP_DYNAMICS_PROFILE`."
    )


def _render_sfdc_snapshot(
    *,
    object_name: str,
    fields_csv: str,
    out_dir: str | None = None,
    where: str | None = None,
    order_by: str | None = None,
    limit: str | None = None,
    include_schema: str | None = None,
    target: str | None = None,
    provider: str = "sfdc",
) -> str:
    if provider != "sfdc":
        raise ValueError(f"sfdc-snapshot renderer does not support provider '{provider}'.")

    resolved_object = _compact(object_name)
    if not resolved_object:
        raise ValueError("object_name is required.")
    resolved_fields = _csv_items(fields_csv)
    if not resolved_fields:
        raise ValueError("fields_csv must contain at least one field.")

    resolved_out_dir = _compact(out_dir) or _default_snapshot_dir(resolved_object)
    resolved_where = _compact(where)
    resolved_order_by = _compact(order_by)
    resolved_limit = _compact(limit)
    resolved_include_schema = (_compact(include_schema) or "false").lower()
    resolved_target = _compact(target)

    tool_lines = [
        f'- `out_dir="{resolved_out_dir}"`',
        f'- `object_name="{resolved_object}"`',
        f"- `fields={resolved_fields!r}`",
    ]
    if resolved_where:
        tool_lines.append(f'- `where="{resolved_where}"`')
    if resolved_order_by:
        tool_lines.append(f'- `order_by="{resolved_order_by}"`')
    if resolved_limit:
        tool_lines.append(f"- `limit={resolved_limit}`")
    if resolved_include_schema in {"true", "1", "yes"}:
        tool_lines.append("- `include_schema=true`")
    if resolved_target:
        tool_lines.append(f'- `target="{resolved_target}"`')

    return (
        "Create a safe Salesforce snapshot using `sfdc.snapshot`.\n\n"
        "Call `sfdc.snapshot` once with these arguments:\n"
        + "\n".join(tool_lines)
        + "\n\nAfter the tool call, respond with:\n"
        "- Snapshot output directory\n"
        "- Object, fields, and any filter applied\n"
        "- Record count or file summary from the tool result\n"
        "- One useful follow-up suggestion, such as adding schema or narrowing the field list\n\n"
        "Do not use any write or apply tools for this workflow."
    )


WORKFLOW_PROMPTS: tuple[WorkflowPromptSpec, ...] = (
    WorkflowPromptSpec(
        name="crm-doctor",
        title="CRM Doctor",
        description="Run a first-pass Salesforce setup check and summarize auth, org identity, and next steps.",
        providers=("sfdc",),
        arguments=(
            WorkflowArgumentSpec(
                name="target",
                description="Optional Salesforce org alias to inspect instead of the configured default.",
                required=False,
            ),
        ),
        render=_render_crm_doctor_sfdc,
    ),
    WorkflowPromptSpec(
        name="crm-doctor",
        title="CRM Doctor",
        description="Run a first-pass Dynamics setup check and summarize profile, credentials, and next steps.",
        providers=("dynamics",),
        arguments=(
            WorkflowArgumentSpec(
                name="profile",
                description="Optional Dynamics profile override for this prompt run.",
                required=False,
            ),
        ),
        render=_render_crm_doctor_dynamics,
    ),
    WorkflowPromptSpec(
        name="sfdc-snapshot",
        title="Salesforce Snapshot",
        description="Capture a deterministic Salesforce snapshot for one object with an explicit field list.",
        providers=("sfdc",),
        arguments=(
            WorkflowArgumentSpec("object_name", "Salesforce object name to snapshot.", required=True),
            WorkflowArgumentSpec("fields_csv", "Comma-separated field list for the snapshot.", required=True),
            WorkflowArgumentSpec("out_dir", "Optional output directory. Defaults to artifacts/mcp-snapshots/<object>.", required=False),
            WorkflowArgumentSpec("where", "Optional SOQL WHERE clause fragment.", required=False),
            WorkflowArgumentSpec("order_by", "Optional ORDER BY clause.", required=False),
            WorkflowArgumentSpec("limit", "Optional row limit.", required=False),
            WorkflowArgumentSpec("include_schema", "Optional true/false flag to include schema output.", required=False),
            WorkflowArgumentSpec("target", "Optional Salesforce org alias override.", required=False),
        ),
        render=_render_sfdc_snapshot,
    ),
)


def iter_workflow_prompts(*, provider: str) -> Iterable[WorkflowPromptSpec]:
    provider_key = str(provider or "sfdc").strip().lower()
    return (spec for spec in WORKFLOW_PROMPTS if provider_key in spec.providers)


def list_workflow_prompt_names(*, provider: str) -> list[str]:
    return [spec.name for spec in iter_workflow_prompts(provider=provider)]


def register_workflow_prompts(server: "FastMCP", *, provider: str) -> None:
    from mcp.server.fastmcp.prompts import Prompt
    from mcp.server.fastmcp.prompts.base import PromptArgument

    for spec in iter_workflow_prompts(provider=provider):
        server.add_prompt(
            Prompt(
                name=spec.name,
                title=spec.title,
                description=spec.description,
                arguments=[
                    PromptArgument(
                        name=arg.name,
                        description=arg.description,
                        required=arg.required,
                    )
                    for arg in spec.arguments
                ],
                fn=partial(spec.render, provider=provider),
            )
        )
