"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractValidInputSchemaForField = void 0;
const ts_morph_1 = require("ts-morph");
function extractValidInputSchemaForField(args) {
    const type = args.typeAtNode.getType();
    const symbol = type.getSymbol();
    const declarations = symbol?.getDeclarations() ?? [];
    // Try to resolve to a class declaration (page object).
    for (const decl of declarations) {
        const classDecl = decl.asKind(ts_morph_1.SyntaxKind.ClassDeclaration);
        if (!classDecl)
            continue;
        const schema = extractFromClassConstructorAssignment(classDecl, args.fieldName);
        if (schema)
            return schema;
    }
    // Fallback: if it's an intersection/anonymous type, we still might have class in base types.
    const baseTypes = type.getBaseTypes();
    for (const base of baseTypes) {
        const baseSymbol = base.getSymbol();
        const baseDecls = baseSymbol?.getDeclarations() ?? [];
        for (const decl of baseDecls) {
            const classDecl = decl.asKind(ts_morph_1.SyntaxKind.ClassDeclaration);
            if (!classDecl)
                continue;
            const schema = extractFromClassConstructorAssignment(classDecl, args.fieldName);
            if (schema)
                return schema;
        }
    }
    return null;
}
exports.extractValidInputSchemaForField = extractValidInputSchemaForField;
function extractFromClassConstructorAssignment(classDecl, fieldName) {
    const ctor = classDecl.getConstructors()[0];
    if (!ctor)
        return null;
    const assignments = ctor
        .getDescendantsOfKind(ts_morph_1.SyntaxKind.BinaryExpression)
        .filter((b) => b.getOperatorToken().getText() === '=')
        .filter((b) => {
        const left = b.getLeft();
        return (ts_morph_1.Node.isPropertyAccessExpression(left) &&
            left.getExpression().getText() === 'this' &&
            left.getName() === fieldName);
    });
    for (const assignment of assignments) {
        const right = assignment.getRight();
        const obj = right.asKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
        if (!obj)
            continue;
        const schema = extractSchemaFromObjectLiteral(obj);
        if (schema)
            return schema;
        // Also allow schema to come from spreads in the literal.
        const spreads = obj.getProperties().filter(ts_morph_1.Node.isSpreadAssignment);
        for (const spread of spreads) {
            const spreadExpr = spread.getExpression();
            const schemaFromSpread = resolveSchemaFromExpression(spreadExpr);
            if (schemaFromSpread)
                return schemaFromSpread;
        }
    }
    return null;
}
function extractSchemaFromObjectLiteral(obj) {
    for (const prop of obj.getProperties()) {
        if (!ts_morph_1.Node.isPropertyAssignment(prop))
            continue;
        if (prop.getName() !== '_schema')
            continue;
        const schemaObj = prop.getInitializerIfKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
        if (!schemaObj)
            continue;
        const minlength = getNumberProp(schemaObj, 'minlength');
        const maxlength = getNumberProp(schemaObj, 'maxlength');
        const violationEffect = getStringProp(schemaObj, 'violationEffect');
        if (typeof minlength === 'number' &&
            typeof maxlength === 'number' &&
            typeof violationEffect === 'string') {
            return {
                _schema: {
                    minlength,
                    maxlength,
                    violationEffect: violationEffect,
                },
            };
        }
    }
    return null;
}
function resolveSchemaFromExpression(expr) {
    // Identifier -> variable initializer
    if (ts_morph_1.Node.isIdentifier(expr)) {
        const decl = expr.getDefinitions()[0]?.getDeclarationNode();
        const varDecl = decl?.asKind(ts_morph_1.SyntaxKind.VariableDeclaration);
        const init = varDecl?.getInitializerIfKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
        if (init) {
            return extractSchemaFromObjectLiteral(init);
        }
    }
    // Inline object literals.
    const inlineObj = expr.asKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
    if (inlineObj)
        return extractSchemaFromObjectLiteral(inlineObj);
    return null;
}
function getNumberProp(obj, name) {
    const prop = obj.getProperty(name);
    if (!prop || !ts_morph_1.Node.isPropertyAssignment(prop))
        return null;
    const init = prop.getInitializer();
    if (!init)
        return null;
    const num = init.asKind(ts_morph_1.SyntaxKind.NumericLiteral);
    if (num)
        return Number(num.getText());
    return null;
}
function getStringProp(obj, name) {
    const prop = obj.getProperty(name);
    if (!prop || !ts_morph_1.Node.isPropertyAssignment(prop))
        return null;
    const init = prop.getInitializer();
    if (!init)
        return null;
    const str = init.asKind(ts_morph_1.SyntaxKind.StringLiteral);
    if (str)
        return str.getLiteralText();
    return null;
}
