"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDatabaseQueryStepWithUpdatedReusableSnippet = createDatabaseQueryStepWithUpdatedReusableSnippet;
exports.humanizeDatabaseQueryStep = humanizeDatabaseQueryStep;
exports.databaseQueryStepToAIString = databaseQueryStepToAIString;
const BaseStep_1 = __importDefault(require("./BaseStep"));
const mablscriptFind_1 = require("@mablhq/test-model/mablscriptFind");
const type_utils_1 = require("../../type-utils");
const variable_names_util_1 = require("../../variable-names-util");
const mablscript_step_types_1 = require("../../mablscript-step-types");
const variable_constants_1 = require("../../variable-constants");
const SnippetsDescriptor_1 = require("@mablhq/test-model/types/SnippetsDescriptor");
const snippetUtils_1 = require("../snippetUtils");
const yaml_utils_1 = require("../yaml-utils");
class DatabaseQueryStep extends BaseStep_1.default {
    static getStepType() {
        return mablscript_step_types_1.StepType.DATABASE_QUERY;
    }
    getSnippetInvariantId() {
        return (0, SnippetsDescriptor_1.isReusableSnippetDescriptor)(this.snippetDescriptor)
            ? this.snippetDescriptor.snippetInvariantId
            : undefined;
    }
    constructor(snippetDescriptor, queryAssertions, queryVariables, stepOptions, queryResult, variables, reusableSnippet) {
        super(DatabaseQueryStep.getStepType());
        this.snippetDescriptor = snippetDescriptor;
        this.queryAssertions = queryAssertions;
        this.queryVariables = queryVariables;
        this.stepOptions = stepOptions;
        this.queryResult = queryResult;
        this.variables = variables;
        this.reusableSnippet = reusableSnippet;
        this.options = {
            editable: true,
        };
    }
    generateMablscript() {
        const descriptor = {
            snippet: this.snippetDescriptor,
            assertions: this.queryAssertions,
            variables: this.queryVariables,
            options: this.stepOptions,
        };
        return `database_query(${(0, mablscriptFind_1.buildStepArgumentString)({
            params: { ...descriptor },
            legacy: false,
        })})`;
    }
    inputVariables() {
        const item = {
            request: this.inputVariablesFromSnippet(),
            queryAssertions: this.queryAssertions,
            queryVariables: this.queryVariables,
        };
        return (0, type_utils_1.distinct)((0, variable_names_util_1.extractVariableNamesFromPattern)(JSON.stringify(item)));
    }
    inputVariablesFromSnippet() {
        if ((0, SnippetsDescriptor_1.isInlineSnippetDescriptor)(this.snippetDescriptor) ||
            (0, SnippetsDescriptor_1.isReusableSnippetDescriptor)(this.snippetDescriptor) ||
            (0, snippetUtils_1.getResolvedSnippetParameters)(this) !== undefined) {
            return (0, snippetUtils_1.getVariableReferencesFromSnippetParameters)(this);
        }
        return undefined;
    }
    static generateStepFromMablStep(mablDatabaseQueryStep, importer) {
        const { snippet: snippetDescriptor, assertions: queryAssertions, variables: queryVariables, options, } = mablDatabaseQueryStep.descriptor;
        const reusableSnippet = (0, SnippetsDescriptor_1.isReusableSnippetDescriptor)(snippetDescriptor)
            ? importer.snippets[snippetDescriptor.snippetInvariantId]
            : undefined;
        let variables = [];
        if (queryVariables) {
            variables = queryVariables.map(({ name: variableName }) => {
                const userScopedVariableName = `user.${variableName}`;
                const variable = {
                    name: userScopedVariableName,
                    humanizedName: variableName,
                    variableType: mablscriptFind_1.VariableType.DATABASE,
                    value: variable_constants_1.EMPTY_VARIABLE_VALUE_PLACEHOLDER,
                    query: mablDatabaseQueryStep.descriptor,
                };
                importer.variables[userScopedVariableName] = { ...variable };
                return variable;
            });
        }
        return new DatabaseQueryStep(snippetDescriptor, queryAssertions, queryVariables, options, undefined, variables, reusableSnippet);
    }
    static create(descriptor) {
        const step = new DatabaseQueryStep(descriptor.snippetDescriptor, descriptor.queryAssertions, descriptor.queryVariables, descriptor.stepOptions, descriptor.queryResult, descriptor.variables ?? [], descriptor.snippet);
        step.generateStepId();
        return step;
    }
}
exports.default = DatabaseQueryStep;
function createDatabaseQueryStepWithUpdatedReusableSnippet(step, snippet) {
    if (!(0, SnippetsDescriptor_1.isReusableSnippetDescriptor)(step.snippetDescriptor) ||
        step.getSnippetInvariantId() !== snippet.invariant_id) {
        return step;
    }
    const newDescriptor = structuredClone(step.snippetDescriptor);
    newDescriptor.snippetInvariantId = snippet.invariant_id;
    newDescriptor.snippetId = snippet.id;
    return new DatabaseQueryStep(newDescriptor, step.queryAssertions, step.queryVariables, step.stepOptions, step.queryResult, step.variables, snippet);
}
function humanizeDatabaseQueryStep(step) {
    let humanized = '';
    let snippetName = '';
    if ((0, SnippetsDescriptor_1.isInlineSnippetDescriptor)(step.snippetDescriptor)) {
        snippetName = step.snippetDescriptor?.inlineSnippet?.name ?? '';
        humanized = `Execute one-time database query`;
    }
    else if (step.reusableSnippet) {
        snippetName = step.reusableSnippet?.name ?? '';
        humanized = 'Execute reusable database query';
    }
    else {
        humanized = 'Execute database query';
    }
    if (snippetName) {
        humanized += ` "${snippetName}"`;
    }
    const humanizedAssertions = humanizeAssertions(step);
    const humanizedVariables = humanizeVariables(step);
    if (humanizedAssertions && humanizedVariables) {
        humanized += `, ${humanizedAssertions}, and ${humanizedVariables}`;
    }
    else if (humanizedAssertions) {
        humanized += ` and ${humanizedAssertions}`;
    }
    else if (humanizedVariables) {
        humanized += ` and ${humanizedVariables}`;
    }
    return humanized;
}
function humanizeAssertions(step) {
    switch (step.queryAssertions?.length) {
        case undefined:
        case 0:
            return '';
        case 1:
            return `assert on ${humanizeSourceAndTarget(step.queryAssertions[0])}`;
        default:
            return `perform ${step.queryAssertions?.length} assertions`;
    }
}
function humanizeVariables(step) {
    switch (step.queryVariables?.length) {
        case undefined:
        case 0:
            return '';
        case 1:
            return `store ${humanizeSourceAndTarget(step.queryVariables[0])} to variable ${step.queryVariables[0].name}`;
        default:
            return `store ${step.queryVariables?.length} variables`;
    }
}
function humanizeSourceAndTarget(assertOrVariable) {
    if (assertOrVariable.source === 'metadata') {
        return `"${assertOrVariable.target}" from the query metadata`;
    }
    const target = assertOrVariable.target?.split('.');
    if (!target || target.length > 2 || Number.isNaN(parseInt(target[0]))) {
        return `"${target}" of the result`;
    }
    const row = parseInt(target[0]);
    const column = target[1];
    if (!column) {
        return `row ${row}`;
    }
    return `"${column}" of row ${row}`;
}
function databaseQueryStepToAIString(step) {
    const result = {
        stepType: step.stepType,
    };
    if ((0, SnippetsDescriptor_1.isInlineSnippetDescriptor)(step.snippetDescriptor)) {
        result.snippet_name = step.snippetDescriptor.inlineSnippet?.name;
        result.snippet_type = 'one_time';
    }
    else if ((0, SnippetsDescriptor_1.isReusableSnippetDescriptor)(step.snippetDescriptor)) {
        result.snippet_name = step.reusableSnippet?.name;
        result.snippet_type = 'reusable';
    }
    if (step.queryAssertions && step.queryAssertions.length > 0) {
        result.assertions = step.queryAssertions.map((a) => ({
            source: a.source,
            target: a.target,
            assertion: a.assertion,
            value: a.value,
        }));
    }
    if (step.queryVariables && step.queryVariables.length > 0) {
        result.variables = step.queryVariables.map((v) => ({
            source: v.source,
            target: v.target,
            name: v.name,
        }));
    }
    return (0, yaml_utils_1.toYaml)(result);
}
