"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MIN_ANCESTOR_WEIGHT_PERCENTAGE = exports.ANCESTOR_BOUNDS_WEIGHT = void 0;
const FindStrategy_1 = __importDefault(require("../strategies/FindStrategy"));
const mablscriptFind_1 = require("@mablhq/test-model/mablscriptFind");
const messaging_1 = require("@mablhq-internal/runtime/core/messaging/messaging");
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
exports.ANCESTOR_BOUNDS_WEIGHT = 400;
exports.MIN_ANCESTOR_WEIGHT_PERCENTAGE = 0.05;
class AncestorAndTargetFindStrategy extends FindStrategy_1.default {
    mobileFindExecutionConfig;
    ancestorFindStrategy;
    targetFindStrategy;
    constructor(mobileFindExecutionConfig, ancestorFindStrategy, targetFindStrategy) {
        super();
        this.mobileFindExecutionConfig = mobileFindExecutionConfig;
        this.ancestorFindStrategy = ancestorFindStrategy;
        this.targetFindStrategy = targetFindStrategy;
    }
    getName() {
        return 'AncestorAndTargetFindStrategy';
    }
    isApplicable(descriptor) {
        return (super.isApplicable(descriptor) &&
            !this.getPlatformSelector(descriptor)?.find_prompt);
    }
    async findElement(driver, mobileExecutionContext, descriptor, isShortCircuited) {
        const targetElementFindResult = await this.targetFindStrategy.findElement(driver, mobileExecutionContext, descriptor, isShortCircuited);
        const auxiliaryDescriptor = descriptor.findTarget.auxiliaryDescriptors?.[0];
        if (!auxiliaryDescriptor ||
            !targetElementFindResult ||
            targetElementFindResult.scoredCandidates.length <= 1) {
            return targetElementFindResult;
        }
        const targetScoreDifference = targetElementFindResult.scoredCandidates[0].score -
            targetElementFindResult.scoredCandidates[1].score;
        if (targetScoreDifference >= exports.ANCESTOR_BOUNDS_WEIGHT) {
            mobileExecutionContext.log.debug(`Skipped finding ancestor due to target score difference: ${targetScoreDifference}`, {
                executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
            });
            return targetElementFindResult;
        }
        mobileExecutionContext.log.debug('Looking for ancestor element...', {
            executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
        });
        const ancestorDescriptor = {
            findTarget: auxiliaryDescriptor,
            findType: mablscriptFind_1.FindType.FIND_ONE,
        };
        const ancestorElementFindResult = await this.ancestorFindStrategy.findElement(driver, mobileExecutionContext, ancestorDescriptor, isShortCircuited);
        const platform = this.mobileFindExecutionConfig.platform;
        if (platform === mablApi_1.MobilePlatformEnum.Android) {
            this.adjustTargetCandidateScoreByAncestorBounds(targetElementFindResult, ancestorElementFindResult, mobileExecutionContext);
        }
        else {
            this.adjustTargetCandidateScoreByAncestorBounds(targetElementFindResult, ancestorElementFindResult, mobileExecutionContext);
        }
        const bestCandidate = targetElementFindResult.scoredCandidates[0];
        if (bestCandidate?.elementReference) {
            return {
                context: targetElementFindResult.context,
                elementReference: bestCandidate.elementReference,
                scoredCandidates: targetElementFindResult.scoredCandidates,
            };
        }
        return undefined;
    }
    adjustTargetCandidateScoreByAncestorBounds(targetElementFindResult, ancestorElementFindResult, mobileExecutionContext) {
        if (!!ancestorElementFindResult?.scoredCandidates?.length) {
            let ancestorBoundsWeight = exports.ANCESTOR_BOUNDS_WEIGHT;
            const scoredAncestorCandidates = ancestorElementFindResult.scoredCandidates;
            if (scoredAncestorCandidates.length > 1) {
                const scoreDiffPercent = Math.min(exports.MIN_ANCESTOR_WEIGHT_PERCENTAGE, (scoredAncestorCandidates[0].score -
                    scoredAncestorCandidates[1].score) /
                    scoredAncestorCandidates[0].score);
                ancestorBoundsWeight = ancestorBoundsWeight * scoreDiffPercent;
            }
            mobileExecutionContext.log.debug(`Ancestor bounds weight: ${ancestorBoundsWeight}`, {
                executionPhase: messaging_1.ExecutionPhase.DURING_ACTION,
            });
            const ancestorElemRef = ancestorElementFindResult.elementReference;
            const { rect: ancestorRect } = ancestorElemRef;
            if (!!ancestorRect) {
                const { x: ancestorX, y: ancestorY, height: ancestorHeight, width: ancestorWidth, } = ancestorRect;
                const targetCandidates = targetElementFindResult.scoredCandidates;
                targetCandidates.forEach((candidate) => {
                    const { elementReference, score } = candidate;
                    const { rect } = elementReference;
                    if (!rect) {
                        return;
                    }
                    const { x, y, height, width } = rect;
                    const isEncapsulatedByAncestor = ancestorX <= x &&
                        ancestorY <= y &&
                        ancestorX + ancestorWidth >= x + width &&
                        ancestorY + ancestorHeight >= y + height;
                    if (isEncapsulatedByAncestor) {
                        candidate.score = score + ancestorBoundsWeight;
                    }
                });
                targetCandidates.sort((a, b) => b.score - a.score);
            }
        }
    }
}
exports.default = AncestorAndTargetFindStrategy;
