import * as i0 from '@angular/core';
import { signal, Injectable, InjectionToken, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { TranslocoService } from '@jsverse/transloco';
import { ConfirmationService } from '@masterteam/components/confirmation';
import { ModalService } from '@masterteam/components/modal';
import { ToastService } from '@masterteam/components/toast';
import { firstValueFrom, map } from 'rxjs';

function resolveActionLabel(action) {
    const name = action.actionName;
    if (typeof name === 'string') {
        return name || action.actionKey;
    }
    return name?.display || action.actionKey;
}

function buildActionPayload(action, formValue, externalResolvers = {}) {
    const templatePayload = buildTemplatePayload(action, formValue, externalResolvers);
    if (templatePayload) {
        return templatePayload;
    }
    const payloadKeys = action.payloadKeys ?? [];
    if (!payloadKeys.length) {
        return undefined;
    }
    const payload = {};
    for (const key of payloadKeys) {
        const formFieldValue = formValue?.[key];
        if (formFieldValue !== undefined) {
            payload[key] = normalizePayloadValue(key, formFieldValue);
            continue;
        }
        const externalValue = resolveExternalPayloadValue(key, action, externalResolvers);
        if (externalValue !== undefined) {
            payload[key] = externalValue;
            continue;
        }
        const defaultValue = resolveDefaultPayloadValue(key);
        if (defaultValue !== undefined) {
            payload[key] = defaultValue;
        }
    }
    return Object.keys(payload).length ? payload : undefined;
}
function buildTemplatePayload(action, formValue, externalResolvers) {
    const template = action.payloadTemplate;
    if (!template || typeof template !== 'object') {
        return null;
    }
    const payload = {};
    const payloadKeys = action.payloadKeys ?? Object.keys(template);
    for (const key of payloadKeys) {
        if (key === 'fields') {
            payload[key] = resolveTemplateFieldsValue(action, template, formValue);
            continue;
        }
        const formFieldValue = formValue?.[key];
        if (formFieldValue !== undefined) {
            payload[key] = normalizePayloadValue(key, formFieldValue);
            continue;
        }
        const externalValue = resolveExternalPayloadValue(key, action, externalResolvers);
        if (externalValue !== undefined) {
            payload[key] = externalValue;
            continue;
        }
        if (template[key] !== undefined) {
            payload[key] = template[key];
            continue;
        }
        const defaultValue = resolveDefaultPayloadValue(key);
        if (defaultValue !== undefined) {
            payload[key] = defaultValue;
        }
    }
    return Object.keys(payload).length ? payload : null;
}
function resolveExternalPayloadValue(key, action, externalResolvers) {
    const resolver = externalResolvers[key];
    if (resolver) {
        return resolver();
    }
    if (key === 'stepId') {
        return action.stepIds?.[0];
    }
    return undefined;
}
function normalizePayloadValue(key, value) {
    if (key === 'attachments') {
        if (value == null) {
            return [];
        }
        return Array.isArray(value) ? value : [value];
    }
    return value;
}
function resolveDefaultPayloadValue(key) {
    switch (key) {
        case 'attachments':
            return [];
        case 'delegatedUser':
        case 'note':
        case 'reason':
            return null;
        case 'values':
            return [];
        default:
            return undefined;
    }
}
function getMissingRequiredPayloadKeys(action, payload) {
    return (action.requiredPayloadKeys ?? []).filter((key) => isMissingPayloadValue(payload?.[key]));
}
function isMissingPayloadValue(value) {
    if (value == null) {
        return true;
    }
    if (typeof value === 'string') {
        return value.trim().length === 0;
    }
    if (Array.isArray(value)) {
        return value.length === 0;
    }
    return false;
}
function normalizeHttpMethod(value) {
    if (!value || typeof value !== 'string') {
        return null;
    }
    const method = value.trim().toUpperCase();
    return method.length ? method : null;
}
function methodSupportsBody(method) {
    return ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
}
function buildRequestOptions(method, payload) {
    if (!payload || !methodSupportsBody(method)) {
        return { observe: 'body' };
    }
    return {
        body: payload,
        observe: 'body',
    };
}
function hasActionFormFields(action) {
    return ((action.payloadKeys ?? []).some((key) => ['delegatedUser', 'reason', 'note', 'attachments'].includes(key)) || hasProgressTemplateField(action));
}
function hasProgressTemplateField(action) {
    const fields = action.payloadTemplate
        ?.fields;
    return (Array.isArray(fields) &&
        fields.some((field) => !!field &&
            typeof field === 'object' &&
            field.propertyKey === 'Progress'));
}
function resolveTemplateFieldsValue(action, template, formValue) {
    const templateFields = template['fields'];
    if (!Array.isArray(templateFields)) {
        return formValue?.['fields'] ?? templateFields;
    }
    if (!hasProgressTemplateField(action)) {
        return formValue?.['fields'] ?? templateFields;
    }
    return templateFields.map((field) => {
        if (!field ||
            typeof field !== 'object' ||
            field.propertyKey !== 'Progress') {
            return field;
        }
        return {
            ...field,
            value: formValue?.['progress'] ?? field.value,
        };
    });
}

class RuntimeActionContextStore {
    formSource = signal(null, ...(ngDevMode ? [{ debugName: "formSource" }] : /* istanbul ignore next */ []));
    setFormSource(source) {
        this.formSource.set(source);
    }
    resolveFormValues() {
        const values = this.formSource()?.getSubmitValues();
        return Array.isArray(values)
            ? values.map((value) => ({
                requestPropertyId: value.requestPropertyId,
                propertyKey: value.propertyKey,
                value: value.value,
            }))
            : [];
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RuntimeActionContextStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RuntimeActionContextStore });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RuntimeActionContextStore, decorators: [{
            type: Injectable
        }] });

const RUNTIME_ACTION_CONFIRM_DIALOG = new InjectionToken('RUNTIME_ACTION_CONFIRM_DIALOG');
class RuntimeActionRunner {
    http = inject(HttpClient);
    confirmation = inject(ConfirmationService);
    modal = inject(ModalService);
    toast = inject(ToastService);
    transloco = inject(TranslocoService);
    confirmDialogClass = inject(RUNTIME_ACTION_CONFIRM_DIALOG, {
        optional: true,
    });
    async execute(action, options = {}) {
        const method = normalizeHttpMethod(action.httpMethod);
        const url = action.url?.trim();
        if (!method || !url || action.isAvailable === false) {
            return cancelledOrError(action, 'error', new Error(this.notExecutableMessage(action)));
        }
        const performRun = (formValue) => {
            let payload = buildActionPayload(action, formValue, options.externalPayloadResolvers ?? {});
            if (options.transformPayload) {
                payload = options.transformPayload(payload, action);
            }
            const missing = getMissingRequiredPayloadKeys(action, payload);
            if (missing.length) {
                const message = this.transloco.translate('components.runtimeAction.missingArgs', { action: resolveActionLabel(action), missing: missing.join(', ') });
                if (!options.silent) {
                    this.toast.error(message);
                }
                return Promise.resolve({
                    status: 'error',
                    actionKey: action.actionKey,
                    afterSuccess: null,
                    error: new Error(message),
                });
            }
            return this.run(action, method, url, payload, options);
        };
        if (action.needConfirmation) {
            if (hasActionFormFields(action) && this.confirmDialogClass) {
                return this.runWithFormDialog(action, performRun);
            }
            const confirmed = await this.confirmPlain(action);
            if (!confirmed) {
                return {
                    status: 'cancelled',
                    actionKey: action.actionKey,
                    afterSuccess: null,
                };
            }
        }
        return performRun();
    }
    runWithFormDialog(action, performRun) {
        return new Promise((resolve) => {
            let pendingRun = null;
            let finalResult = null;
            const ref = this.modal.openModal(this.confirmDialogClass, 'dialog', {
                header: resolveActionLabel(action),
                width: '40rem',
                dismissableMask: true,
                inputValues: {
                    action,
                    onSubmit: async (value) => {
                        pendingRun = performRun(value);
                        const result = await pendingRun;
                        finalResult = result;
                        return result.status === 'success';
                    },
                },
            });
            ref.onClose.subscribe(async () => {
                if (finalResult) {
                    resolve(finalResult);
                    return;
                }
                if (pendingRun) {
                    try {
                        resolve(await pendingRun);
                    }
                    catch (error) {
                        resolve({
                            status: 'error',
                            actionKey: action.actionKey,
                            afterSuccess: null,
                            error,
                        });
                    }
                    return;
                }
                resolve({
                    status: 'cancelled',
                    actionKey: action.actionKey,
                    afterSuccess: null,
                });
            });
        });
    }
    confirmPlain(action) {
        return new Promise((resolve) => {
            const label = resolveActionLabel(action);
            this.confirmation.confirm({
                type: 'dialog',
                header: label,
                message: this.transloco.translate('components.runtimeAction.confirmDefault', { action: label }),
                acceptLabel: label,
                icon: 'alert.alert-triangle',
                accept: () => resolve(true),
                reject: () => resolve(false),
            });
        });
    }
    async run(action, method, url, payload, options) {
        const fallbackErrorMessage = this.transloco.translate('components.runtimeAction.executeFailed', { action: resolveActionLabel(action) });
        try {
            const response = await firstValueFrom(this.http
                .request(method, url, buildRequestOptions(method, payload))
                .pipe(map((res) => ensureSuccessfulEnvelope(res, fallbackErrorMessage))));
            if (options.successMessage && !options.silent) {
                this.toast.success(options.successMessage(response, action));
            }
            return {
                status: 'success',
                actionKey: action.actionKey,
                afterSuccess: resolveAfterSuccess(action, options.defaultAfterSuccess),
                response,
            };
        }
        catch (error) {
            const message = options.errorMessage?.(error, action) ??
                resolveHttpErrorMessage(error, fallbackErrorMessage);
            if (!options.silent) {
                this.toast.error(message);
            }
            return {
                status: 'error',
                actionKey: action.actionKey,
                afterSuccess: null,
                error,
            };
        }
    }
    notExecutableMessage(action) {
        if (action.isAvailable === false) {
            return this.transloco.translate('components.runtimeAction.notAvailable', {
                action: action.actionKey,
            });
        }
        if (!action.url) {
            return this.transloco.translate('components.runtimeAction.noUrl', {
                action: action.actionKey,
            });
        }
        return this.transloco.translate('components.runtimeAction.noHttpMethod', {
            action: action.actionKey,
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RuntimeActionRunner, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RuntimeActionRunner, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: RuntimeActionRunner, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }] });
function cancelledOrError(action, status, error) {
    return {
        status,
        actionKey: action.actionKey,
        afterSuccess: null,
        error,
    };
}
function resolveAfterSuccess(action, fallback) {
    if (action.afterSuccess === 'close' || action.afterSuccess === 'refresh') {
        return action.afterSuccess;
    }
    return fallback ?? 'refresh';
}
function ensureSuccessfulEnvelope(response, fallback) {
    if (!response || typeof response !== 'object') {
        return response;
    }
    const envelope = response;
    if (typeof envelope.code === 'number' && envelope.code !== 1) {
        throw new Error(resolveEnvelopeErrorMessage(response, fallback));
    }
    return response;
}
function resolveEnvelopeErrorMessage(response, fallback) {
    if (!response || typeof response !== 'object') {
        return fallback;
    }
    const envelope = response;
    return envelope.errors?.message ?? envelope.message ?? fallback;
}
function resolveHttpErrorMessage(error, fallback) {
    if (!error || typeof error !== 'object') {
        return fallback;
    }
    const httpError = error;
    return httpError.error?.message ?? httpError.message ?? fallback;
}

/**
 * Generated bundle index. Do not edit.
 */

export { RUNTIME_ACTION_CONFIRM_DIALOG, RuntimeActionContextStore, RuntimeActionRunner, buildActionPayload, buildRequestOptions, getMissingRequiredPayloadKeys, hasActionFormFields, hasProgressTemplateField, isMissingPayloadValue, methodSupportsBody, normalizeHttpMethod, normalizePayloadValue, resolveActionLabel, resolveDefaultPayloadValue };
//# sourceMappingURL=masterteam-components-runtime-action.mjs.map
