/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { GeneralDiagnosticsBehavior } from "#behaviors/general-diagnostics";
import { ScenesManagementServer } from "#behaviors/scenes-management";
import { AggregatorEndpoint } from "#endpoints/aggregator";
import { ServerNode } from "#node/ServerNode.js";
import { BasicMultiplex, Millis, Time } from "@matter/general";
import { hasRemoteActor } from "@matter/protocol";
import { GeneralDiagnostics } from "@matter/types/clusters/general-diagnostics";
import { OnOff } from "@matter/types/clusters/on-off";
import { OnOffBehavior } from "./OnOffBehavior.js";
const OnOffLogicBase = OnOffBehavior.with(OnOff.Feature.Lighting);
class OnOffBaseServer extends OnOffLogicBase {
  initialize() {
    if (this.features.lighting && this.#getBootReason() !== GeneralDiagnostics.BootReason.SoftwareUpdateCompleted && !this.endpoint.ownerOfType(AggregatorEndpoint)) {
      const startUpOnOffValue = this.state.startUpOnOff ?? null;
      if (startUpOnOffValue !== null) {
        const currentOnOffStatus = this.state.onOff;
        const targetOnOffValue = startUpOnOffValue === OnOff.StartUpOnOff.Toggle ? !currentOnOffStatus : startUpOnOffValue === OnOff.StartUpOnOff.On;
        if (targetOnOffValue !== currentOnOffStatus) {
          this.state.onOff = targetOnOffValue;
        }
      }
    }
    if (this.features.lighting) {
      this.reactTo(this.events.onTime$Changed, this.#stopHeldTimer);
      this.reactTo(this.events.offWaitTime$Changed, this.#stopHeldTimer);
    }
    if (this.agent.has(ScenesManagementServer)) {
      this.agent.get(ScenesManagementServer).implementScenes(this, this.#applySceneValues);
      this.reactTo(this.events.onOff$Changed, this.#clearDelayedSceneApplyData);
    }
  }
  /**
   * Stop the active countdown timer when a write parks its attribute at a value that ends the countdown: 0 (the
   * decrement loop only runs while the value is > 0) or 0xFFFF (hold indefinitely). A running countdown picks up
   * any other written value on its next tick. Writes never start a countdown — only OnWithTimedOff does.
   */
  #stopHeldTimer() {
    if (this.state.onOff) {
      if (this.internal.timedOnTimer?.isRunning && (this.state.onTime === 0 || this.state.onTime === 65535)) {
        this.internal.timedOnTimer.stop();
      }
    } else if (this.internal.delayedOffTimer?.isRunning && (this.state.offWaitTime === 0 || this.state.offWaitTime === 65535)) {
      this.internal.delayedOffTimer.stop();
    }
  }
  get delayedPromises() {
    return this.internal.delayedPromises ??= new BasicMultiplex();
  }
  async [Symbol.asyncDispose]() {
    this.internal.timedOnTimer?.stop();
    this.internal.delayedOffTimer?.stop();
    await this.internal.delayedPromises?.close();
    await super[Symbol.asyncDispose]?.();
  }
  on() {
    if (!this.state.onOff) {
      this.#invalidateScenes();
    }
    this.state.onOff = true;
    if (this.features.lighting) {
      this.state.globalSceneControl = true;
      if ((this.state.onTime ?? 0) === 0) {
        if (this.delayedOffTimer.isRunning) {
          this.delayedOffTimer.stop();
        }
        this.state.offWaitTime = 0;
      }
    }
  }
  /** Invalidate all stored scenes manually for this endpoint in the Scenesmanagement cluster because SDK behavior. */
  #invalidateScenes() {
    this.agent.maybeGet(ScenesManagementServer)?.makeAllFabricSceneInfoEntriesInvalid();
  }
  off() {
    if (this.state.onOff) {
      this.#invalidateScenes();
    }
    this.state.onOff = false;
    if (this.features.lighting) {
      if (this.timedOnTimer.isRunning) {
        this.timedOnTimer.stop();
      }
      this.state.onTime = 0;
      if ((this.state.offWaitTime ?? 0) > 0 && this.state.offWaitTime !== 65535 && !this.delayedOffTimer.isRunning) {
        this.delayedOffTimer.start();
      }
    }
  }
  /**
   * Default implementation notes:
   * This method uses the on/off methods when timed actions should occur. This means that it is enough to override
   * on() and off() with custom control logic.
   */
  toggle() {
    if (this.state.onOff) {
      return this.off();
    } else {
      return this.on();
    }
  }
  /**
   * Default implementation notes:
   * * This implementation ignores the effect and just calls off().
   */
  offWithEffect() {
    if (this.state.globalSceneControl) {
      if (hasRemoteActor(this.context) && this.context.session.fabric !== void 0) {
        this.endpoint.agentFor(this.context).maybeGet(ScenesManagementServer)?.storeGlobalScene(this.context.session.fabric.fabricIndex);
      }
      this.state.globalSceneControl = false;
    }
    return this.off();
  }
  async onWithRecallGlobalScene() {
    if (this.state.globalSceneControl) {
      return;
    }
    if (hasRemoteActor(this.context) && this.context.session.fabric !== void 0 && this.agent.has(ScenesManagementServer)) {
      await this.endpoint.agentFor(this.context).maybeGet(ScenesManagementServer)?.recallGlobalScene(this.context.session.fabric.fabricIndex);
    }
    this.state.globalSceneControl = true;
    if (this.state.onTime === 0) {
      this.state.offWaitTime = 0;
    }
    await this.on();
  }
  /**
   * Default implementation notes:
   * * This method uses the on/off methods when timed actions should occur. This means that it is enough to override
   * on() and off() with custom control logic.
   */
  onWithTimedOff({ onOffControl, offWaitTime, onTime }) {
    if (onOffControl.acceptOnlyWhenOn && !this.state.onOff) {
      return;
    }
    if ((this.state.offWaitTime ?? 0) > 0 && !this.state.onOff) {
      this.state.offWaitTime = Math.min(offWaitTime ?? 0, this.state.offWaitTime ?? 0);
      if (!this.delayedOffTimer.isRunning && (this.state.offWaitTime ?? 0) > 0 && this.state.offWaitTime !== 65535) {
        this.delayedOffTimer.start();
      }
      return;
    }
    this.state.onTime = Math.max(onTime ?? 0, this.state.onTime ?? 0);
    this.state.offWaitTime = offWaitTime;
    if (this.state.onTime !== 0 && this.state.onTime !== 65535) {
      this.timedOnTimer.start();
    } else if (this.timedOnTimer.isRunning) {
      this.timedOnTimer.stop();
    }
    return this.on();
  }
  get timedOnTimer() {
    let timer = this.internal.timedOnTimer;
    if (timer === void 0) {
      timer = this.internal.timedOnTimer = Time.getPeriodicTimer(
        "Timed on",
        Millis(100),
        this.callback(this.#timedOnTick, { lock: true })
      );
    }
    return timer;
  }
  async #timedOnTick() {
    if (this.state.onTime === 65535) {
      this.internal.timedOnTimer?.stop();
      return;
    }
    let time = (this.state.onTime ?? 0) - 1;
    if (time <= 0) {
      time = 0;
      this.internal.timedOnTimer?.stop();
      this.state.offWaitTime = 0;
      await this.off();
    }
    this.state.onTime = time;
  }
  get delayedOffTimer() {
    let timer = this.internal.delayedOffTimer;
    if (timer === void 0) {
      timer = this.internal.delayedOffTimer = Time.getPeriodicTimer(
        "Delayed off",
        Millis(100),
        this.callback(this.#delayedOffTick, { lock: true })
      );
    }
    return timer;
  }
  /** Apply Scene values when requested from ScenesManagement cluster */
  #applySceneValues(values, transitionTime) {
    this.#clearDelayedSceneApplyData();
    const onOff = values.onOff;
    if (typeof onOff !== "boolean" || this.state.onOff === onOff) {
      return;
    }
    if (transitionTime === 0) {
      if (onOff) {
        return this.on();
      }
      return this.off();
    }
    this.internal.applyScenePendingOnOff = onOff;
    this.internal.applySceneDelayTimer = Time.getTimer(
      "delayed scene apply",
      Millis(transitionTime),
      this.callback(this.#applyDelayedSceneOnOffValue)
    ).start();
  }
  #clearDelayedSceneApplyData() {
    if (this.internal.applySceneDelayTimer?.isRunning) {
      this.internal.applySceneDelayTimer.stop();
    }
    this.internal.applySceneDelayTimer = void 0;
    this.internal.applyScenePendingOnOff = void 0;
  }
  #applyDelayedSceneOnOffValue() {
    const onOff = this.internal.applyScenePendingOnOff;
    this.#clearDelayedSceneApplyData();
    if (onOff === void 0) {
      return;
    }
    if (onOff) {
      this.delayedPromises.add(this.on());
    } else {
      this.delayedPromises.add(this.off());
    }
  }
  #delayedOffTick() {
    if (this.state.offWaitTime === 65535) {
      this.internal.delayedOffTimer?.stop();
      return;
    }
    let time = (this.state.offWaitTime ?? 0) - 1;
    if (time <= 0) {
      time = 0;
      this.internal.delayedOffTimer?.stop();
    }
    this.state.offWaitTime = time;
  }
  #getBootReason() {
    const rootEndpoint = this.env.get(ServerNode);
    if (rootEndpoint.behaviors.has(GeneralDiagnosticsBehavior)) {
      return rootEndpoint.stateOf(GeneralDiagnosticsBehavior).bootReason;
    }
  }
}
((OnOffBaseServer2) => {
  class Internal {
    timedOnTimer;
    delayedOffTimer;
    applySceneDelayTimer;
    applyScenePendingOnOff;
    delayedPromises;
  }
  OnOffBaseServer2.Internal = Internal;
})(OnOffBaseServer || (OnOffBaseServer = {}));
class OnOffServer extends OnOffBaseServer.with() {
}
export {
  OnOffBaseServer,
  OnOffServer
};
//# sourceMappingURL=OnOffServer.js.map
