"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadFormDataJson = loadFormDataJson;
exports.validateFormDataRecord = validateFormDataRecord;
exports.fillFormFromData = fillFormFromData;
exports.findFormScope = findFormScope;
exports.fillFormField = fillFormField;
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const ui_controls_1 = require("./ui-controls");
async function loadFormDataJson(filePath, cwd = process.cwd()) {
    const resolvedPath = node_path_1.default.resolve(cwd, filePath);
    const raw = await promises_1.default.readFile(resolvedPath, 'utf8');
    const parsed = JSON.parse(raw);
    return validateFormDataRecord(parsed, filePath);
}
function validateFormDataRecord(value, sourceName = 'form data') {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        throw new Error(`${sourceName} must contain a JSON object of field names and values.`);
    }
    const record = {};
    for (const [fieldName, fieldValue] of Object.entries(value)) {
        if (!isSupportedFormValue(fieldValue)) {
            throw new Error(`${sourceName} field "${fieldName}" must be a string, number, boolean, or null.`);
        }
        record[fieldName] = fieldValue;
    }
    return record;
}
async function fillFormFromData(page, data, formSelector, options) {
    const form = await findFormScope(page, formSelector);
    for (const [fieldName, fieldValue] of Object.entries(data)) {
        await fillFormField(form, fieldName, fieldValue, options);
    }
}
async function findFormScope(page, formSelector) {
    const scope = await firstVisible([
        formSelector
            ? page.locator(buildFormSelector(formSelector))
            : page.locator('form, foundation-form, [data-test-id$="-form"]')
    ]) ?? await firstExisting([
        formSelector
            ? page.locator(buildFormSelector(formSelector))
            : page.locator('form, foundation-form, [data-test-id$="-form"]')
    ]);
    if (!scope || !await safeExists(scope)) {
        throw new Error(formSelector ? `Form "${formSelector}" was not found.` : 'No visible form was found.');
    }
    return scope;
}
async function fillFormField(form, fieldName, value, options) {
    if (value === null) {
        await clearField(form, fieldName, options);
        return;
    }
    if (typeof value === 'boolean') {
        await setBooleanField(form, fieldName, value);
        return;
    }
    const textValue = String(value);
    if (await clickOptionField(form, fieldName, textValue)) {
        return;
    }
    if (await fillInputField(form, fieldName, textValue, options)) {
        return;
    }
    if (await fillCounterpartyField(form, fieldName, textValue)) {
        return;
    }
    throw new Error(`Field "${fieldName}" was not found or could not be filled.`);
}
async function clearField(form, fieldName, options) {
    const candidates = await buildInputCandidates(form, fieldName);
    for (const field of candidates) {
        if (!await safeExists(field) || !await safeIsVisible(field)) {
            continue;
        }
        try {
            await field.fill('');
            await dispatchInputEvents(field);
        }
        catch {
            continue;
        }
        await (0, ui_controls_1.assertLocatorValue)(field, '', `Field "${fieldName}"`, (0, ui_controls_1.resolveFieldVerificationRule)(fieldName, options?.fieldVerification));
        return;
    }
    throw new Error(`Field "${fieldName}" was not found and could not be cleared.`);
}
async function fillInputField(form, fieldName, value, options) {
    const candidates = await buildInputCandidates(form, fieldName);
    for (const field of candidates) {
        if (!await safeExists(field) || !await safeIsVisible(field)) {
            continue;
        }
        try {
            await field.fill(value);
            await dispatchInputEvents(field);
        }
        catch {
            continue;
        }
        await (0, ui_controls_1.assertLocatorValue)(field, value, `Field "${fieldName}"`, (0, ui_controls_1.resolveFieldVerificationRule)(fieldName, options?.fieldVerification));
        return true;
    }
    return false;
}
async function setBooleanField(form, fieldName, checked) {
    const candidates = [
        ...buildBooleanFieldSelectors(fieldName).map((selector) => form.locator(selector)),
        form.getByLabel(fieldName),
        form.locator(buildFieldSelector(fieldName))
    ];
    const field = await firstVisible(candidates) ?? await firstExisting(candidates);
    if (!field) {
        throw new Error(`Boolean field "${fieldName}" was not found.`);
    }
    if (checked) {
        await field.check().catch(async () => field.click());
    }
    else {
        await field.uncheck().catch(async () => {
            if (await isCheckedLike(field)) {
                await field.click();
            }
        });
    }
}
async function clickOptionField(form, fieldName, value) {
    const fieldContainer = await findFieldContainer(form, fieldName);
    const candidates = fieldContainer ? [
        fieldContainer.getByRole('radio', { name: value }),
        fieldContainer.getByRole('button', { name: value }),
        fieldContainer.getByText(value, { exact: true })
    ] : [];
    candidates.push(form.getByRole('radio', { name: value }), form.getByRole('button', { name: value }));
    const option = await firstVisible(candidates);
    if (!option) {
        return false;
    }
    await option.click();
    return true;
}
async function fillCounterpartyField(form, fieldName, value) {
    if (!isCounterpartyField(fieldName)) {
        return false;
    }
    const trigger = await firstVisible([
        form.locator('[data-test-id="counterparty-trigger"]'),
        form.getByRole('combobox', { name: /counterparty/i })
    ]);
    if (!trigger) {
        return false;
    }
    await trigger.click();
    const page = form.page();
    const search = await firstVisible([page.getByLabel(/search counterparties/i)]) ?? await firstExisting([page.getByLabel(/search counterparties/i)]);
    if (search && await safeExists(search)) {
        await search.fill(value);
    }
    const row = await firstVisible([page.locator(`[data-test-id="counterparty-row-${escapeAttribute(value)}"]`)]);
    if (row && await safeExists(row)) {
        await row.click();
        return true;
    }
    const option = await firstVisible([page.getByText(value, { exact: true })]);
    if (option && await safeExists(option)) {
        await option.click();
        return true;
    }
    throw new Error(`Counterparty value "${value}" was not found.`);
}
async function findInputCandidate(form, fieldName) {
    return firstVisible(await buildInputCandidates(form, fieldName));
}
async function buildInputCandidates(form, fieldName) {
    const fieldContainer = await findFieldContainer(form, fieldName);
    const scopedCandidates = fieldContainer ? [
        fieldContainer.locator('input, textarea, select'),
        fieldContainer.locator('rapid-text-field input, rapid-number-field input, rapid-combobox input')
    ] : [];
    return expandCandidates([
        ...buildFoundationFieldSelectors(fieldName).map((selector) => form.locator(selector)),
        ...scopedCandidates,
        form.getByLabel(fieldName).locator('input, textarea, select'),
        form.getByLabel(fieldName),
        form.locator(buildFieldSelector(fieldName)).locator('input, textarea, select'),
        form.locator(buildFieldSelector(fieldName))
    ]);
}
async function findFieldContainer(form, fieldName) {
    const escapedText = escapeText(fieldName);
    const candidates = [
        form.locator(`control-wrapper:has-text("${escapedText}")`),
        form.locator(`[data-test-id="${escapeAttribute(fieldName)}-pair-row"]`),
        form.locator(`label:has-text("${escapedText}")`).locator('xpath=ancestor::*[self::div or self::control-wrapper][1]')
    ];
    return await firstVisible(candidates) ?? await firstExisting(candidates);
}
async function dispatchInputEvents(field) {
    await field.evaluate((element) => {
        element.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
        element.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
        element.dispatchEvent(new Event('blur', { bubbles: true, composed: true }));
    }).catch(() => undefined);
}
async function firstVisible(locators) {
    for (const locator of locators) {
        for (const match of await expandLocator(locator)) {
            if (await safeIsVisible(match)) {
                return match;
            }
        }
    }
    return undefined;
}
async function firstExisting(locators) {
    for (const locator of locators) {
        for (const match of await expandLocator(locator)) {
            return match;
        }
    }
    return undefined;
}
async function safeExists(locator) {
    try {
        return await locator.count() > 0;
    }
    catch {
        return false;
    }
}
async function safeIsVisible(locator) {
    try {
        return await locator.isVisible();
    }
    catch {
        return false;
    }
}
async function expandCandidates(locators) {
    const matches = [];
    for (const locator of locators) {
        matches.push(...await expandLocator(locator));
    }
    return matches;
}
async function expandLocator(locator) {
    try {
        const count = await locator.count();
        const matches = [];
        for (let index = 0; index < count; index += 1) {
            matches.push(locator.nth(index));
        }
        return matches;
    }
    catch {
        return [];
    }
}
function buildFormSelector(formSelector) {
    const escaped = escapeAttribute(formSelector);
    return [
        `[data-test-id="${escaped}"]`,
        `#${cssEscape(formSelector)}`,
        formSelector
    ].join(', ');
}
function buildFieldSelector(fieldName) {
    const escaped = escapeAttribute(fieldName);
    const cssEscaped = cssEscape(fieldName);
    return [
        `[data-test-id="${escaped}"]`,
        `[data-testid="${escaped}"]`,
        `[name="${escaped}"]`,
        `[aria-label="${escaped}"]`,
        `#${cssEscaped}`
    ].join(', ');
}
function buildFoundationFieldSelectors(fieldName) {
    const fieldKeys = unique([
        fieldName,
        fieldName.replace(/\*+$/g, '').trim(),
        toSchemaFieldName(fieldName),
        ...getFieldAliases(fieldName)
    ].filter(Boolean));
    return fieldKeys.flatMap((fieldKey) => {
        const escaped = escapeAttribute(fieldKey);
        const cssEscaped = cssEscape(fieldKey);
        return [
            `rapid-text-field[data-test-id="${escaped}"]`,
            `rapid-text-field[data-test-id="${escaped}"] input`,
            `rapid-text-field#${cssEscaped}`,
            `rapid-text-field#${cssEscaped} input`,
            `rapid-number-field[data-test-id="${escaped}"]`,
            `rapid-number-field[data-test-id="${escaped}"] input`,
            `rapid-number-field#${cssEscaped}`,
            `rapid-number-field#${cssEscaped} input`,
            `rapid-combobox[data-test-id="${escaped}"]`,
            `rapid-combobox[data-test-id="${escaped}"] input`,
            `rapid-combobox#${cssEscaped}`,
            `rapid-combobox#${cssEscaped} input`,
            `[data-test-id="${escaped}"] input`,
            `#${cssEscaped} input`,
            `[data-test-id="${escaped}"] textarea`,
            `#${cssEscaped} textarea`,
            `[data-test-id="${escaped}"] select`,
            `#${cssEscaped} select`
        ];
    });
}
function buildBooleanFieldSelectors(fieldName) {
    const fieldKeys = unique([
        fieldName,
        fieldName.replace(/\*+$/g, '').trim(),
        toSchemaFieldName(fieldName),
        ...getFieldAliases(fieldName)
    ].filter(Boolean));
    return fieldKeys.flatMap((fieldKey) => {
        const escaped = escapeAttribute(fieldKey);
        const cssEscaped = cssEscape(fieldKey);
        return [
            `rapid-switch[data-test-id="${escaped}"]`,
            `rapid-switch#${cssEscaped}`,
            `rapid-checkbox[data-test-id="${escaped}"]`,
            `rapid-checkbox#${cssEscaped}`,
            `[data-test-id="${escaped}"] input[type="checkbox"]`,
            `#${cssEscaped} input[type="checkbox"]`
        ];
    });
}
function toSchemaFieldName(fieldName) {
    return fieldName
        .replace(/\*+$/g, '')
        .trim()
        .replace(/[^a-zA-Z0-9]+/g, '_')
        .replace(/^_+|_+$/g, '')
        .toUpperCase();
}
function getFieldAliases(fieldName) {
    const normalized = toSchemaFieldName(fieldName);
    const aliases = {
        PREMIUM_BPS: ['QUOTED_PREMIUM_BP']
    };
    return aliases[normalized] ?? [];
}
function unique(values) {
    return [...new Set(values)];
}
async function isCheckedLike(field) {
    const nativeChecked = await field.isChecked().catch(() => undefined);
    if (nativeChecked !== undefined) {
        return nativeChecked;
    }
    return field.evaluate((element) => {
        const value = element.checked;
        if (typeof value === 'boolean') {
            return value;
        }
        return element.getAttribute('aria-checked') === 'true';
    }).catch(() => false);
}
function isSupportedFormValue(value) {
    return value === null
        || typeof value === 'string'
        || typeof value === 'number'
        || typeof value === 'boolean';
}
function isCounterpartyField(fieldName) {
    const normalized = fieldName.trim().replace(/[\s_-]+/g, '').toLowerCase();
    return normalized === 'counterparty' || normalized === 'cpty';
}
function escapeText(value) {
    return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
function escapeAttribute(value) {
    return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
function cssEscape(value) {
    return value.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1').replace(/\s/g, '\\ ');
}
