import { getPersonaPrefix } from "../personas.js";
export const MAINTENANCE_SCOPE_NOTE = "Apply only to new test functions you are adding and existing test functions affected by changes in the diff. Do NOT modify test functions unrelated to the diff.";
export function maintenanceTaskSuffix(enhanceType) {
    return enhanceType === "maintenance" ? ` ${MAINTENANCE_SCOPE_NOTE}` : "";
}
const SHARED_RULES = [
    {
        title: "Echo-back the request fields",
        description: "For every field returned unchanged from the request body, assert the exact sent value.",
        subPoints: [
            "`is not None` / `not.toBeNull()` is only acceptable when the value is genuinely unknown — for server-generated timestamps or opaque IDs. This rule does not apply to computed fields.",
            "When a response field's value equals the value sent in the request body, path, or query, assert that exact value rather than a null-check — the sent value is known and reproducible.",
            "Also applies to response-only fields the server always returns the same value for (`filename_download`, `content_type`, `size`, `url`, enum status after creation). Asserting only the id on a multi-key resource response is not sufficient.",
            "Range matchers like `toBeGreaterThanOrEqual(0)` and type-only checks like `typeof X === 'number'` are not acceptable for fields whose exact values are known.",
            "Assert the exact status code from the recorded trace or `expected_response_body` — for example, `201` for create endpoints, not a generic `200`. Permissive status matchers are never acceptable.",
        ],
        examples: [
            {
                language: "javascript",
                code: `expect(getValue(response, "data.filename_download")).toBe("invoice.pdf");
expect(getValue(response, "data.type")).toBe("application/pdf");
expect(response.statusCode).toBe(201);`,
            },
            {
                language: "javascript",
                code: `expect(getResponseValue(productsPostResponse, "name"), 'name').toBe("Skyramp Tester");`,
            },
        ],
    },
    {
        title: "Error path (HTTP 4xx/5xx responses)",
        description: "For every HTTP 4xx/5xx response that includes a response body, assert every error body field with its exact value — including `error.code`, `error.message`, `detail`, `errors[0].message`, and `errors[0].extensions.code`.",
        subPoints: [
            "Also apply the array-validation rule to any errors array.",
            "Asserting only the status code is never sufficient when a body is present.",
            "For no-body responses such as a successful DELETE (204): assert the status code only.",
        ],
        examples: [
            {
                language: "javascript",
                code: `expect(response.statusCode).toBe(404);
expect(getValue(response, "errors.0.message")).toBe("Item not found");
expect(getValue(response, "errors.0.extensions.code")).toBe("RECORD_NOT_FOUND");
expect(getValue(response, "errors.1")).toBeUndefined();`,
            },
        ],
    },
    {
        title: "Value ranges",
        description: "For numeric fields where a realistic range is inferable from the field name, domain, or OpenAPI schema (`minimum` / `maximum`), assert the value falls within the expected range.",
        examples: [
            {
                language: "javascript",
                code: `expect(getResponseValue(productsPostResponse, "price")).toBeGreaterThanOrEqual(0);`,
            },
        ],
    },
    {
        title: "Specific known values",
        description: "For enum or status fields where only one outcome is valid for this flow, assert the exact expected value.",
        subPoints: [
            "When the OpenAPI schema defines allowed values, assert the schema-defined value.",
            "Also assert cross-field invariants when one field encodes the count or summary of another (e.g. `total_count == results.length`).",
        ],
        examples: [
            {
                language: "javascript",
                code: `expect(getResponseValue(ordersPostResponse, "status"), 'status').toBe("pending");`,
            },
        ],
    },
    {
        title: "Array / Items validation",
        description: "Only assert indices that exist in the recorded response — never infer array length from the request or scenario name.",
        subPoints: [
            "When the response is an empty array: assert the length is 0 and that the first index is absent. Never assert a field on index 0 of an empty array.",
            "When the response is a non-empty array: assert the exact length, key fields on each item, and that the index after the last item is absent.",
            "Shape-only checks such as `Array.isArray` or `typeof` are not sufficient when the response contains actual values.",
            "If the response is sorted or ordered: assert the ordering direction across the first two items.",
            "When the response is an empty or minimal body (an empty object, empty array, null, or only a few keys): assert the empty or minimal shape and the absence of error fields — do not stop at the status code.",
            "When the request includes pagination or filter parameters, assert the response reflects them.",
        ],
        examples: [
            {
                language: "javascript",
                code: `expect(getResponseValue(patchResponse, "items.0.product_id")).toBe(getResponseValue(productPostResponse, "id"));
expect(getResponseValue(patchResponse, "items.0.quantity")).toBe(2);
expect(getResponseValue(patchResponse, "items.1.product_id")).toBeNull();
expect(getValue(response, "data").length).toBeLessThanOrEqual(10);  // request sent limit=10`,
            },
            {
                language: "javascript",
                code: `expect(getValue(response, "results").length).toBe(2);
expect(getValue(response, "results.0.id")).toBe("prod_001");
expect(getValue(response, "results.2")).toBeUndefined();`,
            },
            {
                language: "javascript",
                code: `expect(getValue(response, "data").length).toBe(0);
expect(getValue(response, "data.0")).toBeUndefined();`,
            },
        ],
    },
    {
        title: "Format for server-generated fields",
        description: "For fields whose exact value varies across runs — UUIDs, auto-incremented IDs, ISO timestamps, IP addresses, emails, and URLs — assert the format or pattern rather than the exact value.",
        subPoints: [
            "UUID matches `/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i`.",
            "Auto-incremented ID is greater than 0.",
            "ISO timestamp matches `/^\\d{4}-\\d{2}-\\d{2}T/`.",
            "IP address matches IPv4 or IPv6 format.",
            "Email contains `@` and a domain.",
            "URL starts with `http://` or `https://`.",
            "Use `not.toBeNull()` / `is not None` only for truly opaque random tokens where no format is recognizable.",
            "Use format info from the OpenAPI schema (`format: uuid`, `format: date-time`) to identify these fields.",
        ],
        examples: [
            {
                language: "javascript",
                code: `expect(getResponseValue(productsPostResponse, "id")).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
expect(getResponseValue(productsPostResponse, "created_at")).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);`,
            },
        ],
    },
    {
        title: "Computed response fields, derived using dynamic formulas",
        description: "When the response contains a field whose value is derived from a calculation, assert the result using a formula built from prior response values and request inputs, not a hardcoded literal.",
        subPoints: [
            "Use a dynamic formula when the formula is visible in source.",
            "Fall back to the exact pre-computed value from `expected_response_body` only when no formula is knowable — all inputs must come from the inline request body or the response.",
        ],
        examples: [
            {
                language: "javascript",
                code: `expect(getResponseValue(patchResponse, "discount_amount")).toBe(getResponseValue(patchResponse, "total_amount") * (getResponseValue(patchResponse, "discount_value") / 100));`,
            },
            {
                language: "javascript",
                code: `expect(getResponseValue(patchResponse, "total_amount")).toBe(getResponseValue(productPostResponse, "price") * quantitySent);`,
            },
        ],
    },
];
export function renderRule(index, rule) {
    const subPoints = rule.subPoints && rule.subPoints.length > 0
        ? "\n" + rule.subPoints.map((p) => `- ${p}`).join("\n")
        : "";
    const examplesBlock = rule.examples.length > 0
        ? "\n" +
            rule.examples
                .map((ex) => `<example language="${ex.language}">
${ex.code}
</example>`)
                .join("\n")
        : "";
    return `${index}. ${rule.title}\n${rule.description}${subPoints}${examplesBlock}`;
}
function renderRules(rules) {
    return rules.map((rule, i) => renderRule(i + 1, rule)).join("\n\n");
}
export function getAssertionsPrompt(specificRules, scope, testFile, enhanceType) {
    const allRules = [...SHARED_RULES, ...specificRules];
    const ruleChecklistKeys = allRules
        .map((rule) => `    "${rule.title}": []`)
        .join(",\n");
    return `${getPersonaPrefix()}Your task is to enhance response body assertions in the given test file: \`${testFile}\`.${maintenanceTaskSuffix(enhanceType)}

### Pre-Edit Assertion Analysis
Before editing the given file, you must output a \`<thinking>\` block. The aim of the \`<thinking>\` block is to analyze each in-scope response in the given test file and output a JSON array that ensures no assertion rule is overlooked. The JSON array should match the template below — every assertion rule title must appear as a key in the \`rule_checklist\`, even when the value is \`[]\`.
1. Scan the given test file and expected responses based on the test recommendations for the code change tested.
2. Classify each response first by its response status type and then assign the applicable assertion rules to the response.
   1. Success with body (2xx with a response body): all assertion rules below may apply — echo-back of request fields, computed response fields, array / items validation, and chained values across steps.
   2. Success with no body (204, or 202 with empty body): assert the status code only. Also apply chained-values rules if a follow-up step uses this response's ID.
   3. Error response (4xx/5xx with a body): assert every error body field with its exact value plus array / items validation on the \`errors[]\` array (exact length + per-item fields + next index undefined). Status code alone is never sufficient when a body is present — for example, also assert \`errors.0.extensions.code == 'INVALID_PAYLOAD'\` and that \`errors.1\` is undefined.
3. For each in-scope response, output one JSON object using the template below. The output is an array — one object per in-scope response.
   - \`step\`: the HTTP method, path, and response variable name for this request (e.g. \`POST /products → products_POST_response\`).
   - \`response_status\`: one of \`success\`, \`no_body\`, or \`error\` based on the classification in step 2.
   - \`rule_checklist\`: an object that MUST contain every rule title below as a key. For each rule, the value is an array of assertion lines you will add for this response under that rule. Use \`[]\` only when the rule does not apply to this response — every key must still be present. This forces you to consider every rule for every response.

\`\`\`json
[{
  "step": "<METHOD> <path> → <responseVar>",
  "response_status": "success | no_body | error",
  "rule_checklist": {
${ruleChecklistKeys}
  }
}]
\`\`\`

### SDK Helpers
Access response body fields via the SDK helper already imported in the generated file (never dict/attribute access on the response variable): Python \`skyramp.get_response_value(response, "json.path")\`; TypeScript / JavaScript \`getValue(response, "json.path")\` or \`getResponseValue(response, "json.path")\` from \`@skyramp/skyramp\`; Java \`getValue(response, "json.path")\`.

### Assertion Rules with Examples
${renderRules(allRules)}

${scope}

### What not to do
- Do not access response fields via dict syntax (\`response["field"]\`) or attribute access (\`response.field\`) — always use the SDK helper.
- Do not assert \`not.toBeNull()\` / \`is not None\` on a field whose exact value is in the request body, prior response, or trace.
- Do not skip body assertions citing genuinely unpredictable fields — every assertable field still needs an assertion.
- Do not use permissive status matchers (\`.toMatch(/^2/)\`, \`.toBeGreaterThanOrEqual(200)\`, \`checkStatusCode(response, '20x')\`).
- Do not use shape-only or type-only assertions as a substitute for exact value validation. Forbidden patterns: \`Array.isArray(...)\`, \`typeof X === '...'\`, \`X instanceof Array\`, \`Object.keys(X).length > 0\`. When the recorded response contains actual values, assert them exactly.
- Do not use shape-only, containment-only, range-only, or weak-length as the sole assertion on a populated array.
- Do not swap between \`getValue\` and \`getResponseValue\` — keep whichever SDK helper the file already imports.
- Do not restructure, reformat, reorder, or modify existing code; do not add comments or docstrings.
- Do not change function signatures, imports, or variable names.
- Do not remove existing assertions.

### Verification of Assertions
After adding all assertion lines in the given test file, verify that every applicable rule has been applied correctly to each in-scope response. If any are missing or weakly applied, fix them before completing.`;
}
