import { z } from "zod";
import { baseTestSchema, baseTraceSchema, TestType, codeRefactoringSchema, } from "../../types/TestTypes.js";
import { TestGenerationService, } from "../../services/TestGenerationService.js";
import { AnalyticsService } from "../../services/AnalyticsService.js";
import { AUTH_CONFLICT_ERROR_MSG } from "../../prompts/test-recommendation/recommendationSections.js";
import { getPersonaPrefix } from "../../prompts/personas.js";
import { isTestbotEnabled } from "../../utils/featureFlags.js";
const ADD_ASSERTIONS_DEFAULT = isTestbotEnabled();
const integrationTestSchema = z
    .object({
    enhanceAssertions: z
        .boolean()
        .default(ADD_ASSERTIONS_DEFAULT)
        .describe("When true, calls skyramp_enhance_assertions after test generation to add richer response-body assertions. Disabled by default. Automatically enabled when running as testbot-feature. Do not override the default value of this parameter, unless the user explicitly asks to enable it."),
    ...baseTestSchema,
    chainingKey: z
        .string()
        .optional()
        .describe("JSON path of POST response that will be used for chaining. Example: If asked to use item_id to chain the requests, please ensure chainingKey is set to item_id."),
    trace: baseTraceSchema.shape.trace.optional(),
    include: baseTraceSchema.shape.include.optional(),
    exclude: baseTraceSchema.shape.exclude.optional(),
    scenarioFile: z
        .string()
        .endsWith(".json", { message: "scenarioFile must be a path to a .json file." })
        .optional()
        .describe("Absolute path to the scenario JSON file produced by skyramp_batch_scenario_test_generation. " +
        "When provided, DO NOT also pass apiSchema or endpointURL — the scenario file already contains all endpoint information."),
    ...codeRefactoringSchema.shape,
    ...baseTestSchema,
    apiSchema: baseTestSchema.apiSchema.describe("MUST be absolute path(/path/to/openapi.json) to the OpenAPI/Swagger schema file or a URL to the OpenAPI/Swagger schema file(e.g. https://demoshop.skyramp.dev/openapi.json). DO NOT TRY TO ASSUME THE OPENAPI SCHEMA IF NOT PROVIDED. NOTE TO AI ASSISTANTS: You do not need to read the contents of this file - simply pass the file path as the backend will read and process it. " +
        "When an OpenAPI schema is provided, this tool automatically derives a CRUD scenario flow (Create → Read → Update → Delete) directly from the schema."),
    output: baseTestSchema.output.describe("Name of the output test file. " +
        "When scenarioFile is provided and user did not specify a name, derive it: " +
        "strip the path and 'scenario_' prefix, replace hyphens/non-alphanum with underscores, append '_integration_test' + language extension. " +
        "Examples: " +
        "'scenario_orders-patch-add-items-recalculate.json' → 'orders_patch_add_items_recalculate_integration_test.py' (Python) or 'orders_patch_add_items_recalculate_integration_test.spec.ts' (Playwright). " +
        "'scenario_products-crud.json' → 'products_crud_integration_test.py'. " +
        "Extensions: '.py' for pytest, '.spec.ts'/'.spec.js' for Playwright, '.java' for JUnit. " +
        "NEVER use the default 'integration_test.py' when scenarioFile is set — it collides with other generated tests."),
    endpointURL: baseTestSchema.endpointURL.default(""),
})
    .omit({ method: true }).shape;
export class IntegrationTestService extends TestGenerationService {
    getTestType() {
        return TestType.INTEGRATION;
    }
    async generateTest(params) {
        const result = await super.generateTest(params);
        if (result.isError)
            return result;
        if (!params.enhanceAssertions)
            return result;
        const content = [...result.content];
        content.push({
            type: "text",
            text: this.buildAssertionEnhancementInstructions(),
        });
        return { ...result, content };
    }
    buildAssertionEnhancementInstructions() {
        return `
### CRITICAL NEXT STEP — Enhance response body assertions after each request
Call \`skyramp_enhance_assertions\` with \`testFile\` set to the absolute path of the generated test file, \`testType: "integration"\`, and \`enhanceType: "generation"\`. Apply every instruction returned to that file.
`;
    }
    buildGenerationOptions(params) {
        return {
            ...super.buildBaseGenerationOptions(params),
            responseData: params.responseData,
            playwrightInput: params.playwrightInput,
            scenarioFile: params.scenarioFile,
        };
    }
    async handleApiAnalysis(_params) {
        return null;
    }
}
const TOOL_NAME = "skyramp_integration_test_generation";
export function registerIntegrationTestTool(server) {
    server.registerTool(TOOL_NAME, {
        description: `${getPersonaPrefix()}Before calling this tool, you MUST output a <thinking> block that covers:
1. The endpoint URL(s) and HTTP method(s) involved in this multi-step workflow
2. Why an integration test (multi-step workflow validation) is the right choice for this intent
3. Assertion planning — answer all four for each step:
   a. Echo-back fields: which response fields are returned unchanged from the request body? List them by name — these get exact-value assertions, not just not-null checks
   b. Computed fields: are there derived numeric fields (e.g. total_amount = price × quantity)? Name the formula — these get formula-based assertions, never hardcoded numbers
   c. Chaining: which IDs in step N+1 come from step N's response? Name the JSON path (e.g. POST /orders response → order_id → GET /orders/{order_id}). Every ID must be dynamically chained — no hardcoded IDs anywhere in the test
   d. Edge cases and business logic bugs: if \`skyramp_analyze_changes\` already ran and returned recommendations for these endpoints, revisit them now — did the recommendation phase flag any boundary conditions, invalid-input behaviors, or business logic bugs? Each flagged risk should map to a targeted assertion in this test. A test that doesn't target a known risk is low-value.
4. Test data isolation: does this test create all its own resources from scratch, or does it depend on pre-existing database state? All prerequisite resources must be created within the test setup.
If any required parameter cannot be determined without guessing, STOP and ask the user before calling the tool.

---

Generate an integration test from a scenario file or a live endpoint trace.

**Two mutually exclusive modes — choose exactly one:**
1. **Scenario mode** (preferred for multi-step flows): pass \`scenarioFile\` (absolute path to the .json file returned by skyramp_batch_scenario_test_generation). Do NOT pass \`apiSchema\` or \`endpointURL\` in this mode. Passing both causes: "scenarioFile is mutually exclusive with apiSchema and endpointURL."
2. **Direct mode**: pass \`endpointURL\` and optionally \`apiSchema\`. Do NOT pass \`scenarioFile\`.

**Auth — scenario mode only:**
- If workspace has \`api.authType\` set: omit ALL auth params — workspace config handles the Bearer prefix. Passing auth alongside workspace authType causes: "${AUTH_CONFLICT_ERROR_MSG}"
- If workspace has no \`api.authType\`: pass \`authHeader\` only (no \`authScheme\`, no \`authToken\`).

**Output filename:** When \`scenarioFile\` is provided and user did not specify a name, derive it: strip path and 'scenario_' prefix, replace hyphens/non-alphanum with underscores, append '_integration_test' + language extension. Example: 'scenario_orders-patch.json' → 'orders_patch_integration_test.py'. Never use the default 'integration_test.py' when scenarioFile is set — it collides.

**IMPORTANT:** If \`apiSchema\` is provided in direct mode, pass the path/URL as-is — do NOT read the file contents. The backend processes it.`,
        inputSchema: integrationTestSchema,
    }, async (params) => {
        if (params.scenarioFile && (params.apiSchema || params.endpointURL)) {
            return {
                content: [{
                        type: "text",
                        text: "**skyramp_integration_test_generation Error: Conflicting parameters**\n\n" +
                            "`scenarioFile` is mutually exclusive with `apiSchema` and `endpointURL`.\n\n" +
                            "**Received:** scenarioFile=" + params.scenarioFile +
                            (params.apiSchema ? ", apiSchema=" + params.apiSchema : "") +
                            (params.endpointURL ? ", endpointURL=" + params.endpointURL : "") + "\n\n" +
                            "**How to fix:** Remove `apiSchema` and `endpointURL` when passing `scenarioFile` — " +
                            "the scenario file already contains all endpoint and schema information.",
                    }],
                isError: true,
            };
        }
        const service = new IntegrationTestService();
        const result = await service.generateTest(params);
        AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
            // Silently ignore analytics errors
        });
        return result;
    });
}
