import { callLlm } from "../llm.js";
import { safeParseJson } from "../utils.js";
import type { PipelineContext } from "../types.js";

function name(ctx: PipelineContext): string {
  return ctx.options.agentName?.trim() || "auto-reviewer";
}

/**
 * Auto-reviewer agent: a second LLM persona that stands in for the human
 * reviewer when the pipeline runs in --auto-mode. It answers clarifying
 * questions and makes approve/edit/regenerate calls on human gates.
 */

const SYSTEM_CLARIFY = `You are an engineering lead acting as a stand-in reviewer for a frontend pipeline targeting the hyperswitch-control-center repo.
A product manager has asked clarifying questions about a task definition. Your job is to answer those questions based on the task, product alignment notes, and your best reasonable assumptions.

Rules:
- Answer every question concisely and concretely.
- If a question can't be answered from the given context, make a reasonable assumption and state it explicitly.
- Never return empty strings.

Return ONLY valid JSON with shape:
{
  "answers": { "<question text>": "<answer>" }
}
No markdown fences, no prose outside the JSON.`;

export async function autoAnswerClarifyingQuestions(
  ctx: PipelineContext,
  questions: string[]
): Promise<Record<string, string>> {
  const user = JSON.stringify(
    {
      task: ctx.artifacts.task,
      priorPmReview: ctx.artifacts.productAlignment,
      priorClarifications:
        (ctx.artifacts as Record<string, unknown>).pmClarifications ?? [],
      questions,
    },
    null,
    2
  );
  const raw = await callLlm({ system: SYSTEM_CLARIFY, user, label: "auto-reviewer:clarify" });
  const parsed = safeParseJson<{ answers: Record<string, string> }>(raw);
  const out: Record<string, string> = {};
  for (const q of questions) {
    const a = parsed?.answers?.[q];
    out[q] =
      typeof a === "string" && a.trim().length > 0
        ? a.trim()
        : "(auto-reviewer: no specific answer — proceed with reasonable defaults)";
  }
  ctx.log(
    `${name(ctx)} answered ${questions.length} clarifying question(s) on your behalf`,
    "success"
  );
  return out;
}

const SYSTEM_SPEC_REVIEW = (specType: "l2" | "l3" | "l4") =>
  `You are a senior engineer reviewing a ${specType.toUpperCase()} spec generated by another LLM for a ReScript+React frontend task (hyperswitch-control-center).

Your role is stand-in human reviewer in an automated pipeline. Decide one of:
- "approve" — spec is good enough to proceed
- "regenerate" — spec has systemic issues; provide clear guidance for regeneration
- (never pick "edit" — you don't manually edit JSON)

Be lenient. Approve unless the spec has obvious omissions, factual mistakes, or contradicts the task. Small style quibbles are not grounds for regeneration.

Return ONLY JSON:
{
  "decision": "approve" | "regenerate",
  "notes": "string — short justification",
  "regeneratePrompt": "string — only when decision=regenerate, guidance for the generator"
}`;

export async function autoReviewSpec(
  ctx: PipelineContext,
  specType: "l2" | "l3" | "l4",
  spec: unknown
): Promise<{ decision: "approve" | "regenerate"; regeneratePrompt?: string }> {
  const user = JSON.stringify(
    {
      task: ctx.artifacts.task,
      productAlignment: ctx.artifacts.productAlignment,
      l2: ctx.artifacts.l2,
      l3: ctx.artifacts.l3,
      specUnderReview: spec,
    },
    null,
    2
  );
  const raw = await callLlm({
    system: SYSTEM_SPEC_REVIEW(specType),
    user,
    label: `auto-reviewer:review-${specType}`,
  });
  const parsed = safeParseJson<{
    decision: "approve" | "regenerate";
    notes?: string;
    regeneratePrompt?: string;
  }>(raw);

  if (!parsed || (parsed.decision !== "approve" && parsed.decision !== "regenerate")) {
    // Fallback: approve to avoid looping forever on bad LLM output.
    ctx.log(
      `${name(ctx)} couldn't parse ${specType.toUpperCase()} review response — approving by default`,
      "warn"
    );
    return { decision: "approve" };
  }

  const notes = parsed.notes ? ` — ${parsed.notes.slice(0, 140)}` : "";
  if (parsed.decision === "approve") {
    ctx.log(
      `${name(ctx)} approved the ${specType.toUpperCase()} spec${notes}`,
      "success"
    );
  } else {
    ctx.log(
      `${name(ctx)} asked for ${specType.toUpperCase()} regeneration${notes}`,
      "warn"
    );
  }

  if (parsed.decision === "regenerate") {
    return {
      decision: "regenerate",
      regeneratePrompt:
        parsed.regeneratePrompt?.trim() ||
        "Please regenerate the spec with more detail.",
    };
  }
  return { decision: "approve" };
}

const SYSTEM_DESIGN_GATE = `You are a stand-in reviewer deciding whether a frontend task requires UI design mockups.

Return ONLY JSON:
{
  "designRequired": boolean,
  "connectorDocUrls": ["array of connector reference document URLs - pass through from task if provided"],
  "skipReason": "string — why design isn't needed, only when designRequired=false"
}

Heuristics:
- If the task mentions visual components, new screens, forms, modals, or layout changes → designRequired=true
- If it's a backend wiring, data change, or pure logic task → designRequired=false
- If the task already has connectorDocUrls, pass them through
- If no connectorDocUrls and design is required but no reference docs → still return true with empty connectorDocUrls; downstream will handle`;

export async function autoDesignGate(
  ctx: PipelineContext
): Promise<{ designRequired: boolean; connectorDocUrls?: string[]; skipReason?: string }> {
  const task = ctx.artifacts.task;
  const raw = await callLlm({
    system: SYSTEM_DESIGN_GATE,
    user: JSON.stringify({ task, productAlignment: ctx.artifacts.productAlignment }, null, 2),
    label: "auto-reviewer:design-gate",
  });
  const parsed = safeParseJson<{
    designRequired: boolean;
    connectorDocUrls?: string[];
    skipReason?: string;
  }>(raw);
  if (!parsed || typeof parsed.designRequired !== "boolean") {
    // Fallback: infer from presence of connectorDocUrls.
    const urls = task?.connectorDocUrls;
    ctx.log(
      `${name(ctx)} couldn't parse design-gate response — inferring from task`,
      "warn"
    );
    return urls && urls.length > 0
      ? { designRequired: true, connectorDocUrls: urls }
      : { designRequired: false, skipReason: `${name(ctx)}: no connector doc urls in task` };
  }
  ctx.log(
    parsed.designRequired
      ? `${name(ctx)} decided design is required for this task`
      : `${name(ctx)} decided to skip the design gate`,
    "success"
  );
  return {
    designRequired: parsed.designRequired,
    connectorDocUrls:
      parsed.connectorDocUrls?.length ? parsed.connectorDocUrls : task?.connectorDocUrls || undefined,
    skipReason: parsed.skipReason,
  };
}
