import * as fs from "fs";
import * as path from "path";
import { z } from "zod";
import { logger } from "../../utils/logger.js";
import { enumerateCandidateUiPages } from "../../utils/uiPageEnumerator.js";
import { isFrontendFile, isTestFile } from "../../prompts/test-recommendation/scopeAssessment.js";
import { parseChangedFilesFromDiff } from "../../utils/branchDiff.js";
import { toolText } from "../../utils/utils.js";
import { isTestbotEnabled } from "../../utils/featureFlags.js";
/**
 * skyramp_ui_analyze_changes — runs before skyramp_analyze_changes when the
 * caller (typically the testbot agent) wants blueprint-grounded recommendations.
 *
 * Computes candidateUiPages[] using the same source-grounded logic that
 * analyze_changes uses internally (shared enumerator), then emits explicit
 * instructions for the agent: navigate to each candidate URL, capture a
 * browser_blueprint, then call skyramp_analyze_changes with the captures
 * available as element vocabulary when the agent writes UI rec reasoning.
 *
 * Graceful degradation: backend-only PRs produce a "no UI work" instruction;
 * unresolvable frontend baseUrl produces "skip captures, call analyze_changes
 * with empty array." analyze_changes still works in either case — same
 * source-grounded output as before slice 4.
 */
export async function runUiAnalyzeChanges(params) {
    const repoPath = params.repositoryPath;
    // Read changedFiles from the canonical PR diff file. The testbot runtime
    // writes this before the prompt runs.
    const diffPath = path.join(repoPath, ".skyramp_git_diff");
    let changedFiles = [];
    let diffFileMissing = false;
    try {
        const raw = fs.readFileSync(diffPath, "utf-8");
        changedFiles = parseChangedFilesFromDiff(raw);
    }
    catch {
        // No diff file — for testbot this would be a runtime bug (it always
        // writes one); for non-testbot callers it means "diff unavailable",
        // which is NOT the same as "no UI changes". Surface that distinction.
        diffFileMissing = true;
    }
    if (diffFileMissing) {
        const uiContext = {
            changedFrontendFiles: [],
            candidateUiPages: [],
        };
        return {
            uiContext,
            instructions: DIFF_FILE_MISSING_INSTRUCTIONS,
        };
    }
    const frontendFiles = changedFiles.filter((f) => isFrontendFile(f) && !isTestFile(f));
    if (frontendFiles.length === 0) {
        const uiContext = {
            changedFrontendFiles: [],
            candidateUiPages: [],
        };
        return {
            uiContext,
            instructions: NO_UI_INSTRUCTIONS,
        };
    }
    const candidateUiPages = await enumerateCandidateUiPages(repoPath, frontendFiles);
    const uiContext = {
        changedFrontendFiles: frontendFiles,
        candidateUiPages,
    };
    const instructions = candidateUiPages.length === 0
        ? NO_RESOLVABLE_URLS_INSTRUCTIONS
        : buildCaptureInstructions(candidateUiPages, params.uiCredentials);
    logger.info("ui_analyze_changes computed candidate UI pages", {
        repositoryPath: repoPath,
        frontendFileCount: frontendFiles.length,
        candidateUiPageCount: candidateUiPages.length,
        hasUiCredentials: !!params.uiCredentials,
    });
    return { uiContext, instructions };
}
const NO_UI_INSTRUCTIONS = `No UI changes detected in this PR. Proceed directly to \`skyramp_analyze_changes\`.`;
const NO_RESOLVABLE_URLS_INSTRUCTIONS = `Frontend changes detected but no candidate URLs could be resolved (workspace baseUrl missing or no router files matched). Proceed to \`skyramp_analyze_changes\`; UI recommendations will be source-grounded only.`;
const DIFF_FILE_MISSING_INSTRUCTIONS = `\`.skyramp_git_diff\` not found in the repository. This file is normally written by the testbot runtime before the prompt runs; without it, this tool cannot determine which files changed. **Do not skip blueprint capture on the assumption there are no UI changes** — that conclusion is not supported by the available data. If you have access to the changed-files list elsewhere (e.g. a \`gh pr diff\` command, an environment variable), determine UI candidates yourself and capture their blueprints before proceeding. Otherwise proceed to \`skyramp_analyze_changes\` and add an \`issuesFound\` info entry: "ui_analyze_changes could not read .skyramp_git_diff; recommendations are source-grounded only".`;
function buildCaptureInstructions(pages, uiCredentials) {
    // The `<ui-credentials>` tag is only emitted by the testbot orchestration
    // prompt. When this tool is called from an IDE (Cursor, etc.) outside
    // testbot, the agent has no such tag — embed the credentials inline
    // instead. uiCredentials is "email:password" formatted by the caller.
    const loginStep = uiCredentials
        ? (isTestbotEnabled()
            ? `\n0. **Log in first.** \`browser_navigate\` to the app's login URL, then use \`<ui-credentials>\` from the testbot prompt to authenticate (you have those credentials in context). Without authentication, the candidate URLs below may redirect to login and the captured blueprints will be of the login page, not the feature.\n`
            : `\n0. **Log in first.** \`browser_navigate\` to the app's login URL, then authenticate with the credentials \`${uiCredentials}\` (formatted as \`email:password\`). Without authentication, the candidate URLs below may redirect to login and the captured blueprints will be of the login page, not the feature.\n`)
        : "";
    const pagesYaml = pages
        .map((p, i) => `  ${i + 1}. ${p.url}  (sourcedFrom: ${p.sourcedFrom.join(", ") || "(none)"}, strategy: ${p.strategy})`)
        .join("\n");
    return `Frontend changes detected. **Before calling \`skyramp_analyze_changes\`, capture blueprints on the candidate UI pages below.** Those captures stay in your tool-result history and serve as element vocabulary when you write UI test recommendations — \`skyramp_analyze_changes\` returns the authoring rules; you bring the observed elements.
${loginStep}
**Candidate URLs:**
${pagesYaml}

**For each candidate URL:**
  - \`browser_navigate\` to the URL
  - \`browser_blueprint\` to capture the page

You don't need to thread the blueprints back into a tool call — they're in your context once captured. **Then call \`skyramp_analyze_changes\`** to get the recommendation catalog and the UI authoring rules.

If a candidate URL 404s or redirects unexpectedly, navigate from the workspace baseUrl and explore (admin apps mount routes under base prefixes the source extraction can't see). If the page rendered but lacks the changed feature (gated UI: modal, dropdown, accordion), do NOT iterate further during this step — UI recs will fall back to source-grounded prose for those, and the agent's later trace recording (Task 2) will navigate into the gate via capture-act-capture.

If \`browser_blueprint\` fails on every candidate URL (app unreachable, all 404s), proceed to \`skyramp_analyze_changes\` and log an \`issuesFound\` info entry. Recommendations will be source-grounded; non-UI work is unaffected.`;
}
export const uiAnalyzeChangesInputSchema = {
    repositoryPath: z
        .string()
        .describe("Absolute path to the repository root"),
    uiCredentials: z
        .string()
        .optional()
        .describe("Optional ui-credentials for authed apps. When present, the returned " +
        "instructions include a login step before blueprint capture."),
};
export function registerUiAnalyzeChangesTool(server) {
    server.registerTool("skyramp_ui_analyze_changes", {
        description: [
            "Run BEFORE skyramp_analyze_changes when the PR may contain frontend changes.",
            "Enumerates candidate UI pages from the diff and instructs the agent to",
            "capture browser_blueprints on each before calling analyze_changes. The",
            "captures stay in the agent's tool-result history and serve as the",
            "element vocabulary for UI rec reasoning.",
            "",
            "On backend-only PRs, returns a 'no UI work' instruction — caller can",
            "proceed directly to skyramp_analyze_changes.",
        ].join("\n"),
        inputSchema: uiAnalyzeChangesInputSchema,
        annotations: {
            readOnlyHint: true, // reads .skyramp_git_diff + workspace.yml; no writes
            idempotentHint: true, // pure function of repo state + uiCredentials
            openWorldHint: false, // no external service calls
        },
    }, async (params) => {
        const result = await runUiAnalyzeChanges(params);
        const summary = JSON.stringify({ uiContext: result.uiContext }, null, 2);
        return toolText(`<summary>
${summary}
</summary>

<instructions>
${result.instructions}
</instructions>`);
    });
}
