import { logger } from "../../utils/logger.js";
import { buildTestQualityCriteria } from "./recommendationSections.js";
import { externalDedupKey } from "./recommendationShared.js";
export function buildFullRepoRecommendations(scored, topN, baseUrl, authHeaderValue, authSchemeSnippet, authTypeValue, isFrontendProject = false, isFrontendOnlyProject = false, externalCoverage = new Set()) {
    // Full-repo mode only — percentage-based UI/E2E slot targets (15% each, floor 1).
    const rawE2E = isFrontendProject ? Math.max(1, Math.round(topN * 0.15)) : 0;
    const rawUI = isFrontendProject ? Math.max(1, Math.round(topN * 0.15)) : 0;
    const slotsFloor = Math.floor(topN / 2);
    const minE2ESlots = Math.min(rawE2E, slotsFloor);
    const minUISlots = Math.min(rawUI, Math.max(0, topN - minE2ESlots));
    const authRef = authHeaderValue
        ? `, authHeader: "${authHeaderValue}"${authSchemeSnippet}`
        : `, authHeader: <check OpenAPI securitySchemes or auth middleware; "" if confirmed unauthenticated>`;
    const hasWorkspaceAuthType = !!authTypeValue && authTypeValue !== "none";
    const authHeaderOnlyRef = hasWorkspaceAuthType
        ? ""
        : authHeaderValue
            ? `, authHeader: "${authHeaderValue}"`
            : `, authHeader: <check OpenAPI securitySchemes or auth middleware; "" if confirmed unauthenticated>`;
    // Supplement count for full-repo mode
    const supplementCount = topN - Math.min(scored.length, topN);
    const toTitle = (name) => name.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase());
    const TYPE_ORDER = ["e2e", "ui", "integration", "contract"];
    const TYPE_LABEL = {
        e2e: "E2E", ui: "UI", integration: "Integration", contract: "Contract",
    };
    // Filter out scenarios already covered by external tests before slicing.
    const scoredFiltered = externalCoverage.size > 0
        ? scored.filter(item => {
            const key = externalDedupKey(item.scenario);
            if (externalCoverage.has(key)) {
                logger.info(`External dedup (full-repo): skipping "${item.scenario.scenarioName}" (${key})`);
                return false;
            }
            return true;
        })
        : scored;
    // For full-stack repos, carve out E2E and UI slots before filling with backend tests.
    const backendSlotCount = isFrontendProject
        ? Math.max(0, topN - minE2ESlots - minUISlots)
        : topN;
    const allItems = scoredFiltered.slice(0, backendSlotCount);
    const byType = new Map();
    for (const t of TYPE_ORDER)
        byType.set(t, []);
    for (const item of allItems) {
        const t = item.scenario.testType ?? (item.scenario.steps.length === 1 ? "contract" : "integration");
        if (!byType.has(t))
            byType.set(t, []);
        byType.get(t).push(item);
    }
    const renderItem = (item, rank) => {
        const s = item.scenario;
        const testType = s.testType ?? (s.steps.length === 1 ? "contract" : "integration");
        const title = toTitle(s.scenarioName);
        if (testType === "contract") {
            const step = s.steps[0];
            const endpointURL = `${baseUrl}${step.path}`;
            const isBodyMethod = ["POST", "PUT", "PATCH"].includes(step.method);
            const dataParam = isBodyMethod
                ? `, requestData: <${step.method} ${step.path} required fields from source code>`
                : "";
            return [
                `**${rank}. ${title}**`,
                `  ${s.description}`,
                `  ${step.method} ${step.path} \u2192 ${step.expectedStatusCode}`,
                `  Tool: \`skyramp_contract_test_generation({ endpointURL: "${endpointURL}", method: "${step.method}"${authRef}${dataParam} })\``,
                `  From source: fill in requestData field names and the specific production boundary this validates`,
            ].join("\n");
        }
        else {
            const stepLines = s.steps.map(st => {
                const isBody = ["POST", "PUT", "PATCH"].includes(st.method);
                const bodyHint = isBody ? ` \u2014 body: <${st.method} ${st.path} required fields from source>` : "";
                return `  ${st.order}. ${st.method} ${st.path} \u2192 ${st.expectedStatusCode}: ${st.description}${bodyHint}`;
            }).join("\n");
            const isTraceBased = testType === "e2e" || testType === "ui";
            let toolCallsBlock;
            if (isTraceBased) {
                // E2E and UI need browser recording first, then generation
                const frontendUrl = "<frontend_url>";
                const zipPath = `<repositoryPath>/.skyramp/${s.scenarioName}_trace.zip`;
                if (testType === "ui") {
                    toolCallsBlock = [
                        `  1. browser_navigate({ url: "${frontendUrl}" })`,
                        `  2. Interact with the changed components (browser_click, browser_type, browser_fill_form, etc.)`,
                        `  3. browser_snapshot() after each key interaction`,
                        `  4. skyramp_export_zip({ outputPath: "${zipPath}" }) — use absolute path`,
                        `  5. skyramp_ui_test_generation({ playwrightInput: "${zipPath}"${authHeaderOnlyRef} })`,
                    ].join("\n");
                }
                else {
                    toolCallsBlock = [
                        `  1. browser_navigate({ url: "${frontendUrl}" }) — record frontend trace`,
                        `  2. Interact with the user journey described above`,
                        `  3. skyramp_export_zip({ outputPath: "${zipPath}" }) — use absolute path`,
                        `  4. Capture backend trace JSON separately (skyramp_start_trace_collection / skyramp_stop_trace_collection)`,
                        `  5. skyramp_e2e_test_generation({ playwrightInput: "${zipPath}", trace: "<backend trace path>"${authHeaderOnlyRef} })`,
                    ].join("\n");
                }
            }
            else {
                // Integration: use batch scenario tool (all steps in one call)
                let destinationHost = s.scenarioName;
                try {
                    destinationHost = new URL(baseUrl).hostname;
                }
                catch { /* keep fallback */ }
                const batchSteps = s.steps.map(st => {
                    const isBody = ["POST", "PUT", "PATCH"].includes(st.method);
                    let dataParam = "";
                    if (isBody) {
                        if (st.requestBody && Object.keys(st.requestBody).length > 0) {
                            const bodyJson = JSON.stringify(st.requestBody).replace(/"/g, '\\"');
                            dataParam = `, requestBody: "${bodyJson}"`;
                        }
                        else {
                            dataParam = `, requestBody: <${st.method} ${st.path} required fields from source code>`;
                        }
                    }
                    return `    { method: "${st.method}", path: "${st.path}", statusCode: ${st.expectedStatusCode}${dataParam} }`;
                }).join(",\n");
                toolCallsBlock = [
                    `  skyramp_batch_scenario_test_generation({ scenarioName: "${s.scenarioName}", destination: "${destinationHost}", baseURL: "${baseUrl}"${authRef}, steps: [\n${batchSteps}\n  ] })`,
                    `  skyramp_integration_test_generation({ scenarioFile: <filePath returned by skyramp_batch_scenario_test_generation above>${authHeaderOnlyRef} })`,
                ].join("\n");
            }
            return [
                `**${rank}. ${title}**`,
                `  ${s.description}`,
                `  Steps:`,
                stepLines,
                `  Tool calls:`,
                toolCallsBlock,
                `  From source: fill in requestBody field values and assert all computed response fields`,
            ].join("\n");
        }
    };
    const backendSections = TYPE_ORDER
        .filter(t => (byType.get(t) ?? []).length > 0)
        .map(t => {
        const items = byType.get(t);
        const label = TYPE_LABEL[t];
        let globalRank = 0;
        for (const prev of TYPE_ORDER) {
            if (prev === t)
                break;
            globalRank += (byType.get(prev) ?? []).length;
        }
        const entries = items.map((item, i) => renderItem(item, globalRank + i + 1)).join("\n\n");
        return `### ${label} (${items.length})\n\n${entries}`;
    });
    // Pre-allocate E2E and UI placeholder sections for full-stack repos.
    const e2eSectionParts = [];
    const uiSectionParts = [];
    if (isFrontendProject) {
        for (let i = 0; i < minE2ESlots; i++) {
            const rank = i + 1;
            e2eSectionParts.push(`**${rank}. E2E User Journey ${i + 1}**\n` +
                `  End-to-end test covering a complete user journey through the frontend and backend.\n` +
                `  To generate: record a browser trace, then call the generation tool.\n` +
                `    browser_navigate({ url: "${baseUrl}" }) \u2192 exercise key user flow \u2192 skyramp_export_zip({ outputPath: "<repo>/.skyramp/e2e_journey_${i + 1}.zip" })\n` +
                `  Tool: \`skyramp_e2e_test_generation({ playwrightInput: "<repo>/.skyramp/e2e_journey_${i + 1}.zip"${authHeaderOnlyRef} })\`\n` +
                `  From source: read frontend components and their API calls to identify the highest-value user journey`);
        }
        for (let i = 0; i < minUISlots; i++) {
            const rank = minE2ESlots + i + 1;
            uiSectionParts.push(`**${rank}. UI Component Test ${i + 1}**\n` +
                `  Test key UI component interactions and state changes.\n` +
                `  To generate: record a browser trace, then call the generation tool.\n` +
                `    browser_navigate({ url: "${baseUrl}" }) \u2192 interact with UI components \u2192 skyramp_export_zip({ outputPath: "<repo>/.skyramp/ui_component_${i + 1}.zip" })\n` +
                `  Tool: \`skyramp_ui_test_generation({ playwrightInput: "<repo>/.skyramp/ui_component_${i + 1}.zip"${authHeaderOnlyRef} })\`\n` +
                `  From source: read frontend component files to identify interactions, form submissions, and state transitions`);
        }
        // Offset backend section ranks by the number of E2E + UI placeholders
        const offset = minE2ESlots + minUISlots;
        backendSections.forEach((_, idx) => {
            const t = TYPE_ORDER.filter(t => (byType.get(t) ?? []).length > 0)[idx];
            if (!t)
                return;
            const items = byType.get(t);
            const label = TYPE_LABEL[t];
            let globalRank = offset;
            for (const prev of TYPE_ORDER) {
                if (prev === t)
                    break;
                globalRank += (byType.get(prev) ?? []).length;
            }
            backendSections[idx] = `### ${label} (${items.length})\n\n${items.map((item, i) => renderItem(item, globalRank + i + 1)).join("\n\n")}`;
        });
    }
    const allSections = [
        ...(e2eSectionParts.length > 0 ? [`### E2E (${e2eSectionParts.length})\n\n${e2eSectionParts.join("\n\n")}`] : []),
        ...(uiSectionParts.length > 0 ? [`### UI (${uiSectionParts.length})\n\n${uiSectionParts.join("\n\n")}`] : []),
        ...backendSections,
    ];
    const sections = allSections.join("\n\n");
    const frontendTierNote = isFrontendOnlyProject
        ? `\n\n**Frontend repo:** supplement MUST include at least ${minE2ESlots} E2E test${minE2ESlots > 1 ? "s" : ""} (\`skyramp_e2e_test_generation\`) and at least ${minUISlots} UI test${minUISlots > 1 ? "s" : ""} (\`skyramp_ui_test_generation\`). Do NOT add integration or contract tests.`
        : isFrontendProject
            ? `\n\n**Full-stack repo:** supplement MUST include at least ${minE2ESlots} E2E test${minE2ESlots > 1 ? "s" : ""} (\`skyramp_e2e_test_generation\`) and at least ${minUISlots} UI test${minUISlots > 1 ? "s" : ""} (\`skyramp_ui_test_generation\`). Add these before exhausting backend tiers.`
            : "";
    const repoSupplementNote = supplementCount > 0
        ? `
<supplement_guidance>
**When to use:** The pre-ranked sections above contain fewer than ${topN} items. Add exactly ${supplementCount} more using the tiers below — exhaust each tier before moving to the next.

**Tier 1 — Error paths for endpoints already in the list** (highest value, do first):
  • Auth boundary (no Authorization header → 403/401) → \`testType: contract, category: security_boundary\`
  • Invalid/non-existent IDs (→ 404) → \`testType: contract, category: error_handling\`
  • Missing required fields (→ 422) → \`testType: contract, category: data_validation\`
  • Boundary values for numeric fields → \`testType: integration, category: data_validation\`
  Note: DISCARD unique-constraint scenarios if the storage backend is Redis, MongoDB, or schema-less.

**Tier 2 — Auth coverage for any endpoint not yet covered by Tier 1:**
  → \`testType: contract, category: security_boundary\`

**Tier 3 — Cross-resource integration** (only when one resource's POST body contains another's \`_id\` field):
  → \`testType: integration, category: workflow\`

**Tier 4 — CRUD lifecycle** for any resource not yet covered:
  → \`testType: integration, category: crud\`

**How to fill each item:** Use path parameters in \`{param}\` format. Use real field names from the analysis or handler source — no generic placeholders. Describe behavior in API terms (HTTP method, path, status code), not storage internals.${frontendTierNote}
</supplement_guidance>`
        : "";
    const typeMixText = isFrontendOnlyProject
        ? `This is a frontend repo. Focus on E2E and UI tests only. Include at least ${minE2ESlots} E2E test${minE2ESlots > 1 ? "s" : ""} (\`skyramp_e2e_test_generation\`) and at least ${minUISlots} UI test${minUISlots > 1 ? "s" : ""} (\`skyramp_ui_test_generation\`). Do NOT add integration or contract tests.`
        : isFrontendProject
            ? `This is a full-stack repo. Coverage ranking: E2E > UI > Integration > Contract. Include at least ${minE2ESlots} E2E test${minE2ESlots > 1 ? "s" : ""} (\`skyramp_e2e_test_generation\`) and at least ${minUISlots} UI test${minUISlots > 1 ? "s" : ""} (\`skyramp_ui_test_generation\`), in addition to backend integration and contract tests.`
            : `Focus on integration and contract tests for all API endpoints.`;
    return `## Test Recommendations — ${topN} total (grouped by test type)

> Repo mode — no tests are executed. Ranked by risk within each type.
> To generate any item: read the handler source, fill \`<…from source>\` placeholders with real values, then call the tool.

${sections}

**Test type mix — MANDATORY. No smoke tests. No fuzz tests. Only: integration, contract, E2E, UI.**
${typeMixText}

${repoSupplementNote}

**Present up to ${topN} recommendations.** Prioritize quality — only include a recommendation if it adds genuine new coverage. If fewer than ${topN} high-value tests exist for this codebase, stop at the last useful item rather than padding with trivial ones.

---
<enrichment_notes>
**Path resolution (do this before filling in any tool call):**
Cross-check every endpoint path against the Router Mounting / Nesting section in the analysis above. Sub-routers may be mounted at nested prefixes — e.g. a reviews router with \`@router.get("/")\` may actually be \`GET /api/v1/products/{product_id}/reviews\` if mounted under that prefix. Always use the fully-qualified nested path in tool calls, not the path as it appears in the route file alone.

**Existing test files (check before assigning output filenames):**
See the Existing Tests section above. If a recommendation's primary resource already has a \`[skyramp]\` test file listed there, prefer passing an explicit \`output\` filename (e.g. \`output: "orders_integration_test.py"\`) to update the existing file rather than creating a duplicate. Do NOT update \`[external]\` test files — they are user-maintained.

Before filling in tool call parameters for each item, use the analysis data already provided above (endpoint interactions, source context) first. Only read the route handler source code directly when the analysis data does not contain the specific value you need:
- Required request body fields (POST/PUT/PATCH) — use field names from the analysis interactions; read source only if they show \`{}\` or are missing
- Computed/derived response fields and their formulas — assert exact values; read source for formula details not captured in the analysis
- Auth middleware — set authHeader/authScheme from the repository context above; FastAPI HTTPBearer → 403 not 401
- Storage backend — if Redis or schema-less, discard unique-constraint and cascade-delete scenarios
- Delete behavior — hard-delete → 204; soft-delete/cancel → 200

${buildTestQualityCriteria()}

**5-dimension rubric — use to assign priority for supplement items:**
| Dimension | What to assess |
| Production Safety | Guards a critical boundary (auth, unique constraint, cascade delete, data integrity, breaking migration)? → HIGH |
| Bug-Finding Potential | Targets a known failure mode (race condition, data consistency, state transition, cascade effect)? → HIGH |
| User Journey Relevance | Reflects how real users interact (from traces, business flows, critical paths)? → HIGH or MEDIUM |
| Coverage Gap | Addresses an area with zero existing test coverage? → bump up one tier |
| Code Insight | Derived from actual implementation (spotted middleware pattern, N+1 risk, unique constraint)? → bump up one tier |
</enrichment_notes>`;
}
