import { assert } from "../../../shared/src/asserts.js";
import { omitFanout } from "./planner-node.js";
class PlannerFanIn {
  kind = "fan-in";
  #type;
  #output;
  #inputs;
  constructor(inputs) {
    this.#type = "FI";
    this.#inputs = inputs;
  }
  get type() {
    return this.#type;
  }
  closestJoinOrSource() {
    return "join";
  }
  setOutput(node) {
    this.#output = node;
  }
  get output() {
    assert(this.#output !== void 0, "Output not set");
    return this.#output;
  }
  reset() {
    this.#type = "FI";
  }
  convertToUFI() {
    this.#type = "UFI";
  }
  /**
   * Propagate unlimiting when a parent join is flipped.
   * Fan-in propagates to all of its inputs.
   */
  propagateUnlimitFromFlippedJoin() {
    for (const input of this.#inputs) {
      if ("propagateUnlimitFromFlippedJoin" in input && typeof input.propagateUnlimitFromFlippedJoin === "function") {
        input.propagateUnlimitFromFlippedJoin();
      }
    }
  }
  estimateCost(downstreamChildSelectivity, branchPattern, planDebugger) {
    let totalCost = {
      returnedRows: 0,
      cost: 0,
      scanEst: 0,
      startupCost: 0,
      selectivity: 0,
      limit: void 0,
      fanout: () => {
        throw new Error("Failed to set fanout model");
      }
    };
    if (this.#type === "FI") {
      const updatedPattern = [0, ...branchPattern];
      let maxrows = 0;
      let maxRunningCost = 0;
      let maxStartupCost = 0;
      let maxScanEst = 0;
      let noMatchProb = 1;
      for (const input of this.#inputs) {
        const cost = input.estimateCost(
          downstreamChildSelectivity,
          updatedPattern,
          planDebugger
        );
        totalCost.fanout = cost.fanout;
        if (cost.returnedRows > maxrows) {
          maxrows = cost.returnedRows;
        }
        if (cost.cost > maxRunningCost) {
          maxRunningCost = cost.cost;
        }
        if (cost.startupCost > maxStartupCost) {
          maxStartupCost = cost.startupCost;
        }
        if (cost.scanEst > maxScanEst) {
          maxScanEst = cost.scanEst;
        }
        noMatchProb *= 1 - cost.selectivity;
        assert(
          totalCost.limit === void 0 || cost.limit === totalCost.limit,
          "All FanIn inputs should have the same limit"
        );
        totalCost.limit = cost.limit;
      }
      totalCost.returnedRows = maxrows;
      totalCost.cost = maxRunningCost;
      totalCost.selectivity = 1 - noMatchProb;
      totalCost.startupCost = maxStartupCost;
      totalCost.scanEst = maxScanEst;
    } else {
      let i = 0;
      let noMatchProb = 1;
      for (const input of this.#inputs) {
        const updatedPattern = [i, ...branchPattern];
        const cost = input.estimateCost(
          downstreamChildSelectivity,
          updatedPattern,
          planDebugger
        );
        totalCost.fanout = cost.fanout;
        totalCost.returnedRows += cost.returnedRows;
        totalCost.cost += cost.cost;
        totalCost.scanEst += cost.scanEst;
        totalCost.startupCost = totalCost.startupCost + cost.startupCost;
        noMatchProb *= 1 - cost.selectivity;
        assert(
          totalCost.limit === void 0 || cost.limit === totalCost.limit,
          "All FanIn inputs should have the same limit"
        );
        totalCost.limit = cost.limit;
        i++;
      }
      totalCost.selectivity = 1 - noMatchProb;
    }
    if (planDebugger) {
      planDebugger.log({
        type: "node-cost",
        nodeType: "fan-in",
        node: this.#type,
        branchPattern,
        downstreamChildSelectivity,
        costEstimate: omitFanout(totalCost)
      });
    }
    return totalCost;
  }
  propagateConstraints(branchPattern, constraint, from, planDebugger) {
    planDebugger?.log({
      type: "node-constraint",
      nodeType: "fan-in",
      node: this.#type,
      branchPattern,
      constraint,
      from: from?.kind ?? "unknown"
    });
    if (this.#type === "FI") {
      const updatedPattern = [0, ...branchPattern];
      for (const input of this.#inputs) {
        input.propagateConstraints(
          updatedPattern,
          constraint,
          this,
          planDebugger
        );
      }
      return;
    }
    let i = 0;
    for (const input of this.#inputs) {
      input.propagateConstraints(
        [i, ...branchPattern],
        constraint,
        this,
        planDebugger
      );
      i++;
    }
  }
}
export {
  PlannerFanIn
};
//# sourceMappingURL=planner-fan-in.js.map
