import * as i0 from '@angular/core';
import { input, signal, inject, DestroyRef, effect, computed, ChangeDetectionStrategy, Component, model, contentChildren } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { startWith } from 'rxjs/operators';
import { coerceBooleanProperty } from '@angular/cdk/coercion';

class HeadlessStep {
    /** Form control associated with this step */
    stepControl = input(undefined, ...(ngDevMode ? [{ debugName: "stepControl" }] : /* istanbul ignore next */ []));
    /** Whether the step is optional */
    optional = input(false, ...(ngDevMode ? [{ debugName: "optional" }] : /* istanbul ignore next */ []));
    /** Whether the user has interacted with this step (writable signal) */
    interacted = signal(false, ...(ngDevMode ? [{ debugName: "interacted" }] : /* istanbul ignore next */ []));
    /** Whether this step is currently active (writable signal, controlled by Stepper) */
    isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
    /** Internal writable signal tracking the validity of the stepControl */
    _isValid = signal(true, ...(ngDevMode ? [{ debugName: "_isValid" }] : /* istanbul ignore next */ [])); // Default to true if no control
    _destroyRef = inject(DestroyRef); // Inject DestroyRef for automatic cleanup
    constructor() {
        effect(() => {
            const control = this.stepControl(); // Read the input signal
            if (control) {
                // Set initial validity based on the control's current state when the effect runs
                this._isValid.set(control.valid);
                // Subscribe to statusChanges and update the _isValid signal
                // Use takeUntilDestroyed for automatic cleanup when the component is destroyed
                control.statusChanges.pipe(startWith(control.status), // Emit initial status immediately to sync _isValid
                takeUntilDestroyed(this._destroyRef) // Automatically unsubscribe on component destroy
                ).subscribe(() => {
                    // Update the internal validity signal whenever the control's status changes
                    this._isValid.set(control.valid);
                });
            }
            else {
                // If no control is provided, consider the step inherently valid for completion
                this._isValid.set(true);
            }
        });
    }
    /** Computed signal indicating if the step is completed */
    completed = computed(() => {
        // A step is completed if it doesn't have a stepControl or
        // if the internal _isValid signal (reflecting the control's validity) is true.
        return !this.stepControl() || this._isValid();
    }, ...(ngDevMode ? [{ debugName: "completed" }] : /* istanbul ignore next */ []));
    /**
     * Checks if the step is currently valid.
     * Considers the validity of the associated stepControl if provided.
     * @returns boolean True if the step is valid, false otherwise.
     */
    isValid() {
        return this._isValid(); // Directly return the tracked validity state
    }
    /** Resets the step's interacted state */
    reset() {
        this.interacted.set(false);
    }
    /** Sets the active state of the step */
    setActive(active) {
        this.isActive.set(active);
    }
    /** Sets the interacted state of the step */
    setInteracted(interacted) {
        this.interacted.set(interacted);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HeadlessStep, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", type: HeadlessStep, isStandalone: true, selector: "ngs-headless-step", inputs: { stepControl: { classPropertyName: "stepControl", publicName: "stepControl", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.display": "isActive() ? \"block\" : \"none\"" }, classAttribute: "ngs-headless-step" }, exportAs: ["ngsHeadlessStep"], ngImport: i0, template: `<ng-content/>`, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HeadlessStep, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-headless-step', exportAs: 'ngsHeadlessStep', template: `<ng-content/>`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        'class': 'ngs-headless-step',
                        '[style.display]': 'isActive() ? "block" : "none"',
                    }, styles: [":host{display:block}\n"] }]
        }], ctorParameters: () => [], propDecorators: { stepControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepControl", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }] } });

class HeadlessStepper {
    /** Whether the stepper should operate in linear mode. */
    linear = input(false, { ...(ngDevMode ? { debugName: "linear" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
    /** The index of the active step (two-way bound). */
    selectedIndex = model(0, ...(ngDevMode ? [{ debugName: "selectedIndex" }] : /* istanbul ignore next */ []));
    /** Collection of child StepComponent instances. */
    steps = contentChildren(HeadlessStep, ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
    /** Computed signal for the total number of steps. */
    stepsCount = computed(() => {
        return this.steps()?.length ?? 0;
    }, ...(ngDevMode ? [{ debugName: "stepsCount" }] : /* istanbul ignore next */ []));
    /** The currently selected StepComponent instance. */
    selected = computed(() => {
        const index = this.selectedIndex();
        const stepList = this.steps();
        // Use the stepsCount() computed signal for checking length
        return stepList && index >= 0 && index < this.stepsCount() ? stepList[index] : undefined;
    }, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
    /** Checks if the user can navigate to the next step. */
    canMoveNext = computed(() => {
        const index = this.selectedIndex();
        // Use stepsCount() signal
        const totalSteps = this.stepsCount();
        const currentSelected = this.selected();
        const isLinear = this.linear();
        // Cannot move next if there are no steps or already on the last step
        if (totalSteps === 0 || index >= totalSteps - 1) {
            return false;
        }
        if (!isLinear || !currentSelected) {
            return true;
        }
        return currentSelected.completed() || currentSelected.optional();
    }, ...(ngDevMode ? [{ debugName: "canMoveNext" }] : /* istanbul ignore next */ []));
    /** Computed signal indicating if the current step is the first step. */
    isFirstStep = computed(() => {
        const index = this.selectedIndex();
        // Use stepsCount() signal
        return index === 0 && this.stepsCount() > 0;
    }, ...(ngDevMode ? [{ debugName: "isFirstStep" }] : /* istanbul ignore next */ []));
    /** Computed signal indicating if the current step is the last step. */
    isLastStep = computed(() => {
        const index = this.selectedIndex();
        // Use stepsCount() signal
        const totalSteps = this.stepsCount();
        return totalSteps > 0 && index === totalSteps - 1;
    }, ...(ngDevMode ? [{ debugName: "isLastStep" }] : /* istanbul ignore next */ []));
    /** Computed signal calculating the progress percentage. */
    progressPercent = computed(() => {
        const totalSteps = this.stepsCount();
        const index = this.selectedIndex();
        if (totalSteps === 0) {
            return 0;
        }
        const progress = (100 / totalSteps) * (index + 1);
        return Math.max(0, Math.min(100, progress));
    }, ...(ngDevMode ? [{ debugName: "progressPercent" }] : /* istanbul ignore next */ []));
    constructor() {
        effect(() => {
            const currentSteps = this.steps();
            const currentIndex = this.selectedIndex();
            currentSteps.forEach((step, index) => step.setActive(index === currentIndex));
        });
        effect((onCleanup) => {
            const currentSelected = this.selected();
            if (currentSelected && !currentSelected.interacted()) {
                currentSelected.setInteracted(true);
            }
        });
        effect(() => {
            const totalSteps = this.stepsCount();
            const currentIndex = this.selectedIndex();
            if (totalSteps > 0) {
                const newIndex = Math.max(0, Math.min(currentIndex, totalSteps - 1));
                if (newIndex !== currentIndex)
                    this.selectedIndex.set(newIndex);
            }
            else if (currentIndex !== 0) {
                this.selectedIndex.set(0);
            }
        });
    }
    /** Navigates to the next step if possible. */
    next() {
        if (this.canMoveNext()) {
            this.selectedIndex.update(i => i + 1);
        }
    }
    /** Navigates to the previous step if possible. */
    previous() {
        // Use renamed computed signal for check
        if (!this.isFirstStep()) {
            this.selectedIndex.update(i => i - 1);
        }
    }
    /** Resets the stepper to the first step. */
    reset() {
        this.steps().forEach(step => step.reset());
        this.selectedIndex.set(0);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HeadlessStepper, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.4", type: HeadlessStepper, isStandalone: true, selector: "ngs-headless-stepper", inputs: { linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange" }, host: { classAttribute: "ngs-headless-stepper" }, queries: [{ propertyName: "steps", predicate: HeadlessStep, isSignal: true }], exportAs: ["ngsHeadlessStepper"], ngImport: i0, template: `<ng-content select="ngs-step"/>`, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: HeadlessStepper, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-headless-stepper', exportAs: 'ngsHeadlessStepper', standalone: true, template: `<ng-content select="ngs-step"/>`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        'class': 'ngs-headless-stepper',
                    }, styles: [":host{display:block}\n"] }]
        }], ctorParameters: () => [], propDecorators: { linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }, { type: i0.Output, args: ["selectedIndexChange"] }], steps: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => HeadlessStep), { isSignal: true }] }] } });

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

export { HeadlessStep, HeadlessStepper };
//# sourceMappingURL=ngstarter-ui-components-headless-stepper.mjs.map
