import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { input, inject, DestroyRef, signal, computed, ChangeDetectionStrategy, Component, effect, untracked, viewChild, linkedSignal } from '@angular/core';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import * as i1$1 from '@angular/forms';
import { ControlContainer, Validators, NgControl, FormsModule } from '@angular/forms';
import { startWith, finalize, catchError, of } from 'rxjs';
import { Tooltip } from '@masterteam/components/tooltip';
import { Icon } from '@masterteam/icons';
import { SelectField } from '@masterteam/components/select-field';
import { MultiSelectField } from '@masterteam/components/multi-select-field';
import { FieldValidation } from '@masterteam/components/field-validation';
import { HttpClient } from '@angular/common/http';
import * as i2 from '@jsverse/transloco';
import { TranslocoService, TranslocoModule } from '@jsverse/transloco';
import { Button } from '@masterteam/components/button';
import { NumberField } from '@masterteam/components/number-field';
import { Table } from '@masterteam/components/table';

class LookupMatrixField {
    config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
    controlContainer = inject(ControlContainer, {
        optional: true,
    });
    destroyRef = inject(DestroyRef);
    valueVersion = signal(0, ...(ngDevMode ? [{ debugName: "valueVersion" }] : []));
    xOptions = computed(() => this.config().xOptions ?? [], ...(ngDevMode ? [{ debugName: "xOptions" }] : []));
    yOptions = computed(() => this.config().yOptions ?? [], ...(ngDevMode ? [{ debugName: "yOptions" }] : []));
    hasMatrix = computed(() => this.config().cells.length > 0, ...(ngDevMode ? [{ debugName: "hasMatrix" }] : []));
    cellMap = computed(() => new Map(this.config().cells.map((cell) => [
        this.cellKey(cell.xLookupItemId, cell.yLookupItemId),
        cell,
    ])), ...(ngDevMode ? [{ debugName: "cellMap" }] : []));
    selectedXOption = computed(() => {
        this.valueVersion();
        return this.resolveSelectedOption(this.xOptions(), this.getControlValue(this.config().xFieldKey));
    }, ...(ngDevMode ? [{ debugName: "selectedXOption" }] : []));
    selectedYOption = computed(() => {
        this.valueVersion();
        return this.resolveSelectedOption(this.yOptions(), this.getControlValue(this.config().yFieldKey));
    }, ...(ngDevMode ? [{ debugName: "selectedYOption" }] : []));
    activeCell = computed(() => {
        const selectedX = this.selectedXOption();
        const selectedY = this.selectedYOption();
        if (!selectedX || !selectedY) {
            return null;
        }
        return this.getCell(selectedX.id ?? selectedX.value, selectedY.id ?? selectedY.value);
    }, ...(ngDevMode ? [{ debugName: "activeCell" }] : []));
    selectionSummary = computed(() => {
        const parts = [];
        const xAxisLabel = this.config().xAxisLabel.trim() || 'X';
        const yAxisLabel = this.config().yAxisLabel.trim() || 'Y';
        const selectedX = this.selectedXOption();
        const selectedY = this.selectedYOption();
        if (selectedX) {
            parts.push(`${xAxisLabel}: ${selectedX.label}`);
        }
        if (selectedY) {
            parts.push(`${yAxisLabel}: ${selectedY.label}`);
        }
        return parts.join(' / ');
    }, ...(ngDevMode ? [{ debugName: "selectionSummary" }] : []));
    pendingMessage = computed(() => {
        const xAxisLabel = this.config().xAxisLabel.trim() || 'the first value';
        const yAxisLabel = this.config().yAxisLabel.trim() || 'the second value';
        return `Select ${xAxisLabel} and ${yAxisLabel} first.`;
    }, ...(ngDevMode ? [{ debugName: "pendingMessage" }] : []));
    missingResultMessage = computed(() => {
        if (!this.selectedXOption() || !this.selectedYOption()) {
            return this.pendingMessage();
        }
        return 'No matching value for the selected combination.';
    }, ...(ngDevMode ? [{ debugName: "missingResultMessage" }] : []));
    constructor() {
        const control = this.controlContainer?.control;
        if (!control)
            return;
        control.valueChanges
            .pipe(startWith(control.getRawValue()), takeUntilDestroyed(this.destroyRef))
            .subscribe(() => {
            this.valueVersion.update((value) => value + 1);
        });
    }
    getCell(xLookupItemId, yLookupItemId) {
        return (this.cellMap().get(this.cellKey(xLookupItemId, yLookupItemId)) ?? null);
    }
    getSwatchStyle(color) {
        const resolvedColor = this.resolveColor(color);
        if (!resolvedColor)
            return {};
        return {
            backgroundColor: resolvedColor,
            borderColor: resolvedColor,
        };
    }
    getControlValue(path) {
        if (!path)
            return null;
        return this.controlContainer?.control?.get(path)?.value ?? null;
    }
    resolveSelectedOption(options, rawValue) {
        if (rawValue == null || rawValue === '')
            return null;
        return (options.find((option) => this.optionMatchesValue(option, rawValue)) ??
            null);
    }
    optionMatchesValue(option, rawValue) {
        return [option.value, option.key, option.id].some((candidate) => candidate != null && String(candidate) === String(rawValue));
    }
    cellKey(xLookupItemId, yLookupItemId) {
        return `${String(xLookupItemId)}::${String(yLookupItemId)}`;
    }
    resolveColor(color) {
        if (!color)
            return null;
        if (color.startsWith('#') ||
            color.startsWith('rgb') ||
            color.startsWith('hsl') ||
            color.startsWith('var(')) {
            return color;
        }
        return `var(--p-${color}-500)`;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: LookupMatrixField, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: LookupMatrixField, isStandalone: true, selector: "mt-lookup-matrix-field", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "grid gap-2" }, ngImport: i0, template: "@if (config().label) {\r\n  <label class=\"text-sm font-medium text-color\">\r\n    {{ config().label }}\r\n  </label>\r\n}\r\n\r\n<div\r\n  class=\"rounded-lg border border-surface-200 bg-surface-50 px-3 flex items-center\"\r\n>\r\n  @if (activeCell(); as cell) {\r\n    <div class=\"flex flex-1 min-h-6 items-center gap-2 justify-between\">\r\n      <span\r\n        class=\"h-6 w-2 shrink-0 rounded-[2px] border border-surface-200\"\r\n        [ngStyle]=\"getSwatchStyle(cell.color)\"\r\n      ></span>\r\n\r\n      <div class=\"min-w-0 flex-1 py-2 flex items-center justify-between\">\r\n        <div class=\"truncate font-semibold text-gray-500\">\r\n          {{ cell.label }}\r\n        </div>\r\n\r\n        @if (selectionSummary()) {\r\n          <div class=\"font-bold text-gray-500\">\r\n            <mt-icon\r\n              [mtTooltip]=\"selectionSummary()\"\r\n              icon=\"general.info-circle\"\r\n            />\r\n          </div>\r\n        }\r\n      </div>\r\n    </div>\r\n  } @else {\r\n    <div class=\"flex min-h-6 items-center text-muted-color py-2\">\r\n      {{ hasMatrix() ? missingResultMessage() : config().emptyMessage }}\r\n    </div>\r\n  }\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: LookupMatrixField, decorators: [{
            type: Component,
            args: [{ selector: 'mt-lookup-matrix-field', standalone: true, imports: [CommonModule, Tooltip, Icon], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        class: 'grid gap-2',
                    }, template: "@if (config().label) {\r\n  <label class=\"text-sm font-medium text-color\">\r\n    {{ config().label }}\r\n  </label>\r\n}\r\n\r\n<div\r\n  class=\"rounded-lg border border-surface-200 bg-surface-50 px-3 flex items-center\"\r\n>\r\n  @if (activeCell(); as cell) {\r\n    <div class=\"flex flex-1 min-h-6 items-center gap-2 justify-between\">\r\n      <span\r\n        class=\"h-6 w-2 shrink-0 rounded-[2px] border border-surface-200\"\r\n        [ngStyle]=\"getSwatchStyle(cell.color)\"\r\n      ></span>\r\n\r\n      <div class=\"min-w-0 flex-1 py-2 flex items-center justify-between\">\r\n        <div class=\"truncate font-semibold text-gray-500\">\r\n          {{ cell.label }}\r\n        </div>\r\n\r\n        @if (selectionSummary()) {\r\n          <div class=\"font-bold text-gray-500\">\r\n            <mt-icon\r\n              [mtTooltip]=\"selectionSummary()\"\r\n              icon=\"general.info-circle\"\r\n            />\r\n          </div>\r\n        }\r\n      </div>\r\n    </div>\r\n  } @else {\r\n    <div class=\"flex min-h-6 items-center text-muted-color py-2\">\r\n      {{ hasMatrix() ? missingResultMessage() : config().emptyMessage }}\r\n    </div>\r\n  }\r\n</div>\r\n" }]
        }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }] } });

class SchemaConnectionField {
    // ============================================================================
    // Inputs
    // ============================================================================
    label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
    placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
    readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
    required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
    filter = input(true, ...(ngDevMode ? [{ debugName: "filter" }] : []));
    /**
     * Full connection configuration object from the API.
     * Includes `targetLevelId`, `sourceLevels[]`, `payloadTemplate`, etc.
     */
    configuration = input.required(...(ngDevMode ? [{ debugName: "configuration" }] : []));
    /**
     * Optional HttpContext for API requests (e.g. to set base URL behavior).
     */
    context = input(undefined, ...(ngDevMode ? [{ debugName: "context" }] : []));
    // ============================================================================
    // Internal State
    // ============================================================================
    /** The full ConnectionPayload value */
    value = signal(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
    disabled = signal(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
    /** Per-source-level state: options + loading */
    sourceLevelStates = signal([], ...(ngDevMode ? [{ debugName: "sourceLevelStates" }] : []));
    /** Mutable selections per source level: sourceLevelId -> selected value */
    selections = {};
    requiredValidator = Validators.required;
    onTouched = () => { };
    onModelChange = () => { };
    ngControl = null;
    http = inject(HttpClient);
    destroyRef = inject(DestroyRef);
    /**
     * Source levels from configuration.
     */
    sourceLevels = computed(() => {
        return this.configuration()?.sourceLevels ?? [];
    }, ...(ngDevMode ? [{ debugName: "sourceLevels" }] : []));
    /**
     * Percentage width for each dropdown, evenly divided.
     */
    dropdownWidth = computed(() => {
        const count = this.sourceLevels().length;
        if (count <= 0)
            return '100%';
        return `${100 / count}%`;
    }, ...(ngDevMode ? [{ debugName: "dropdownWidth" }] : []));
    constructor() {
        this.ngControl = inject(NgControl, { self: true, optional: true });
        if (this.ngControl) {
            this.ngControl.valueAccessor = this;
        }
        effect(() => {
            if (this.ngControl?.control && this.required()) {
                this.ngControl.control.addValidators(Validators.required);
                this.ngControl.control.updateValueAndValidity();
            }
        });
        // Load options for each source level when configuration changes
        effect(() => {
            const levels = this.sourceLevels();
            if (!levels.length)
                return;
            // Everything below must NOT create signal dependencies,
            // only sourceLevels() (→ configuration) should trigger this effect.
            untracked(() => {
                // Initialize per-source states + selections
                this.sourceLevelStates.set(levels.map((source) => ({
                    source,
                    options: [],
                    loading: false,
                })));
                for (const source of levels) {
                    if (!(source.levelId in this.selections)) {
                        this.selections[source.levelId] = source.allowManyToMany
                            ? []
                            : null;
                    }
                }
                // Distribute existing value to selections
                this.distributeToSelections(this.value());
                // Load options for each source level
                for (const source of levels) {
                    this.loadOptions(source);
                }
            });
        });
    }
    // ============================================================================
    // Option Loading (per source level)
    // ============================================================================
    loadOptions(source) {
        this.updateSourceState(source.levelId, { loading: true });
        const ctx = this.context();
        const payload = {
            contextKey: `level:${source.levelId}`,
            projection: 'Flat',
            propertyKeys: ['name'],
        };
        this.http
            .post('fetch/query', payload, {
            ...(ctx ? { context: ctx } : {}),
        })
            .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.updateSourceState(source.levelId, { loading: false })), catchError((err) => {
            console.error(`Connection options load failed for ${source.levelKey}:`, err);
            return of({ data: { records: [] } });
        }))
            .subscribe((response) => {
            const options = (response?.data?.records ?? [])
                .filter((record) => record?.id != null)
                .map((record) => ({
                ...record,
                id: record.id,
                name: record.name,
            }));
            this.updateSourceState(source.levelId, {
                options,
                loading: false,
            });
        });
    }
    updateSourceState(levelId, patch) {
        this.sourceLevelStates.update((states) => states.map((s) => s.source.levelId === levelId ? { ...s, ...patch } : s));
    }
    // ============================================================================
    // Value Handling
    // ============================================================================
    /**
     * Single handler — no params. Reads all selections, builds the combined payload.
     */
    syncValue() {
        const cfg = this.configuration();
        const states = this.sourceLevelStates();
        const sources = [];
        for (const state of states) {
            const val = this.selections[state.source.levelId];
            if (val == null)
                continue;
            if (state.source.allowManyToMany && Array.isArray(val)) {
                for (const id of val) {
                    sources.push({
                        sourceLevelId: state.source.levelId,
                        sourceLevelDataId: id,
                    });
                }
            }
            else if (typeof val === 'number') {
                sources.push({
                    sourceLevelId: state.source.levelId,
                    sourceLevelDataId: val,
                });
            }
        }
        const payload = sources.length > 0 ? { levelId: cfg.targetLevelId, sources } : null;
        this.value.set(payload);
        this.onModelChange(payload);
        this.onTouched();
    }
    distributeToSelections(payload) {
        const states = this.sourceLevelStates();
        for (const state of states) {
            const ids = (payload?.sources ?? [])
                .filter((s) => s.sourceLevelId === state.source.levelId)
                .map((s) => s.sourceLevelDataId);
            this.selections[state.source.levelId] = state.source.allowManyToMany
                ? ids
                : (ids[0] ?? null);
        }
    }
    // ============================================================================
    // ControlValueAccessor
    // ============================================================================
    writeValue(value) {
        if (typeof value === 'string') {
            try {
                value = JSON.parse(value);
            }
            catch {
                value = null;
            }
        }
        this.value.set(value ?? null);
        this.distributeToSelections(value ?? null);
    }
    registerOnChange(fn) {
        this.onModelChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(disabled) {
        this.disabled.set(disabled);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: SchemaConnectionField, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: SchemaConnectionField, isStandalone: true, selector: "mt-schema-connection-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, configuration: { classPropertyName: "configuration", publicName: "configuration", isSignal: true, isRequired: true, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "grid gap-1" }, ngImport: i0, template: "<!-- @if (label()) {\r\n  <label\r\n    [class.required]=\"ngControl?.control?.hasValidator(requiredValidator)\"\r\n    [for]=\"ngControl?.name || label()\"\r\n  >\r\n    {{ label() }}\r\n  </label>\r\n} -->\r\n\r\n<div class=\"flex gap-4 w-full\">\r\n  @for (state of sourceLevelStates(); track state.source.levelId) {\r\n    <div [style.width]=\"dropdownWidth()\" class=\"min-w-0\">\r\n      @if (sourceLevelStates().length > 1) {\r\n        <span class=\"text-sm text-muted-color mb-1 block truncate\">\r\n          {{ state.source.name?.en || state.source.levelKey }}\r\n        </span>\r\n      }\r\n\r\n      @if (state.source.allowManyToMany) {\r\n        <mt-multi-select-field\r\n          [field]=\"false\"\r\n          [(ngModel)]=\"selections[state.source.levelId]\"\r\n          (ngModelChange)=\"syncValue()\"\r\n          [options]=\"state.options\"\r\n          optionLabel=\"name\"\r\n          optionValue=\"id\"\r\n          [placeholder]=\"placeholder() || state.source.name?.en || 'Select...'\"\r\n          [readonly]=\"disabled() || readonly()\"\r\n          [filter]=\"filter()\"\r\n          [showClear]=\"true\"\r\n          display=\"chip\"\r\n        />\r\n      } @else {\r\n        <mt-select-field\r\n          [field]=\"false\"\r\n          [(ngModel)]=\"selections[state.source.levelId]\"\r\n          (ngModelChange)=\"syncValue()\"\r\n          [options]=\"state.options\"\r\n          optionLabel=\"name\"\r\n          optionValue=\"id\"\r\n          [placeholder]=\"placeholder() || state.source.name?.en || 'Select...'\"\r\n          [readonly]=\"disabled() || readonly()\"\r\n          [filter]=\"filter()\"\r\n          [loading]=\"state.loading\"\r\n          [showClear]=\"true\"\r\n        />\r\n      }\r\n    </div>\r\n  }\r\n</div>\r\n\r\n<mt-field-validation [control]=\"ngControl?.control\"></mt-field-validation>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: MultiSelectField, selector: "mt-multi-select-field", inputs: ["field", "label", "placeholder", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "display", "required", "maxSelectedLabels", "group", "optionGroupLabel", "optionGroupChildren", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: FieldValidation, selector: "mt-field-validation", inputs: ["control", "touched"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: SchemaConnectionField, decorators: [{
            type: Component,
            args: [{ selector: 'mt-schema-connection-field', standalone: true, imports: [FormsModule, SelectField, MultiSelectField, FieldValidation], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        class: 'grid gap-1',
                    }, template: "<!-- @if (label()) {\r\n  <label\r\n    [class.required]=\"ngControl?.control?.hasValidator(requiredValidator)\"\r\n    [for]=\"ngControl?.name || label()\"\r\n  >\r\n    {{ label() }}\r\n  </label>\r\n} -->\r\n\r\n<div class=\"flex gap-4 w-full\">\r\n  @for (state of sourceLevelStates(); track state.source.levelId) {\r\n    <div [style.width]=\"dropdownWidth()\" class=\"min-w-0\">\r\n      @if (sourceLevelStates().length > 1) {\r\n        <span class=\"text-sm text-muted-color mb-1 block truncate\">\r\n          {{ state.source.name?.en || state.source.levelKey }}\r\n        </span>\r\n      }\r\n\r\n      @if (state.source.allowManyToMany) {\r\n        <mt-multi-select-field\r\n          [field]=\"false\"\r\n          [(ngModel)]=\"selections[state.source.levelId]\"\r\n          (ngModelChange)=\"syncValue()\"\r\n          [options]=\"state.options\"\r\n          optionLabel=\"name\"\r\n          optionValue=\"id\"\r\n          [placeholder]=\"placeholder() || state.source.name?.en || 'Select...'\"\r\n          [readonly]=\"disabled() || readonly()\"\r\n          [filter]=\"filter()\"\r\n          [showClear]=\"true\"\r\n          display=\"chip\"\r\n        />\r\n      } @else {\r\n        <mt-select-field\r\n          [field]=\"false\"\r\n          [(ngModel)]=\"selections[state.source.levelId]\"\r\n          (ngModelChange)=\"syncValue()\"\r\n          [options]=\"state.options\"\r\n          optionLabel=\"name\"\r\n          optionValue=\"id\"\r\n          [placeholder]=\"placeholder() || state.source.name?.en || 'Select...'\"\r\n          [readonly]=\"disabled() || readonly()\"\r\n          [filter]=\"filter()\"\r\n          [loading]=\"state.loading\"\r\n          [showClear]=\"true\"\r\n        />\r\n      }\r\n    </div>\r\n  }\r\n</div>\r\n\r\n<mt-field-validation [control]=\"ngControl?.control\"></mt-field-validation>\r\n" }]
        }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], configuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "configuration", required: true }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }] } });

const PREDECESSOR_TYPE_BY_TOKEN = {
    FS: 0,
    SS: 1,
    FF: 2,
    SF: 3,
};
const PREDECESSOR_TOKEN_BY_TYPE = {
    0: 'FS',
    1: 'SS',
    2: 'FF',
    3: 'SF',
};
class SchedulePredecessorField {
    predecessorCellTpl = viewChild.required('predecessorCellTpl');
    typeCellTpl = viewChild.required('typeCellTpl');
    lagCellTpl = viewChild.required('lagCellTpl');
    label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
    placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
    readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
    required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
    configuration = input(null, ...(ngDevMode ? [{ debugName: "configuration" }] : []));
    runtimeContext = input(null, ...(ngDevMode ? [{ debugName: "runtimeContext" }] : []));
    loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
    taskOptions = signal([], ...(ngDevMode ? [{ debugName: "taskOptions" }] : []));
    rows = signal([], ...(ngDevMode ? [{ debugName: "rows" }] : []));
    disabled = signal(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
    typeOptions = computed(() => [
        { label: 'FS', value: 0 },
        { label: 'SS', value: 1 },
        { label: 'FF', value: 2 },
        { label: 'SF', value: 3 },
    ], ...(ngDevMode ? [{ debugName: "typeOptions" }] : []));
    availableTaskOptions = computed(() => {
        const currentTaskId = this.runtimeContext()?.moduleDataId;
        return this.taskOptions().filter((item) => String(item.value) !== String(currentTaskId ?? ''));
    }, ...(ngDevMode ? [{ debugName: "availableTaskOptions" }] : []));
    canAddRow = computed(() => !!this.runtimeContext()?.levelId &&
        !!this.runtimeContext()?.levelDataId &&
        this.availableTaskOptions().length > 0, ...(ngDevMode ? [{ debugName: "canAddRow" }] : []));
    addTooltip = computed(() => this.canAddRow() ? undefined : this.texts().noAvailable, ...(ngDevMode ? [{ debugName: "addTooltip" }] : []));
    pageSize = computed(() => this.rows().length > 0 ? this.rows().length : 1, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
    columns = linkedSignal(() => [
        {
            key: 'predecessorId',
            label: this.texts().predecessor,
            type: 'custom',
            customCellTpl: this.predecessorCellTpl(),
        },
        {
            key: 'type',
            label: this.texts().type,
            type: 'custom',
            customCellTpl: this.typeCellTpl(),
        },
        {
            key: 'lagDays',
            label: this.texts().lagDays,
            type: 'custom',
            customCellTpl: this.lagCellTpl(),
        },
    ], ...(ngDevMode ? [{ debugName: "columns" }] : []));
    rowActions = computed(() => [
        {
            icon: 'general.trash-01',
            color: 'danger',
            variant: 'text',
            tooltip: this.texts().delete,
            hidden: () => this.disabled() || this.readonly(),
            action: (row) => this.removeRow(row.key),
        },
    ], ...(ngDevMode ? [{ debugName: "rowActions" }] : []));
    requiredValidator = Validators.required;
    onTouched = () => { };
    onModelChange = () => { };
    ngControl = null;
    http = inject(HttpClient);
    transloco = inject(TranslocoService);
    destroyRef = inject(DestroyRef);
    activeLang = toSignal(this.transloco.langChanges$, {
        initialValue: this.transloco.getActiveLang(),
    });
    rowKey = 0;
    texts = computed(() => {
        this.activeLang();
        return {
            noAvailable: this.transloco.translate('components.schedulePredecessorField.noAvailable'),
            predecessor: this.transloco.translate('components.schedulePredecessorField.predecessor'),
            type: this.transloco.translate('components.schedulePredecessorField.type'),
            lagDays: this.transloco.translate('components.schedulePredecessorField.lagDays'),
            delete: this.transloco.translate('components.schedulePredecessorField.delete'),
        };
    }, ...(ngDevMode ? [{ debugName: "texts" }] : []));
    constructor() {
        this.ngControl = inject(NgControl, { self: true, optional: true });
        if (this.ngControl) {
            this.ngControl.valueAccessor = this;
        }
        effect(() => {
            if (this.ngControl?.control && this.required()) {
                this.ngControl.control.addValidators(Validators.required);
                this.ngControl.control.updateValueAndValidity();
            }
        });
        effect(() => {
            const context = this.runtimeContext();
            const levelId = context?.levelId;
            const levelDataId = context?.levelDataId;
            if (levelId == null || levelDataId == null) {
                this.taskOptions.set([]);
                return;
            }
            untracked(() => this.loadTaskOptions(levelId, levelDataId));
        });
    }
    addRow() {
        if (this.disabled() || this.readonly() || !this.canAddRow()) {
            return;
        }
        this.syncRows([
            ...this.rows(),
            {
                key: this.nextRowKey(),
                predecessorId: '',
                type: 0,
                lagDays: 0,
            },
        ]);
    }
    removeRow(key) {
        if (this.disabled() || this.readonly()) {
            return;
        }
        this.syncRows(this.rows().filter((row) => row.key !== key));
    }
    updateRow(key, patch) {
        if (this.disabled() || this.readonly()) {
            return;
        }
        this.syncRows(this.rows().map((row) => (row.key === key ? { ...row, ...patch } : row)));
    }
    writeValue(value) {
        const rows = typeof value === 'string'
            ? value
                .split(',')
                .map((item) => item.trim())
                .filter(Boolean)
                .map((item) => this.parseCompactValue(item))
                .filter((item) => item != null)
                .map((item) => ({
                key: this.nextRowKey(),
                ...item,
            }))
            : [];
        this.rows.set(rows);
    }
    registerOnChange(fn) {
        this.onModelChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(disabled) {
        this.disabled.set(disabled);
    }
    syncRows(rows) {
        this.rows.set(rows);
        const value = rows
            .filter((row) => row.predecessorId !== null &&
            row.predecessorId !== undefined &&
            String(row.predecessorId).trim() !== '')
            .map((row) => this.formatCompactValue(row))
            .join(',');
        this.onModelChange(value || null);
        this.onTouched();
    }
    loadTaskOptions(levelId, levelDataId) {
        this.loading.set(true);
        this.http
            .post(`levels/${levelId}/${levelDataId}/schedule/query`, {
            projection: 'Tree',
            propertyKeys: ['name'],
        })
            .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.loading.set(false)), catchError(() => of({ data: { records: [] } })))
            .subscribe((response) => {
            this.taskOptions.set(this.mapTaskOptions(response.data));
        });
    }
    mapTaskOptions(data) {
        return this.flattenRecords(this.resolveRootRecords(data?.records ?? [], data?.projectionMeta?.rootIds ?? [])).map((record) => ({
            label: `${record.id} - ${record.name ?? record.id}`,
            value: String(record.id),
        }));
    }
    resolveRootRecords(records, rootIds) {
        if (!rootIds?.length) {
            return records;
        }
        const recordsById = new Map(records.map((record) => [String(record.id), record]));
        return rootIds
            .map((id) => recordsById.get(String(id)))
            .filter((record) => record != null);
    }
    flattenRecords(records) {
        const output = [];
        const walk = (items) => {
            items.forEach((item) => {
                output.push(item);
                walk(item.children ?? []);
            });
        };
        walk(records);
        return output;
    }
    nextRowKey() {
        this.rowKey += 1;
        return `predecessor-${this.rowKey}`;
    }
    parseCompactValue(value) {
        const match = value.match(/^(.+?)(FS|SS|FF|SF)([+-]\d+)?$/);
        if (!match) {
            return null;
        }
        return {
            predecessorId: match[1],
            type: PREDECESSOR_TYPE_BY_TOKEN[match[2]],
            lagDays: match[3] ? Number(match[3]) : 0,
        };
    }
    formatCompactValue(row) {
        const lag = row.lagDays > 0 ? `+${row.lagDays}` : row.lagDays < 0 ? row.lagDays : '';
        return `${row.predecessorId}${PREDECESSOR_TOKEN_BY_TYPE[row.type]}${lag}`;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: SchedulePredecessorField, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: SchedulePredecessorField, isStandalone: true, selector: "mt-schedule-predecessor-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, configuration: { classPropertyName: "configuration", publicName: "configuration", isSignal: true, isRequired: false, transformFunction: null }, runtimeContext: { classPropertyName: "runtimeContext", publicName: "runtimeContext", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "grid gap-1" }, viewQueries: [{ propertyName: "predecessorCellTpl", first: true, predicate: ["predecessorCellTpl"], descendants: true, isSignal: true }, { propertyName: "typeCellTpl", first: true, predicate: ["typeCellTpl"], descendants: true, isSignal: true }, { propertyName: "lagCellTpl", first: true, predicate: ["lagCellTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (label()) {\r\n  <label\r\n    [class.required]=\"ngControl?.control?.hasValidator(requiredValidator)\"\r\n    [for]=\"ngControl?.name || label()\"\r\n  >\r\n    {{ label() }}\r\n  </label>\r\n}\r\n\r\n<div class=\"grid gap-3\">\r\n  <div class=\"flex items-center justify-end\">\r\n    <mt-button\r\n      icon=\"general.plus\"\r\n      [label]=\"'components.schedulePredecessorField.add' | transloco\"\r\n      severity=\"secondary\"\r\n      variant=\"outlined\"\r\n      [disabled]=\"disabled() || readonly() || !canAddRow()\"\r\n      [tooltip]=\"addTooltip()\"\r\n      (onClick)=\"addRow()\"\r\n    />\r\n  </div>\r\n\r\n  <ng-template #predecessorCellTpl let-row>\r\n    <mt-select-field\r\n      [field]=\"false\"\r\n      [options]=\"availableTaskOptions()\"\r\n      optionLabel=\"label\"\r\n      optionValue=\"value\"\r\n      [placeholder]=\"\r\n        placeholder() ||\r\n        ('components.schedulePredecessorField.selectPredecessor' | transloco)\r\n      \"\r\n      [filter]=\"true\"\r\n      [showClear]=\"true\"\r\n      [loading]=\"loading()\"\r\n      [readonly]=\"disabled() || readonly()\"\r\n      [ngModel]=\"row.predecessorId || undefined\"\r\n      (onChange)=\"updateRow(row.key, { predecessorId: $event ?? '' })\"\r\n    />\r\n  </ng-template>\r\n\r\n  <ng-template #typeCellTpl let-row>\r\n    <mt-select-field\r\n      [field]=\"false\"\r\n      [options]=\"typeOptions()\"\r\n      optionLabel=\"label\"\r\n      optionValue=\"value\"\r\n      [readonly]=\"disabled() || readonly()\"\r\n      [ngModel]=\"row.type\"\r\n      (onChange)=\"updateRow(row.key, { type: $event ?? 0 })\"\r\n    />\r\n  </ng-template>\r\n\r\n  <ng-template #lagCellTpl let-row>\r\n    <mt-number-field\r\n      [field]=\"false\"\r\n      [useGrouping]=\"false\"\r\n      [placeholder]=\"'components.schedulePredecessorField.lagDays' | transloco\"\r\n      [readonly]=\"disabled() || readonly()\"\r\n      [ngModel]=\"row.lagDays\"\r\n      (ngModelChange)=\"updateRow(row.key, { lagDays: $event ?? 0 })\"\r\n    />\r\n  </ng-template>\r\n\r\n  <mt-table\r\n    [data]=\"rows()\"\r\n    [columns]=\"columns()\"\r\n    [rowActions]=\"rowActions()\"\r\n    [pageSize]=\"pageSize()\"\r\n    [generalSearch]=\"false\"\r\n    [showFilters]=\"false\"\r\n    [dataKey]=\"'key'\"\r\n    [loading]=\"loading()\"\r\n  >\r\n    <ng-template #empty>\r\n      <div\r\n        class=\"rounded-xl border border-dashed border-surface px-4 py-5 text-sm text-muted-color\"\r\n      >\r\n        {{ \"components.schedulePredecessorField.empty\" | transloco }}\r\n      </div>\r\n    </ng-template>\r\n  </mt-table>\r\n</div>\r\n\r\n<mt-field-validation [control]=\"ngControl?.control\"></mt-field-validation>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: FieldValidation, selector: "mt-field-validation", inputs: ["control", "touched"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "exportable", "exportFilename", "actionShape", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: SchedulePredecessorField, decorators: [{
            type: Component,
            args: [{ selector: 'mt-schedule-predecessor-field', standalone: true, imports: [
                        CommonModule,
                        FormsModule,
                        Button,
                        FieldValidation,
                        NumberField,
                        SelectField,
                        Table,
                        TranslocoModule,
                    ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        class: 'grid gap-1',
                    }, template: "@if (label()) {\r\n  <label\r\n    [class.required]=\"ngControl?.control?.hasValidator(requiredValidator)\"\r\n    [for]=\"ngControl?.name || label()\"\r\n  >\r\n    {{ label() }}\r\n  </label>\r\n}\r\n\r\n<div class=\"grid gap-3\">\r\n  <div class=\"flex items-center justify-end\">\r\n    <mt-button\r\n      icon=\"general.plus\"\r\n      [label]=\"'components.schedulePredecessorField.add' | transloco\"\r\n      severity=\"secondary\"\r\n      variant=\"outlined\"\r\n      [disabled]=\"disabled() || readonly() || !canAddRow()\"\r\n      [tooltip]=\"addTooltip()\"\r\n      (onClick)=\"addRow()\"\r\n    />\r\n  </div>\r\n\r\n  <ng-template #predecessorCellTpl let-row>\r\n    <mt-select-field\r\n      [field]=\"false\"\r\n      [options]=\"availableTaskOptions()\"\r\n      optionLabel=\"label\"\r\n      optionValue=\"value\"\r\n      [placeholder]=\"\r\n        placeholder() ||\r\n        ('components.schedulePredecessorField.selectPredecessor' | transloco)\r\n      \"\r\n      [filter]=\"true\"\r\n      [showClear]=\"true\"\r\n      [loading]=\"loading()\"\r\n      [readonly]=\"disabled() || readonly()\"\r\n      [ngModel]=\"row.predecessorId || undefined\"\r\n      (onChange)=\"updateRow(row.key, { predecessorId: $event ?? '' })\"\r\n    />\r\n  </ng-template>\r\n\r\n  <ng-template #typeCellTpl let-row>\r\n    <mt-select-field\r\n      [field]=\"false\"\r\n      [options]=\"typeOptions()\"\r\n      optionLabel=\"label\"\r\n      optionValue=\"value\"\r\n      [readonly]=\"disabled() || readonly()\"\r\n      [ngModel]=\"row.type\"\r\n      (onChange)=\"updateRow(row.key, { type: $event ?? 0 })\"\r\n    />\r\n  </ng-template>\r\n\r\n  <ng-template #lagCellTpl let-row>\r\n    <mt-number-field\r\n      [field]=\"false\"\r\n      [useGrouping]=\"false\"\r\n      [placeholder]=\"'components.schedulePredecessorField.lagDays' | transloco\"\r\n      [readonly]=\"disabled() || readonly()\"\r\n      [ngModel]=\"row.lagDays\"\r\n      (ngModelChange)=\"updateRow(row.key, { lagDays: $event ?? 0 })\"\r\n    />\r\n  </ng-template>\r\n\r\n  <mt-table\r\n    [data]=\"rows()\"\r\n    [columns]=\"columns()\"\r\n    [rowActions]=\"rowActions()\"\r\n    [pageSize]=\"pageSize()\"\r\n    [generalSearch]=\"false\"\r\n    [showFilters]=\"false\"\r\n    [dataKey]=\"'key'\"\r\n    [loading]=\"loading()\"\r\n  >\r\n    <ng-template #empty>\r\n      <div\r\n        class=\"rounded-xl border border-dashed border-surface px-4 py-5 text-sm text-muted-color\"\r\n      >\r\n        {{ \"components.schedulePredecessorField.empty\" | transloco }}\r\n      </div>\r\n    </ng-template>\r\n  </mt-table>\r\n</div>\r\n\r\n<mt-field-validation [control]=\"ngControl?.control\"></mt-field-validation>\r\n" }]
        }], ctorParameters: () => [], propDecorators: { predecessorCellTpl: [{ type: i0.ViewChild, args: ['predecessorCellTpl', { isSignal: true }] }], typeCellTpl: [{ type: i0.ViewChild, args: ['typeCellTpl', { isSignal: true }] }], lagCellTpl: [{ type: i0.ViewChild, args: ['lagCellTpl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], configuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "configuration", required: false }] }], runtimeContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "runtimeContext", required: false }] }] } });

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

export { LookupMatrixField, SchedulePredecessorField, SchemaConnectionField };
//# sourceMappingURL=masterteam-components-business-fields.mjs.map
