import { Knex } from "knex";
import {
  createTenantKnex,
  withTransaction,
} from "@alga-psa/db";
import {
  IBillingPeriod,
  IBillingResult,
  IBillingCharge,
  IClientContractLine,
  IBucketUsage,
  IBucketCharge,
  IDiscount,
  IAdjustment,
  IUsageBasedCharge,
  ITimeBasedCharge,
  IFixedPriceCharge,
  IProductCharge,
  ILicenseCharge,
  IClientContractLineCycle,
  IRecurringServicePeriod,
  IRecurringServicePeriodRecord,
  BillingCycleType,
  DEFAULT_RECURRING_SERVICE_PERIOD_DUE_SELECTION_STATES,
  RECURRING_RANGE_SEMANTICS,
} from "@alga-psa/types";
import {
  IContractLineServiceConfiguration,
  IContractLineServiceFixedConfig,
  IContractLineServiceHourlyConfig,
  IContractLineServiceUsageConfig,
  IContractLineServiceBucketConfig,
  IContractLineServiceRateTier,
} from "@alga-psa/types";
// Use the Temporal polyfill for all date arithmetic and plain‐date handling
import { Temporal } from "@js-temporal/polyfill";
import type { ISO8601String, IClient, IRecurringObligationRef } from "@alga-psa/types";
import { toPlainDate, toISODate, toISOTimestamp, getCurrencySymbol } from "@alga-psa/core";
import { getClientDefaultTaxRegionCode as getClientDefaultTaxRegionCodeShared } from "@alga-psa/shared/billingClients";
import {
  calculateServicePeriodCoverage,
  resolveCadenceOwner,
  resolveRecurringSettlementsForInvoiceWindow,
} from "@alga-psa/shared/billingClients/recurringTiming";
import {
  generateAnnualContractCadenceServicePeriods,
  generateMonthlyContractCadenceServicePeriods,
  generateQuarterlyContractCadenceServicePeriods,
  generateSemiAnnualContractCadenceServicePeriods,
  resolveContractCadenceAnchorDate,
  resolveContractCadenceInvoiceWindowForServicePeriod,
} from "@alga-psa/shared/billingClients/contractCadenceServicePeriods";
import {
  buildPostDropRecurringObligationCandidates,
  buildClientCadencePostDropObligationRef,
  CLIENT_CADENCE_POST_DROP_OBLIGATION_TYPE,
  POST_DROP_RECURRING_OBLIGATION_TYPES,
} from "@alga-psa/shared/billingClients/postDropRecurringObligationIdentity";
// Removed TaxService import as it's no longer directly used here
// Import necessary functions from invoiceService
import {
  calculateAndDistributeTax,
  updateInvoiceTotalsAndRecordTransaction,
  getClientDetails,
} from "../../services/invoiceService";
import { v4 as uuidv4 } from "uuid";
import ContractLineFixedConfig from "../../models/contractLineFixedConfig"; // Added import for new model
import contractLine from "../../models/contractLine";
import service from "../../models/service";
import { TaxService } from "../../services/taxService";
import { ClientContractServiceConfigurationService } from "../../services/clientContractServiceConfigurationService";
// Workflow imports removed as event emission is moved back to the calling action

type DiscountQueryRow = IDiscount & {
  contract_line_id?: string | null;
  start_date: ISO8601String;
  end_date?: ISO8601String | null;
};

type ResolvedRecurringChargeTiming = {
  duePosition: "arrears" | "advance";
  servicePeriodStart: ISO8601String;
  servicePeriodEnd: ISO8601String;
  servicePeriodStartExclusive: ISO8601String;
  servicePeriodEndExclusive: ISO8601String;
  coverageRatio: number;
};

type RecurringChargeTimingSelections = Record<
  string,
  ResolvedRecurringChargeTiming
>;

type CalculateBillingOptions = {
  recurringTimingSelections?: RecurringChargeTimingSelections;
  recurringTimingSelectionSource?: "derived" | "persisted";
  nonContractSelection?: {
    include: boolean;
    timeEntryIds?: string[];
    usageRecordIds?: string[];
  };
};

type PersistedRecurringTimingSelectionRecord = Pick<
  IRecurringServicePeriodRecord,
  "sourceObligation" | "cadenceOwner" | "duePosition" | "servicePeriod" | "activityWindow"
>;

type ContractCadenceGenerator = typeof generateMonthlyContractCadenceServicePeriods;

const RECURRING_TIMING_ROLLOUT_GUARD_PREFIX =
  "Recurring timing rollout guard blocked mixed legacy/canonical timing state";

export class BillingEngine {
  private knex: Knex;
  private tenant: string | null;
  private readonly clientDefaultTaxRegionCodeCache = new Map<
    string,
    string | null
  >();
  private readonly locationTaxRegionCodeCache = new Map<
    string,
    string | null
  >();

  constructor() {
    this.knex = null as any;
    this.tenant = null;
  }

  private async initKnex() {
    if (!this.knex) {
      const { knex, tenant } = await createTenantKnex();
      if (!tenant) {
        throw new Error("tenant context not found");
      }
      this.knex = knex;
      this.tenant = tenant;
    }
  }

  private isTransactionKnex(
    knex: Knex | Knex.Transaction,
  ): knex is Knex.Transaction {
    return (
      typeof (knex as Knex.Transaction).commit === "function" &&
      typeof (knex as Knex.Transaction).rollback === "function"
    );
  }

  private async withPinnedTransaction<T>(
    callback: (trx: Knex.Transaction) => Promise<T>,
  ): Promise<T> {
    await this.initKnex();

    if (this.isTransactionKnex(this.knex)) {
      return callback(this.knex);
    }

    return withTransaction(this.knex, async (trx) => {
      const previousKnex = this.knex;
      this.knex = trx;
      try {
        return await callback(trx);
      } finally {
        this.knex = previousKnex;
      }
    });
  }

  private getNextBillingDateForCycle(
    currentEndDate: ISO8601String,
    billingCycle: BillingCycleType,
  ): ISO8601String {
    const currentDate = toPlainDate(currentEndDate);
    let nextDate: Temporal.PlainDate;

    switch (billingCycle) {
      case "weekly":
        nextDate = currentDate.add({ days: 7 });
        break;
      case "bi-weekly":
        nextDate = currentDate.add({ days: 14 });
        break;
      case "monthly":
        nextDate = currentDate.add({ months: 1 });
        break;
      case "quarterly":
        nextDate = currentDate.add({ months: 3 });
        break;
      case "semi-annually":
        nextDate = currentDate.add({ months: 6 });
        break;
      case "annually":
        nextDate = currentDate.add({ years: 1 });
        break;
      default:
        nextDate = currentDate.add({ months: 1 });
        break;
    }

    return toISODate(nextDate);
  }

  private async getClientDefaultTaxRegionCode(
    clientId: string,
  ): Promise<string | null> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const cacheKey = `${this.tenant}:${clientId}`;
    if (this.clientDefaultTaxRegionCodeCache.has(cacheKey)) {
      return this.clientDefaultTaxRegionCodeCache.get(cacheKey) ?? null;
    }

    const taxRegionCode = await getClientDefaultTaxRegionCodeShared(
      this.knex,
      this.tenant,
      clientId,
    );
    this.clientDefaultTaxRegionCodeCache.set(cacheKey, taxRegionCode);
    return taxRegionCode;
  }

  private async getLocationTaxRegionCode(
    locationId: string | null | undefined,
  ): Promise<string | null> {
    if (!locationId) {
      return null;
    }
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const cacheKey = `${this.tenant}:${locationId}`;
    if (this.locationTaxRegionCodeCache.has(cacheKey)) {
      return this.locationTaxRegionCodeCache.get(cacheKey) ?? null;
    }

    const row = await this.knex("client_locations")
      .where({ tenant: this.tenant, location_id: locationId })
      .select("region_code")
      .first();

    const regionCode = (row?.region_code as string | null | undefined) ?? null;
    this.locationTaxRegionCodeCache.set(cacheKey, regionCode);
    return regionCode;
  }

  /**
   * Determines the tax region and taxability based on a service's tax_rate_id.
   * @param service - The service object, expected to have service_id and tax_rate_id.
   * @returns An object containing the taxRegion (string | null) and isTaxable (boolean).
   */
  private async getTaxInfoFromService(
    service: any,
  ): Promise<{ taxRegion: string | null; isTaxable: boolean }> {
    if (!this.knex || !this.tenant) {
      await this.initKnex(); // Ensure Knex is initialized
      if (!this.tenant)
        throw new Error("Tenant context not found in getTaxInfoFromService");
    }

    // Default values if no service is provided or found
    if (!service) {
      console.warn("[getTaxInfoFromService] No service object provided.");
      return { taxRegion: null, isTaxable: false }; // Assuming non-taxable if no service context
    }

    if (service.tax_rate_id) {
      try {
        const taxRateInfo = await this.knex("tax_rates")
          .where({ tax_rate_id: service.tax_rate_id, tenant: this.tenant })
          // TODO: Add validity checks if needed (e.g., is_active, date range matching billing period)
          .select("region_code")
          .first();

        if (taxRateInfo && taxRateInfo.region_code) {
          // Valid tax_rate_id found, service is taxable in this region
          return { taxRegion: taxRateInfo.region_code, isTaxable: true };
        } else {
          // tax_rate_id exists but doesn't link to a valid/active rate? Treat as non-taxable.
          console.warn(
            `[getTaxInfoFromService] Service ${service.service_id} has tax_rate_id ${service.tax_rate_id} but no matching/valid tax_rate found in tenant ${this.tenant}. Treating as non-taxable.`,
          );
          return { taxRegion: null, isTaxable: false };
        }
      } catch (error) {
        console.error(
          `[getTaxInfoFromService] Error fetching tax rate info for tax_rate_id ${service.tax_rate_id}:`,
          error,
        );
        return { taxRegion: null, isTaxable: false }; // Treat as non-taxable on error
      }
    } else {
      // Service exists but tax_rate_id is NULL, explicitly non-taxable
      return { taxRegion: null, isTaxable: false };
    }
  }

  // Removed getDefaultTaxRatePercentage function as it uses outdated logic
  // and tax calculation is now delegated to invoiceService.

  private async hasExistingInvoiceForCycle(
    clientId: string,
    billingCycleId: string,
  ): Promise<boolean> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const existingInvoice = await this.knex("invoices")
      .where({
        client_id: clientId,
        billing_cycle_id: billingCycleId,
        tenant: this.tenant,
      })
      .first();
    return !!existingInvoice;
  }

  async calculateBilling(
    clientId: string,
    startDate: ISO8601String,
    endDate: ISO8601String,
    billingCycleId: string,
    options: CalculateBillingOptions = {},
  ): Promise<IBillingResult & { error?: string }> {
    this.clientDefaultTaxRegionCodeCache.clear();
    this.locationTaxRegionCodeCache.clear();
    return this.withPinnedTransaction(async () => {
      return this.calculateBillingInternal(
        clientId,
        startDate,
        endDate,
        billingCycleId,
        options,
      );
    });
  }

  async calculateBillingForExecutionWindow(
    clientId: string,
    startDate: ISO8601String,
    endDate: ISO8601String,
    options: CalculateBillingOptions = {},
  ): Promise<IBillingResult & { error?: string }> {
    this.clientDefaultTaxRegionCodeCache.clear();
    this.locationTaxRegionCodeCache.clear();
    return this.withPinnedTransaction(async () => {
      await this.initKnex();
      const client = await this.knex<IClient>("clients")
        .where({ client_id: clientId, tenant: this.tenant! })
        .first();

      const billingPeriod: IBillingPeriod = {
        startDate: toISODate(toPlainDate(startDate)),
        endDate: toISODate(toPlainDate(endDate)),
      };

      console.log(
        `Calculating billing for client ${client?.client_name} (${clientId}) using execution window: ${billingPeriod.startDate} to ${billingPeriod.endDate}`,
      );

      return this.calculateBillingForPreparedPeriod(
        clientId,
        billingPeriod,
        client,
        options,
      );
    });
  }

  async selectDueRecurringServicePeriodsForBillingWindow(
    clientId: string,
    startDate: ISO8601String,
    endDate: ISO8601String,
  ): Promise<RecurringChargeTimingSelections> {
    this.clientDefaultTaxRegionCodeCache.clear();
    this.locationTaxRegionCodeCache.clear();
    return this.withPinnedTransaction(async () => {
      const billingPeriod: IBillingPeriod = {
        startDate: toISODate(toPlainDate(startDate)),
        endDate: toISODate(toPlainDate(endDate)),
      };
      const clientContractLines =
        await this.getClientContractLinesForBillingPeriod(
          clientId,
          billingPeriod,
        );
      const persistedSelections =
        await this.loadPersistedRecurringTimingSelections(
          billingPeriod,
          clientContractLines,
        );

      if (persistedSelections === null) {
        throw new Error(
          `Recurring service periods have not been materialized for client ${clientId} in execution window ${billingPeriod.startDate} to ${billingPeriod.endDate}`,
        );
      }

      return persistedSelections;
    });
  }

  private async loadPersistedRecurringTimingSelections(
    billingPeriod: IBillingPeriod,
    clientContractLines: IClientContractLine[],
  ): Promise<RecurringChargeTimingSelections | null> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const eligibleLineIds = [...new Set(
      clientContractLines.map((line) => line.client_contract_line_id),
    )];

    if (eligibleLineIds.length === 0) {
      return {};
    }

    const dueRows = await this.knex("recurring_service_periods")
      .where({ tenant: this.tenant })
      .whereIn("obligation_id", eligibleLineIds)
      .whereIn("obligation_type", [...POST_DROP_RECURRING_OBLIGATION_TYPES])
      .whereIn(
        "lifecycle_state",
        [...DEFAULT_RECURRING_SERVICE_PERIOD_DUE_SELECTION_STATES],
      )
      .where("invoice_window_start", billingPeriod.startDate)
      .where("invoice_window_end", billingPeriod.endDate)
      .whereNull("invoice_charge_detail_id")
      .orderBy("obligation_id", "asc")
      .orderBy("service_period_start", "asc")
      .orderBy("revision", "asc")
      .select(
        "obligation_id",
        "obligation_type",
        "charge_family",
        "cadence_owner",
        "due_position",
        "service_period_start",
        "service_period_end",
        "activity_window_start",
        "activity_window_end",
      );

    if (dueRows.length === 0) {
      const existingMaterializedRow = await this.knex("recurring_service_periods")
        .where({ tenant: this.tenant })
        .whereIn("obligation_id", eligibleLineIds)
        .whereIn("obligation_type", [...POST_DROP_RECURRING_OBLIGATION_TYPES])
        .whereNotIn("lifecycle_state", ["archived", "superseded"])
        .first("record_id");

      return existingMaterializedRow ? {} : null;
    }

    return this.buildRecurringTimingSelectionsFromPersistedRecords(
      dueRows.map((row) => ({
        sourceObligation: {
          tenant: this.tenant!,
          obligationId: row.obligation_id,
          obligationType: row.obligation_type,
          chargeFamily: row.charge_family,
        },
        cadenceOwner: row.cadence_owner,
        duePosition: row.due_position,
        servicePeriod: {
          start: toISODate(toPlainDate(row.service_period_start)),
          end: toISODate(toPlainDate(row.service_period_end)),
          semantics: RECURRING_RANGE_SEMANTICS,
        },
        activityWindow:
          row.activity_window_start && row.activity_window_end
            ? {
                start: toISODate(toPlainDate(row.activity_window_start)),
                end: toISODate(toPlainDate(row.activity_window_end)),
                semantics: RECURRING_RANGE_SEMANTICS,
              }
            : null,
      })),
    );
  }

  private async calculateBillingInternal(
    clientId: string,
    startDate: ISO8601String,
    endDate: ISO8601String,
    billingCycleId: string,
    options: CalculateBillingOptions = {},
  ): Promise<IBillingResult & { error?: string }> {
    try {
      await this.initKnex();
      const client = await this.knex<IClient>("clients")
        .where({ client_id: clientId, tenant: this.tenant! })
        .first();
      console.log(
        `Calculating billing for client ${client?.client_name} (${clientId}) using billingCycleId: ${billingCycleId}`,
      );

      // Fetch the specific billing cycle record
      const cycleRecord = await this.knex("client_billing_cycles")
        .where({
          billing_cycle_id: billingCycleId,
          client_id: clientId, // Ensure it matches the client
          tenant: this.tenant,
        })
        .first();

      if (!cycleRecord) {
        return {
          charges: [],
          totalAmount: 0,
          discounts: [],
          adjustments: [],
          finalAmount: 0,
          currency_code: client?.default_currency_code || "USD",
          error: `Billing cycle ${billingCycleId} not found for client ${clientId}`,
        };
      }

      // Check for existing invoice in this billing cycle (using the fetched cycleRecord)
      const hasExistingInvoice = await this.hasExistingInvoiceForCycle(
        clientId,
        cycleRecord.billing_cycle_id,
      );
      if (hasExistingInvoice) {
        // Return zero-amount billing result if already invoiced
        return {
          charges: [],
          totalAmount: 0,
          discounts: [],
          adjustments: [],
          finalAmount: 0,
          currency_code: client?.default_currency_code || "USD",
        };
      }

      // Determine billing period dates CONSISTENTLY
      let periodStartDate: ISO8601String;
      let periodEndDate: ISO8601String;

      if (cycleRecord.period_start_date && cycleRecord.period_end_date) {
        console.log(
          `Using period dates from cycle record: ${cycleRecord.period_start_date} to ${cycleRecord.period_end_date}`,
        );
        // Ensure dates are in the correct plain date format before converting
        periodStartDate = toISODate(toPlainDate(cycleRecord.period_start_date));
        periodEndDate = toISODate(toPlainDate(cycleRecord.period_end_date));
      } else if (cycleRecord.effective_date) {
        console.log(
          `Calculating period dates from effective date: ${cycleRecord.effective_date}`,
        );
        // Ensure effective_date is in the correct plain date format
        const effectivePlainDate = toPlainDate(cycleRecord.effective_date);
        periodStartDate = toISODate(effectivePlainDate); // Start date is the effective date
        // Need client billing frequency to calculate end date accurately
        // Use the cycle's effective date to determine the relevant frequency
        const clientBillingCycle = (await this.getBillingCycle(
          clientId,
          periodStartDate,
        )) as BillingCycleType;
        const nextBillingDate = this.getNextBillingDateForCycle(
          periodStartDate,
          clientBillingCycle,
        );
        // Billing periods are treated as [start, end) (end exclusive).
        // The end date is the start of the next cycle.
        periodEndDate = toISODate(toPlainDate(nextBillingDate));
        console.log(
          `Calculated period: ${periodStartDate} to ${periodEndDate}`,
        );
      } else {
        return {
          charges: [],
          totalAmount: 0,
          discounts: [],
          adjustments: [],
          finalAmount: 0,
          currency_code: client?.default_currency_code || "USD",
          error: `Billing cycle ${billingCycleId} has invalid dates (no period dates or effective date)`,
        };
      }

      const billingPeriod: IBillingPeriod = {
        startDate: periodStartDate,
        endDate: periodEndDate,
      };
      console.log(
        `Consistent billing period: ${billingPeriod.startDate} to ${billingPeriod.endDate}`,
      );

      return this.calculateBillingForPreparedPeriod(
        clientId,
        billingPeriod,
        client,
        options,
      );
    } catch (err) {
      console.error("Error in calculateBilling:", err);
      return {
        charges: [],
        totalAmount: 0,
        discounts: [],
        adjustments: [],
        finalAmount: 0,
        currency_code: "USD", // Default on error
        error:
          err instanceof Error
            ? err.message
            : "An error occurred while calculating billing",
      };
    }
  }

  private async calculateBillingForPreparedPeriod(
    clientId: string,
    billingPeriod: IBillingPeriod,
    client: IClient | undefined,
    options: CalculateBillingOptions = {},
  ): Promise<IBillingResult & { error?: string }> {
    if (options.recurringTimingSelectionSource !== "persisted") {
      // Legacy cycle validation is still relevant for cycle-driven/manual runs.
      const validationResult = await this.validateBillingPeriod(
        clientId,
        billingPeriod.startDate,
        billingPeriod.endDate,
      );
      if (!validationResult.success) {
        return {
          charges: [],
          totalAmount: 0,
          discounts: [],
          adjustments: [],
          finalAmount: 0,
          currency_code: client?.default_currency_code || "USD",
          error: validationResult.error,
        };
      }
    }

    let totalCharges: IBillingCharge[] = [];
    let clientContractLines: IClientContractLine[] = [];
    let cycle: string | undefined;

    if (options.recurringTimingSelectionSource === "persisted") {
      clientContractLines = await this.getClientContractLinesForBillingPeriod(
        clientId,
        billingPeriod,
      );
    } else {
      const plansResult = await this.getClientContractLinesAndCycle(
        clientId,
        billingPeriod,
      );

      const {
        clientContractLines: loadedContractLines,
        billingCycle,
        error: plansError,
      } = plansResult as {
        clientContractLines: IClientContractLine[];
        billingCycle: string;
        error?: string;
      };

      if (plansError) {
        return {
          charges: [],
          totalAmount: 0,
          discounts: [],
          adjustments: [],
          finalAmount: 0,
          currency_code: client?.default_currency_code || "USD",
          error: plansError,
        };
      }

      clientContractLines = loadedContractLines;
      cycle = billingCycle;
    }

    const uniqueCurrencies = Array.from(
      new Set(
        clientContractLines
          .map((line) => line.currency_code)
          .filter((code): code is string => !!code),
      ),
    );

    if (uniqueCurrencies.length > 1) {
      return {
        charges: [],
        totalAmount: 0,
        discounts: [],
        adjustments: [],
        finalAmount: 0,
        currency_code: client?.default_currency_code || "USD",
        error: `Billing Error: Client ${clientId} has active contracts in multiple currencies (${uniqueCurrencies.join(", ")}). Mixed currency billing is not supported.`,
      };
    }

    const billingCurrency =
      uniqueCurrencies.length === 1
        ? uniqueCurrencies[0]
        : client?.default_currency_code || "USD";

    console.log(
      `[BillingEngine] Resolved billing currency: ${billingCurrency}`,
    );

    const recurringTimingSelections = options.recurringTimingSelections
      ? options.recurringTimingSelectionSource === "persisted"
        ? this.assertPersistedRecurringTimingSelectionsReferenceEligibleLines(
            clientContractLines,
            options.recurringTimingSelections,
          )
        : this.assertRecurringTimingSelectionsMatchCanonical(
            this.buildRecurringTimingSelections(
              billingPeriod,
              clientContractLines,
              cycle!,
            ),
            options.recurringTimingSelections,
          )
      : cycle
        ? this.buildRecurringTimingSelections(
            billingPeriod,
            clientContractLines,
            cycle,
          )
        : {};
    const nonContractSelection = options.nonContractSelection;

    const materialCharges = await this.calculateMaterialCharges(
      clientId,
      billingPeriod,
      billingCurrency,
    );

    if (clientContractLines.length === 0 && !nonContractSelection?.include) {
      if (materialCharges.length === 0) {
        return {
          charges: [],
          totalAmount: 0,
          discounts: [],
          adjustments: [],
          finalAmount: 0,
          currency_code: billingCurrency,
          error:
            "No active contract lines found for this client in the selected billing period.",
        };
      }

      totalCharges = totalCharges.concat(materialCharges);
    }

    console.log(
      `Found ${clientContractLines.length} active contract line(s) for client ${clientId}`,
    );
    if (cycle) {
      console.log(`Billing cycle: ${cycle}`);
    } else if (options.recurringTimingSelectionSource === "persisted") {
      console.log(
        "[BillingEngine] Using persisted recurring service-period timing without client billing cycle lookup",
      );
    }

    for (const clientContractLine of clientContractLines) {
      console.log(
        `Processing contract line: ${clientContractLine.contract_line_name}`,
      );
      const [
        fixedPriceCharges,
        timeBasedCharges,
        usageBasedCharges,
        bucketPlanCharges,
        productCharges,
        licenseCharges,
      ] = await Promise.all([
        this.calculateFixedPriceCharges(
          clientId,
          billingPeriod,
          clientContractLine,
          cycle,
          recurringTimingSelections[clientContractLine.client_contract_line_id],
          options.recurringTimingSelectionSource,
        ),
        this.calculateTimeBasedCharges(
          clientId,
          billingPeriod,
          clientContractLine,
          cycle,
          recurringTimingSelections[clientContractLine.client_contract_line_id],
          options.recurringTimingSelectionSource,
        ),
        this.calculateUsageBasedCharges(
          clientId,
          billingPeriod,
          clientContractLine,
          cycle,
          recurringTimingSelections[clientContractLine.client_contract_line_id],
          options.recurringTimingSelectionSource,
        ),
        this.calculateBucketPlanCharges(
          clientId,
          billingPeriod,
          clientContractLine,
        ),
        this.calculateProductCharges(
          clientId,
          billingPeriod,
          clientContractLine,
          cycle,
          recurringTimingSelections[clientContractLine.client_contract_line_id],
          options.recurringTimingSelectionSource,
        ),
        this.calculateLicenseCharges(
          clientId,
          billingPeriod,
          clientContractLine,
          cycle,
          recurringTimingSelections[clientContractLine.client_contract_line_id],
          options.recurringTimingSelectionSource,
        ),
      ]);

      console.log(`Fixed price charges: ${fixedPriceCharges.length}`);
      console.log(`Time-based charges: ${timeBasedCharges.length}`);
      console.log(`Usage-based charges: ${usageBasedCharges.length}`);
      console.log(`Bucket plan charges: ${bucketPlanCharges.length}`);
      console.log(`Product charges: ${productCharges.length}`);
      console.log(`License charges: ${licenseCharges.length}`);

      const totalFixedCharges = fixedPriceCharges.reduce(
        (sum: number, charge: IFixedPriceCharge) => sum + charge.total,
        0,
      );
      console.log(
        `Total fixed charges: ${getCurrencySymbol(billingCurrency)}${(totalFixedCharges / 100).toFixed(2)} (${totalFixedCharges} cents)`,
      );

      totalCharges = totalCharges.concat(
        fixedPriceCharges,
        timeBasedCharges,
        usageBasedCharges,
        bucketPlanCharges,
        productCharges,
        licenseCharges,
      );

      console.log("Total charges breakdown:");
      const currencySymbol = getCurrencySymbol(billingCurrency);
      fixedPriceCharges.forEach((charge: IBillingCharge) => {
        console.log(
          `fixed - ${charge.serviceName}: ${currencySymbol}${(charge.total / 100).toFixed(2)}`,
        );
      });
      timeBasedCharges.forEach((charge: ITimeBasedCharge) => {
        console.log(
          `hourly - ${charge.serviceName}: ${currencySymbol}${(charge.total / 100).toFixed(2)}`,
        );
      });
      usageBasedCharges.forEach((charge: IUsageBasedCharge) => {
        console.log(
          `usage - ${charge.serviceName}: ${currencySymbol}${(charge.total / 100).toFixed(2)}`,
        );
      });
      bucketPlanCharges.forEach((charge: IBucketCharge) => {
        console.log(
          `bucket - ${charge.serviceName}: ${currencySymbol}${(charge.total / 100).toFixed(2)}`,
        );
      });
      productCharges.forEach((charge: IProductCharge) => {
        console.log(
          `product - ${charge.serviceName}: ${currencySymbol}${(charge.total / 100).toFixed(2)}`,
        );
      });
      licenseCharges.forEach((charge: ILicenseCharge) => {
        console.log(
          `license - ${charge.serviceName}: ${currencySymbol}${(charge.total / 100).toFixed(2)}`,
        );
      });

      console.log("Total charges:", totalCharges);
    }

    if (clientContractLines.length > 0 && materialCharges.length > 0) {
      totalCharges = totalCharges.concat(materialCharges);
    }

    if (nonContractSelection?.include) {
      const nonContractCharges = await this.calculateUnresolvedNonContractCharges(
        clientId,
        billingPeriod,
        {
          timeEntryIds: nonContractSelection.timeEntryIds,
          usageRecordIds: nonContractSelection.usageRecordIds,
        },
      );
      totalCharges = totalCharges.concat(nonContractCharges);
    }

    const totalAmount = totalCharges.reduce(
      (sum: number, charge: IBillingCharge) => sum + charge.total,
      0,
    );

    const finalCharges = await this.applyDiscountsAndAdjustments(
      {
        charges: totalCharges,
        totalAmount,
        discounts: [],
        adjustments: [],
        finalAmount: totalAmount,
        currency_code: billingCurrency,
      },
      clientId,
      billingPeriod,
    );

    console.log(`Discounts applied: ${finalCharges.discounts.length}`);
    console.log(`Adjustments applied: ${finalCharges.adjustments.length}`);
    console.log(
      `Final amount after discounts and adjustments: ${getCurrencySymbol(billingCurrency)}${(finalCharges.finalAmount / 100).toFixed(2)} (${finalCharges.finalAmount} cents)`,
    );

    return finalCharges;
  }

  async calculateUnresolvedNonContractChargesForExecutionWindow(input: {
    clientId: string;
    windowStart: ISO8601String;
    windowEnd: ISO8601String;
    selectedTimeEntryIds?: string[];
    selectedUsageRecordIds?: string[];
  }): Promise<IBillingCharge[]> {
    return this.withPinnedTransaction(async () => {
      const billingPeriod: IBillingPeriod = {
        startDate: toISODate(toPlainDate(input.windowStart)),
        endDate: toISODate(toPlainDate(input.windowEnd)),
      };
      return this.calculateUnresolvedNonContractCharges(
        input.clientId,
        billingPeriod,
        {
          timeEntryIds: input.selectedTimeEntryIds,
          usageRecordIds: input.selectedUsageRecordIds,
        },
      );
    });
  }

  private async getEligibleContractLineIdsForServiceAtDate(input: {
    clientId: string;
    serviceId: string;
    workDate: ISO8601String;
  }): Promise<string[]> {
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const rows = await this.knex("client_contracts as cc")
      .join("contracts as c", function () {
        this.on("c.contract_id", "=", "cc.contract_id").andOn(
          "c.tenant",
          "=",
          "cc.tenant",
        );
      })
      .join("contract_lines as cl", function () {
        this.on("cl.contract_id", "=", "c.contract_id").andOn(
          "cl.tenant",
          "=",
          "c.tenant",
        );
      })
      .join("contract_line_services as cls", function () {
        this.on("cls.contract_line_id", "=", "cl.contract_line_id").andOn(
          "cls.tenant",
          "=",
          "cl.tenant",
        );
      })
      .where({
        "cc.tenant": this.tenant,
        "cc.client_id": input.clientId,
        "cc.is_active": true,
        "cls.service_id": input.serviceId,
      })
      .where("cc.start_date", "<=", input.workDate)
      .where(function (this: Knex.QueryBuilder) {
        this.whereNull("cc.end_date").orWhere("cc.end_date", ">=", input.workDate);
      })
      .distinct("cl.contract_line_id")
      .select("cl.contract_line_id");

    return rows
      .map((row: any) => row.contract_line_id)
      .filter((lineId: unknown): lineId is string =>
        typeof lineId === "string" && lineId.length > 0,
      );
  }

  private async calculateUnresolvedNonContractCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    selection?: {
      timeEntryIds?: string[];
      usageRecordIds?: string[];
    },
  ): Promise<IBillingCharge[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const selectedTimeEntryIds =
      selection?.timeEntryIds && selection.timeEntryIds.length > 0
        ? new Set(selection.timeEntryIds)
        : null;
    const selectedUsageRecordIds =
      selection?.usageRecordIds && selection.usageRecordIds.length > 0
        ? new Set(selection.usageRecordIds)
        : null;

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const unresolvedCharges: Array<ITimeBasedCharge | IUsageBasedCharge> = [];
    const defaultTaxRegion = await this.getClientDefaultTaxRegionCode(clientId);

    const timeEntries = await this.knex("time_entries")
      .join("users", function () {
        this.on("time_entries.user_id", "=", "users.user_id").andOn(
          "users.tenant",
          "=",
          "time_entries.tenant",
        );
      })
      .leftJoin("service_catalog", function () {
        this.on(
          "service_catalog.service_id",
          "=",
          "time_entries.service_id",
        ).andOn("service_catalog.tenant", "=", "time_entries.tenant");
      })
      .leftJoin("project_ticket_links", function () {
        this.on(
          "time_entries.work_item_id",
          "=",
          "project_ticket_links.ticket_id",
        ).andOn("project_ticket_links.tenant", "=", "time_entries.tenant");
      })
      .leftJoin("project_tasks", function () {
        this.on(
          "time_entries.work_item_id",
          "=",
          "project_tasks.task_id",
        ).andOn("project_tasks.tenant", "=", "time_entries.tenant");
      })
      .leftJoin("project_phases", function () {
        this.on("project_tasks.phase_id", "=", "project_phases.phase_id").andOn(
          "project_phases.tenant",
          "=",
          "project_tasks.tenant",
        );
      })
      .leftJoin("projects", function () {
        this.on("project_phases.project_id", "=", "projects.project_id").andOn(
          "projects.tenant",
          "=",
          "project_phases.tenant",
        );
      })
      .leftJoin("tickets", function () {
        this.on("time_entries.work_item_id", "=", "tickets.ticket_id").andOn(
          "tickets.tenant",
          "=",
          "time_entries.tenant",
        );
      })
      .where({
        "time_entries.tenant": this.tenant,
      })
      .where("time_entries.start_time", ">=", billingPeriod.startDate)
      .where("time_entries.end_time", "<", billingPeriod.endDate)
      .where("time_entries.invoiced", false)
      .whereNull("time_entries.contract_line_id")
      .whereNotNull("time_entries.service_id")
      .where("time_entries.approval_status", "APPROVED")
      .where(function (this: Knex.QueryBuilder) {
        this.where("projects.client_id", clientId).orWhere(
          "tickets.client_id",
          clientId,
        );
      })
      .select(
        "time_entries.*",
        "service_catalog.service_name",
        "service_catalog.default_rate",
        "service_catalog.tax_rate_id",
      );

    for (const entry of timeEntries) {
      if (selectedTimeEntryIds && !selectedTimeEntryIds.has(entry.entry_id)) {
        continue;
      }
      if (!entry.service_id) {
        continue;
      }
      const workDate = toISODate(toPlainDate(entry.start_time));
      const eligibleLineIds = await this.getEligibleContractLineIdsForServiceAtDate({
        clientId,
        serviceId: entry.service_id,
        workDate,
      });
      if (eligibleLineIds.length === 1) {
        const updatedCount = await this.knex("time_entries")
          .where({
            tenant: this.tenant,
            entry_id: entry.entry_id,
          })
          .whereNull("contract_line_id")
          .update({
            contract_line_id: eligibleLineIds[0],
            updated_at: this.knex.fn.now(),
          });
        console.info("[billing_engine.reconcile.unresolved]", {
          event: "billing_engine.reconcile.unresolved",
          recordType: "time_entry",
          tenant: this.tenant,
          clientId,
          recordId: entry.entry_id,
          decision: "deterministic_single_match",
          selectedContractLineId: eligibleLineIds[0],
          eligibleLineCount: eligibleLineIds.length,
          persisted: updatedCount > 0,
          metric: { name: "unmatched_resolved_deterministically", value: 1 },
        });
        continue;
      }
      console.info("[billing_engine.reconcile.unresolved]", {
        event: "billing_engine.reconcile.unresolved",
        recordType: "time_entry",
        tenant: this.tenant,
        clientId,
        recordId: entry.entry_id,
        decision: eligibleLineIds.length > 1 ? "ambiguous" : "no_match",
        selectedContractLineId: null,
        eligibleLineCount: eligibleLineIds.length,
        persisted: false,
        metric:
          eligibleLineIds.length > 1
            ? { name: "unresolved_ambiguous_count", value: 1 }
            : undefined,
      });

      const startDateTime = Temporal.PlainDateTime.from(
        entry.start_time.toISOString().replace("Z", ""),
      );
      const endDateTime = Temporal.PlainDateTime.from(
        entry.end_time.toISOString().replace("Z", ""),
      );
      const durationMinutes = Math.max(
        1,
        startDateTime.until(endDateTime, {
          largestUnit: "minutes",
        }).minutes,
      );
      // Bill the actual elapsed hours. Unresolved (non-contract) entries have no
      // contract-line rounding config, so fall back to exact time rather than
      // Math.ceil to a whole hour, which overbilled every partial-hour entry.
      const duration = durationMinutes / 60;
      const rate = Math.ceil(entry.custom_rate ?? entry.default_rate ?? 0);
      const total = Math.round(duration * rate);
      const { taxRegion: serviceTaxRegion, isTaxable } =
        await this.getTaxInfoFromService({
          service_id: entry.service_id,
          tax_rate_id: entry.tax_rate_id,
        });

      let taxAmount = 0;
      let taxRate = 0;
      const effectiveTaxRegion = serviceTaxRegion ?? defaultTaxRegion ?? undefined;
      if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
        try {
          const taxServiceInstance = new TaxService();
          const taxResult = await taxServiceInstance.calculateTax(
            client.client_id,
            total,
            billingPeriod.endDate,
            effectiveTaxRegion,
            true,
            client.default_currency_code || "USD",
          );
          taxRate = taxResult.taxRate;
          taxAmount = taxResult.taxAmount;
        } catch (error) {
          console.error(
            `Error calculating tax for unresolved time entry ${entry.entry_id}:`,
            error,
          );
        }
      }

      unresolvedCharges.push({
        type: "time",
        serviceId: entry.service_id,
        serviceName: entry.service_name,
        userId: entry.user_id,
        duration,
        quantity: duration,
        rate,
        total,
        tax_amount: taxAmount,
        tax_rate: taxRate,
        tax_region: effectiveTaxRegion,
        entryId: entry.entry_id,
        is_taxable: isTaxable,
        servicePeriodStart: billingPeriod.startDate,
        servicePeriodEnd: billingPeriod.endDate,
        billingTiming: "arrears",
      } satisfies ITimeBasedCharge);
    }

    const usageRecords = await this.knex("usage_tracking")
      .leftJoin("service_catalog", function () {
        this.on(
          "service_catalog.service_id",
          "=",
          "usage_tracking.service_id",
        ).andOn("service_catalog.tenant", "=", "usage_tracking.tenant");
      })
      .where({
        "usage_tracking.client_id": clientId,
        "usage_tracking.tenant": this.tenant,
        "usage_tracking.invoiced": false,
      })
      .where("usage_tracking.usage_date", ">=", billingPeriod.startDate)
      .where("usage_tracking.usage_date", "<", billingPeriod.endDate)
      .whereNull("usage_tracking.contract_line_id")
      .whereNotNull("usage_tracking.service_id")
      .select(
        "usage_tracking.*",
        "service_catalog.service_name",
        "service_catalog.default_rate",
        "service_catalog.tax_rate_id",
      );

    for (const record of usageRecords) {
      if (selectedUsageRecordIds && !selectedUsageRecordIds.has(record.usage_id)) {
        continue;
      }
      if (!record.service_id) {
        continue;
      }
      const workDate = toISODate(toPlainDate(record.usage_date));
      const eligibleLineIds = await this.getEligibleContractLineIdsForServiceAtDate({
        clientId,
        serviceId: record.service_id,
        workDate,
      });
      if (eligibleLineIds.length === 1) {
        const updatedCount = await this.knex("usage_tracking")
          .where({
            tenant: this.tenant,
            usage_id: record.usage_id,
          })
          .whereNull("contract_line_id")
          .update({
            contract_line_id: eligibleLineIds[0],
            updated_at: this.knex.fn.now(),
          });
        console.info("[billing_engine.reconcile.unresolved]", {
          event: "billing_engine.reconcile.unresolved",
          recordType: "usage_record",
          tenant: this.tenant,
          clientId,
          recordId: record.usage_id,
          decision: "deterministic_single_match",
          selectedContractLineId: eligibleLineIds[0],
          eligibleLineCount: eligibleLineIds.length,
          persisted: updatedCount > 0,
          metric: { name: "unmatched_resolved_deterministically", value: 1 },
        });
        continue;
      }
      console.info("[billing_engine.reconcile.unresolved]", {
        event: "billing_engine.reconcile.unresolved",
        recordType: "usage_record",
        tenant: this.tenant,
        clientId,
        recordId: record.usage_id,
        decision: eligibleLineIds.length > 1 ? "ambiguous" : "no_match",
        selectedContractLineId: null,
        eligibleLineCount: eligibleLineIds.length,
        persisted: false,
        metric:
          eligibleLineIds.length > 1
            ? { name: "unresolved_ambiguous_count", value: 1 }
            : undefined,
      });

      const quantity = Math.max(0, Number(record.quantity ?? 0));
      const rate = Math.ceil(record.custom_rate ?? record.default_rate ?? 0);
      const total = Math.ceil(quantity * rate);
      const { taxRegion: serviceTaxRegion, isTaxable } =
        await this.getTaxInfoFromService({
          service_id: record.service_id,
          tax_rate_id: record.tax_rate_id,
        });

      let taxAmount = 0;
      let taxRate = 0;
      const effectiveTaxRegion = serviceTaxRegion ?? defaultTaxRegion ?? undefined;
      if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
        try {
          const taxServiceInstance = new TaxService();
          const taxResult = await taxServiceInstance.calculateTax(
            client.client_id,
            total,
            billingPeriod.endDate,
            effectiveTaxRegion,
            true,
            client.default_currency_code || "USD",
          );
          taxRate = taxResult.taxRate;
          taxAmount = taxResult.taxAmount;
        } catch (error) {
          console.error(
            `Error calculating tax for unresolved usage record ${record.usage_id}:`,
            error,
          );
        }
      }

      unresolvedCharges.push({
        type: "usage",
        serviceId: record.service_id,
        serviceName: record.service_name,
        quantity,
        rate,
        total,
        tax_amount: taxAmount,
        tax_rate: taxRate,
        tax_region: effectiveTaxRegion,
        usageId: record.usage_id,
        is_taxable: isTaxable,
        servicePeriodStart: billingPeriod.startDate,
        servicePeriodEnd: billingPeriod.endDate,
        billingTiming: "arrears",
      } satisfies IUsageBasedCharge);
    }

    return unresolvedCharges;
  }

  private async getClientContractLinesForBillingPeriod(
    clientId: string,
    billingPeriod: IBillingPeriod,
  ): Promise<IClientContractLine[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }
    const knex = this.knex;
    if (!knex) {
      throw new Error("Database connection not initialized");
    }

    const client = await knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    console.log(
      `[BillingEngine] Loading contract lines for client ${clientId}, period: ${billingPeriod.startDate} to ${billingPeriod.endDate}`,
    );

    // Query contract lines via client-owned client_contracts -> contracts -> contract_lines.
    // template_contract_id is provenance for draft/review flows only; live billing must
    // read the cloned lines from cc.contract_id.
    const clientContractLines = await knex("client_contracts as cc")
      .join("contracts as c", function () {
        this.on("c.contract_id", "=", "cc.contract_id").andOn(
          "c.tenant",
          "=",
          "cc.tenant",
        );
      })
      .join("contract_lines as cl", function () {
        this.on("cl.contract_id", "=", "c.contract_id").andOn(
          "cl.tenant",
          "=",
          "c.tenant",
        );
      })
      .where({
        "cc.client_id": clientId,
        "cc.is_active": true,
        "cc.tenant": this.tenant,
      })
      // [start, end) semantics: a contract starting exactly on period end is not active within the period.
      .where("cc.start_date", "<", billingPeriod.endDate)
      .where(function (this: any) {
        this.where("cc.end_date", ">=", billingPeriod.startDate).orWhereNull(
          "cc.end_date",
        );
      })
      .select(
        // Map to IClientContractLine interface
        "cl.contract_line_id as client_contract_line_id", // Use contract_line_id as the identifier
        "cc.client_id",
        "cl.contract_line_id",
        "cl.service_category",
        "cc.start_date",
        "cc.end_date",
        "cc.is_active",
        "cc.client_contract_id",
        "cc.template_contract_id",
        "c.contract_id",
        "c.contract_name",
        "c.is_system_managed_default",
        "c.currency_code",
        "cl.contract_line_name",
        "cl.contract_line_type",
        "cl.billing_frequency",
        "cl.billing_timing",
        "cl.cadence_owner",
        "cl.custom_rate",
        "cl.enable_proration",
        "cl.location_id",
        knex.raw("cc.tenant as tenant"),
      );

    console.log(
      `[BillingEngine] Found ${clientContractLines.length} contract lines for client ${clientId}:`,
      JSON.stringify(clientContractLines, null, 2),
    );

    // Convert dates from the DB into plain ISO strings and normalize values
    clientContractLines.forEach((plan: any) => {
      plan.start_date = toISODate(toPlainDate(plan.start_date));
      plan.end_date = plan.end_date
        ? toISODate(toPlainDate(plan.end_date))
        : null;

      // Normalize billing_timing default
      plan.billing_timing = (plan.billing_timing ?? "arrears") as
        | "arrears"
        | "advance";
      plan.cadence_owner = resolveCadenceOwner(plan.cadence_owner);
      plan.is_system_managed_default = plan.is_system_managed_default === true;

      // custom_rate is already stored in cents in the database, just parse it
      if (plan.custom_rate !== null && plan.custom_rate !== undefined) {
        const parsedRate =
          typeof plan.custom_rate === "string"
            ? parseFloat(plan.custom_rate)
            : Number(plan.custom_rate);
        plan.custom_rate = Number.isFinite(parsedRate)
          ? Math.round(parsedRate)
          : null;
      }

      // Set defaults for recurring coverage settlement.
      plan.enable_proration = plan.enable_proration ?? false;
    });

    return clientContractLines;
  }

  private async getClientContractLinesAndCycle(
    clientId: string,
    billingPeriod: IBillingPeriod,
  ): Promise<{
    clientContractLines: IClientContractLine[];
    billingCycle: string;
  }> {
    const clientContractLines = await this.getClientContractLinesForBillingPeriod(
      clientId,
      billingPeriod,
    );
    const billingCycle = await this.getBillingCycle(
      clientId,
      billingPeriod.startDate,
    );

    return { clientContractLines, billingCycle };
  }

  private async getBillingCycle(
    clientId: string,
    date: ISO8601String = toISODate(Temporal.Now.plainDateISO()),
  ): Promise<string> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const result = (await this.knex("client_billing_cycles")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .where("effective_date", "<=", date)
      .orderBy("effective_date", "desc")
      .first()) as IClientContractLineCycle | undefined;

    if (!result) {
      // Check again for existing cycle to handle race conditions
      const existingCycle = await this.knex("client_billing_cycles")
        .where({
          client_id: clientId,
          tenant: this.tenant,
        })
        .first();

      if (existingCycle) {
        return existingCycle.billing_cycle;
      }

      try {
        const defaultCycle: Partial<IClientContractLineCycle> = {
          client_id: clientId,
          billing_cycle: "monthly",
          effective_date: "2023-01-01T00:00:00Z",
          tenant: this.tenant,
        };

        await this.knex("client_billing_cycles").insert(defaultCycle);
      } catch (error) {
        // If insert fails due to race condition, get the existing record
        const cycle = await this.knex("client_billing_cycles")
          .where({
            client_id: clientId,
            tenant: this.tenant,
          })
          .first();

        if (!cycle) {
          throw new Error(
            `Failed to create or retrieve billing cycle for client ${clientId} in tenant ${this.tenant}`,
          );
        }

        return cycle.billing_cycle;
      }
      return "monthly" as BillingCycleType;
    }

    return result.billing_cycle as BillingCycleType;
  }

  private async validateBillingPeriod(
    clientId: string,
    startDate: ISO8601String,
    endDate: ISO8601String,
  ): Promise<{ success: boolean; error?: string }> {
    try {
      await this.initKnex();
      if (!this.tenant) {
        return {
          success: false,
          error: "tenant context not found",
        };
      }

      const client = await this.knex("clients")
        .where({
          client_id: clientId,
          tenant: this.tenant,
        })
        .first();
      if (!client) {
        return {
          success: false,
          error: `Client ${clientId} not found in tenant ${this.tenant}`,
        };
      }

      const cycles = await this.knex("client_billing_cycles")
        .where({
          client_id: clientId,
          tenant: this.tenant,
        })
        // [start, end) semantics: a cycle starting exactly on endDate is not inside the period.
        .where("effective_date", "<", endDate)
        .orderBy("effective_date", "asc");

      let currentCycle = null;
      for (const cycle of cycles) {
        const cycleDate = toPlainDate(cycle.effective_date);
        const start = toPlainDate(startDate);
        const end = toPlainDate(endDate);
        if (Temporal.PlainDate.compare(cycleDate, start) <= 0) {
          currentCycle = cycle;
        } else if (
          Temporal.PlainDate.compare(cycleDate, start) > 0 &&
          Temporal.PlainDate.compare(cycleDate, end) < 0
        ) {
          return {
            success: false,
            error: "Invoice period cannot span billing cycle change",
          };
        }
      }

      if (!currentCycle) {
        // If no cycle found, create default monthly cycle
        await this.getBillingCycle(clientId, startDate);
      }

      return { success: true };
    } catch (err) {
      return {
        success: false,
        error:
          err instanceof Error
            ? err.message
            : "Failed to validate billing period",
      };
    }
  }
  private async calculateFixedPriceCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): Promise<IFixedPriceCharge[]> {
    // Note: Fixed plan rates are stored as dollars (decimal) in the database,
    // but need to be converted to cents (integer) for consistency with other monetary values in the system.
    // Custom contract-level rates are assumed to be in cents already.
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const resolvedBillingCycle =
      billingCycle ??
      (!recurringTimingSelection &&
      recurringTimingSelectionSource !== "persisted"
        ? await this.getBillingCycle(clientId, billingPeriod.startDate)
        : undefined);

    const timingResolution = this.resolveFixedRecurringChargeTiming(
      billingPeriod,
      clientContractLine,
      resolvedBillingCycle,
      recurringTimingSelection,
      recurringTimingSelectionSource,
    );
    if (!timingResolution) {
      return [];
    }

    const {
      duePosition: lineBillingTiming,
      servicePeriodStart,
      servicePeriodEnd,
      servicePeriodStartExclusive,
      servicePeriodEndExclusive,
      coverageRatio,
    } = timingResolution;
    let fixedProrationEnabled = false;

    // --- Custom Rate Check (Contracts & Pricing Schedules) ---
    // Check if a custom rate is defined for this plan assignment (provided via contract association)
    // or if an active pricing schedule overrides the rate for this billing period.
    // Pricing schedules take precedence over contract-level custom rates.
    // Ensure custom_rate is not null and not undefined before using it.

    let effectiveCustomRate = clientContractLine.custom_rate;
    let generatedCharges: IFixedPriceCharge[] | null = null;
    let generatedChargeAmountsUseCoverage = false;

    // Check for an active pricing schedule that overlaps the due service period.
    if (clientContractLine.contract_id) {
      try {
        const activePricingSchedule = await this.knex(
          "contract_pricing_schedules",
        )
          .where({
            tenant: this.tenant,
            contract_id: clientContractLine.contract_id,
          })
          // [start, end) semantics: schedule starting exactly on service-period end does not apply.
          .where("effective_date", "<", servicePeriodEndExclusive)
          .where(function (builder) {
            builder
              .whereNull("end_date")
              .orWhere("end_date", ">", servicePeriodStartExclusive);
          })
          .orderBy("effective_date", "desc")
          .first();

        if (
          activePricingSchedule &&
          activePricingSchedule.custom_rate !== null &&
          activePricingSchedule.custom_rate !== undefined
        ) {
          effectiveCustomRate = activePricingSchedule.custom_rate;
          console.log(
            `[PRICING_SCHEDULE] Using pricing schedule rate ${activePricingSchedule.custom_rate} cents for contract ${clientContractLine.contract_id} during service period ${servicePeriodStart} to ${servicePeriodEnd}. Schedule ID: ${activePricingSchedule.schedule_id}`,
          );
        }
      } catch (error) {
        console.warn(
          `[PRICING_SCHEDULE] Error checking for active pricing schedule for contract ${clientContractLine.contract_id}:`,
          error,
        );
      }
    }

    if (!generatedCharges) {
      // If no custom rate, proceed with calculating based on individual services or plan's fixed rate
      console.log(
        `No custom rate found for plan ${clientContractLine.contract_line_name} (ID: ${clientContractLine.contract_line_id}). Calculating based on services/plan rate.`,
      );
      const client = (await this.knex("clients")
        .where({
          client_id: clientId,
          tenant: this.tenant,
        })
        .first()) as IClient;

      if (!client) {
        throw new Error(
          `Client ${clientId} not found in tenant ${this.tenant}`,
        );
      }

      // Removed old logic fetching tax region via client_tax_settings (Phase 1.2)

      const tenant = this.tenant; // Capture tenant value for joins

      // Get the contract line details to determine if this is a fixed fee plan
      const contractLineDetails = await this.knex("contract_lines")
        .where({
          contract_line_id: clientContractLine.contract_line_id,
          tenant: client.tenant,
        })
        .first();

      const isFixedFeePlan =
        contractLineDetails?.contract_line_type === "Fixed";

      // --- Fetch Plan-Level Fixed Config (Base Rate and coverage settlement) ---
      let planLevelBaseRate: number | null = null; // Store plan base rate in dollars
      let planLevelEnableProration = false;

      if (isFixedFeePlan && contractLineDetails) {
        if (
          contractLineDetails.custom_rate !== undefined &&
          contractLineDetails.custom_rate !== null
        ) {
          const parsedContractRate =
            typeof contractLineDetails.custom_rate === "string"
              ? parseFloat(contractLineDetails.custom_rate)
              : Number(contractLineDetails.custom_rate);
          if (!Number.isNaN(parsedContractRate)) {
            // custom_rate is stored in cents, convert to dollars for planLevelBaseRate
            planLevelBaseRate = parsedContractRate / 100;
            console.log(
              `[BILLING DEBUG] custom_rate from DB: ${contractLineDetails.custom_rate}, parsedContractRate: ${parsedContractRate}, planLevelBaseRate (dollars): ${planLevelBaseRate}`,
            );
          }
        }

        if (
          contractLineDetails.enable_proration !== undefined &&
          contractLineDetails.enable_proration !== null
        ) {
          planLevelEnableProration = Boolean(
            contractLineDetails.enable_proration,
          );
        }
        fixedProrationEnabled = planLevelEnableProration;
      }

      if (isFixedFeePlan) {
        // Use the contract line's custom_rate directly
        // (client_contract_line_pricing table has been deprecated)
        if (
          planLevelBaseRate === null &&
          clientContractLine.custom_rate != null
        ) {
          const parsedAssignmentRate =
            typeof clientContractLine.custom_rate === "string"
              ? parseFloat(clientContractLine.custom_rate)
              : Number(clientContractLine.custom_rate);
          if (!Number.isNaN(parsedAssignmentRate)) {
            // custom_rate is stored in cents, convert to dollars for planLevelBaseRate
            planLevelBaseRate = parsedAssignmentRate / 100;
          }
        }
      }

      // Query services from contract_line_services (the contract definition)
      // Note: client_contract_line_id is actually a contract_line_id value (see getClientContractLinesAndCycle)
      const planServices = await this.knex("contract_line_services as cls")
        .join("contract_line_service_configuration as clsc", function () {
          this.on("clsc.contract_line_id", "=", "cls.contract_line_id")
            .andOn("clsc.service_id", "=", "cls.service_id")
            .andOn("clsc.tenant", "=", "cls.tenant");
        })
        .leftJoin("contract_line_service_fixed_config as clsfc", function () {
          this.on("clsfc.config_id", "=", "clsc.config_id").andOn(
            "clsfc.tenant",
            "=",
            "clsc.tenant",
          );
        })
        .join("service_catalog as sc", function () {
          this.on("sc.service_id", "=", "cls.service_id").andOn(
            "sc.tenant",
            "=",
            "cls.tenant",
          );
        })
        .where({
          "cls.contract_line_id": clientContractLine.client_contract_line_id,
          "cls.tenant": tenant,
          "clsc.configuration_type": "Fixed",
        })
        .whereNot("sc.item_kind", "product")
        .select(
          "sc.service_id",
          "sc.service_name",
          "sc.default_rate",
          "sc.tax_rate_id",
          "cls.quantity as service_quantity",
          "cls.custom_rate as service_line_custom_rate",
          "clsc.quantity as configuration_quantity",
          "clsc.custom_rate as configuration_custom_rate",
          "clsc.config_id",
          "clsfc.base_rate as service_base_rate",
          // Note: enable_proration is on contract_lines, not service config
        );

      const normalizedPlanServices = planServices.map((service: any) => {
        const quantityValue =
          service.configuration_quantity ??
          service.service_quantity ??
          service.quantity ??
          1;
        return {
          ...service,
          quantity: Number(quantityValue ?? 1) || 1,
        };
      });

      if (!planLevelEnableProration) {
        const prorationFromService = planServices.find(
          (service: any) => service.enable_proration,
        );
        if (prorationFromService?.enable_proration) {
          planLevelEnableProration = Boolean(
            prorationFromService.enable_proration,
          );
        }
      }
      fixedProrationEnabled = planLevelEnableProration;

      if (
        isFixedFeePlan &&
        (planLevelBaseRate === null || Number.isNaN(planLevelBaseRate))
      ) {
        let derivedBaseRate = 0;
        let hasServiceBaseRate = false;

        for (const service of planServices) {
          const rawServiceBaseRate = service.service_base_rate;
          if (rawServiceBaseRate !== null && rawServiceBaseRate !== undefined) {
            const parsedServiceBaseRate =
              typeof rawServiceBaseRate === "string"
                ? parseFloat(rawServiceBaseRate)
                : Number(rawServiceBaseRate);
            if (!Number.isNaN(parsedServiceBaseRate)) {
              const quantity =
                Number(
                  service.configuration_quantity ??
                    service.service_quantity ??
                    1,
                ) || 1;
              derivedBaseRate += parsedServiceBaseRate * quantity;
              hasServiceBaseRate = true;
            }
          }
        }

        if (hasServiceBaseRate) {
          // service_base_rate is stored in cents, convert to dollars for planLevelBaseRate
          planLevelBaseRate = derivedBaseRate / 100;
          console.log(
            `[DEBUG] Contract Line ${clientContractLine.contract_line_id} - Derived plan base rate from service configs: ${planLevelBaseRate} (converted from ${derivedBaseRate} cents)`,
          );
        }
      }

      if (
        isFixedFeePlan &&
        (planLevelBaseRate === null || Number.isNaN(planLevelBaseRate))
      ) {
        const totalDefaultRateCents = planServices.reduce(
          (sum: number, service: any) => {
            const rate = Number(service.default_rate ?? 0);
            const quantity =
              Number(
                service.configuration_quantity ?? service.service_quantity ?? 1,
              ) || 1;
            return sum + rate * quantity;
          },
          0,
        );
        if (totalDefaultRateCents !== 0) {
          planLevelBaseRate = totalDefaultRateCents / 100;
          console.log(
            `[DEBUG] Contract Line ${clientContractLine.contract_line_id} - Derived plan base rate from service default rates: ${planLevelBaseRate}`,
          );
        }
      }

      if (
        isFixedFeePlan &&
        (planLevelBaseRate === null || Number.isNaN(planLevelBaseRate))
      ) {
        console.error(
          `[DEBUG] Unable to determine base_rate for contract line ${clientContractLine.contract_line_id}.`,
        );
        return [];
      }

      const planLevelBaseRateCents =
        planLevelBaseRate !== null && !Number.isNaN(planLevelBaseRate)
          ? Math.round(planLevelBaseRate * 100)
          : null;
      console.log(
        `[BILLING DEBUG] planLevelBaseRate (dollars): ${planLevelBaseRate}, planLevelBaseRateCents: ${planLevelBaseRateCents}`,
      );

      const hasCustomRateOverride =
        effectiveCustomRate !== null &&
        effectiveCustomRate !== undefined &&
        (planLevelBaseRateCents === null ||
          Math.round(Number(effectiveCustomRate)) !== planLevelBaseRateCents);

      if (planServices.length === 0) {
        if (!isFixedFeePlan || planLevelBaseRateCents === null) {
          return [];
        }

        const baseRateInCents = hasCustomRateOverride
          ? Math.round(Number(effectiveCustomRate))
          : planLevelBaseRateCents;
        // config_id lives on contract_line_service_configuration, not on
        // contract_line_services; selecting it from cls_fallback raised
        // "column cls_fallback.config_id does not exist" and aborted billing
        // for any fixed line whose catalog items are all products/licenses.
        const fallbackService = await this.knex(
          "contract_line_services as cls_fallback",
        )
          .join("contract_line_service_configuration as clsc_fallback", function () {
            this.on(
              "clsc_fallback.contract_line_id",
              "=",
              "cls_fallback.contract_line_id",
            )
              .andOn("clsc_fallback.service_id", "=", "cls_fallback.service_id")
              .andOn("clsc_fallback.tenant", "=", "cls_fallback.tenant");
          })
          .join("service_catalog as sc", function () {
            this.on("sc.service_id", "=", "cls_fallback.service_id").andOn(
              "sc.tenant",
              "=",
              "cls_fallback.tenant",
            );
          })
          .where({
            "cls_fallback.contract_line_id":
              clientContractLine.client_contract_line_id,
            "cls_fallback.tenant": tenant,
          })
          .whereNot("sc.item_kind", "product")
          .orderBy("sc.service_id", "asc")
          .first(
            "sc.service_id",
            "sc.service_name",
            "sc.tax_rate_id",
            "clsc_fallback.config_id",
          );

        if (!fallbackService?.service_id || !fallbackService?.config_id) {
          return [];
        }

        const { taxRegion: fallbackServiceTaxRegion, isTaxable: fallbackIsTaxable } =
          await this.getTaxInfoFromService(fallbackService);
        const fallbackTaxRegion =
          fallbackServiceTaxRegion ??
          (await this.getLocationTaxRegionCode(clientContractLine.location_id)) ??
          (await this.getClientDefaultTaxRegionCode(client.client_id));
        let fallbackTaxAmount = 0;
        let fallbackTaxRate = 0;
        if (!client.is_tax_exempt && fallbackIsTaxable && fallbackTaxRegion) {
          const taxServiceInstance = new TaxService();
          const taxResult = await taxServiceInstance.calculateTax(
            client.client_id,
            baseRateInCents,
            servicePeriodEnd,
            fallbackTaxRegion,
            true,
            clientContractLine.currency_code || "USD",
          );
          fallbackTaxRate = taxResult.taxRate;
          fallbackTaxAmount = taxResult.taxAmount;
        }

        generatedCharges = [
          {
            type: "fixed",
            serviceId: fallbackService.service_id,
            config_id: fallbackService.config_id,
            serviceName:
              fallbackService.service_name ||
              clientContractLine.contract_line_name ||
              "Fixed Plan Charge",
            quantity: 1,
            rate: baseRateInCents,
            total: baseRateInCents,
            tax_amount: fallbackTaxAmount,
            tax_rate: fallbackTaxRate,
            tax_region: fallbackTaxRegion ?? undefined,
            is_taxable: fallbackIsTaxable,
            client_contract_line_id: clientContractLine.client_contract_line_id,
            client_contract_id:
              clientContractLine.client_contract_id || undefined,
            contract_name: clientContractLine.contract_name || undefined,
            location_id: clientContractLine.location_id ?? null,
            base_rate: baseRateInCents,
            enable_proration: planLevelEnableProration,
            fmv: baseRateInCents,
            proportion: 1,
            allocated_amount: baseRateInCents,
          },
        ];
      }

      if (!generatedCharges && isFixedFeePlan) {
        // For fixed fee plans, we want to create a single consolidated charge
        // but internally allocate the tax based on FMV of each service

        const baseRateInCents = hasCustomRateOverride
          ? Math.round(Number(effectiveCustomRate))
          : Math.round(planLevelBaseRate! * 100);
        const baseRate = baseRateInCents / 100;

        if (hasCustomRateOverride) {
          console.log(
            `Using custom rate ${baseRateInCents} cents for plan ${clientContractLine.contract_line_name} (ID: ${clientContractLine.contract_line_id}) from contract ${clientContractLine.contract_name || "N/A"}`,
          );
        }

        console.log(
          `[DEBUG] Plan ${clientContractLine.contract_line_id} - Using Plan Level Base Rate: ${baseRate}`,
        );

        // Calculate the total FMV (Fair Market Value) of all services
        // Calculate the total FMV (Fair Market Value) of all services in CENTS
        const totalFMVCents = normalizedPlanServices.reduce((sum, service) => {
          const serviceFMV =
            Number(service.default_rate ?? 0) * service.quantity;
          return sum + serviceFMV;
        }, 0);
        console.log(
          `[DEBUG] Plan ${clientContractLine.contract_line_id} - Calculated totalFMVCents: ${totalFMVCents}`,
        ); // DEBUG LOG

        // If totalFMVCents is exactly zero, we can't allocate properly
        // Note: Negative FMV is valid for credit-generating services and should be processed
        if (totalFMVCents === 0) {
          console.log(
            `Total FMV (cents) for services in plan ${clientContractLine.contract_line_id} is zero`,
          );
          return [];
        }

        // Calculate tax for each service based on its proportion of the total FMV
        let totalTaxAmount = 0;
        let totalTaxableAmount = 0;
        let totalNonTaxableAmount = 0;

        // For detailed tax calculation and audit purposes

        // Instantiate TaxService
        const taxServiceInstance = new TaxService(); // Corrected instantiation

        const serviceAllocations = await Promise.all(
          normalizedPlanServices.map(async (service) => {
            // Calculate the FMV for this service in CENTS
            // Use custom_rate from plan config if available (assume dollars), otherwise fallback to service default_rate (assume cents).
            console.log(
              "[DEBUG] Processing service object:",
              JSON.stringify(service, null, 2),
            ); // DEBUG LOG - Inspect the service object
            // FMV should always be based on the service's default rate, not plan overrides.
            // Assume service.default_rate is stored in cents.
            const rateForFMV = Number(service.default_rate || 0); // Ensure it's a number, default to 0 if null/undefined
            const serviceFMVCents = Math.round(rateForFMV * service.quantity); // FMV is now correctly in cents
            console.log(
              `[DEBUG] Service ${service.service_id} - Calculated serviceFMVCents: ${serviceFMVCents} (Rate: ${rateForFMV}, Qty: ${service.quantity})`,
            ); // DEBUG LOG

            // Calculate the proportion of the total fixed fee that should be allocated to this service
            const proportion =
              totalFMVCents !== 0 ? serviceFMVCents / totalFMVCents : 0; // Use totalFMVCents and handle division by zero
            console.log(
              `[DEBUG] Service ${service.service_id} - Calculated proportion: ${proportion} (${serviceFMVCents} / ${totalFMVCents})`,
            ); // DEBUG LOG

            // --- Proration Calculation ---
            let prorationFactor = 1.0;
            let effectiveBaseRateInCents = baseRateInCents; // Start with full rate in cents

            // Use the plan-level partial-period setting fetched earlier for fixed plans
            if (planLevelEnableProration) {
              prorationFactor = coverageRatio;
              effectiveBaseRateInCents = Math.round(
                effectiveBaseRateInCents * prorationFactor,
              );
              console.log(
                `[DEBUG] Service ${service.service_id} - Proration Enabled. Factor: ${prorationFactor.toFixed(4)}, Prorated Base (cents): ${effectiveBaseRateInCents}`,
              );
            } else {
              console.log(
                `[DEBUG] Service ${service.service_id} - Proration Disabled.`,
              );
            }
            // --- End Proration Calculation ---

            // Allocate a portion of the (potentially prorated) fixed fee to this service
            const allocatedAmount = Math.round(
              effectiveBaseRateInCents * proportion,
            ); // Use effective rate, round final cents value
            console.log(
              `[DEBUG] Service ${service.service_id} - Calculated allocatedAmount (cents): ${allocatedAmount} (Effective Base: ${effectiveBaseRateInCents}, Prop: ${proportion})`,
            ); // DEBUG LOG

            // Determine tax info using the helper function
            const { taxRegion: serviceTaxRegion, isTaxable } =
              await this.getTaxInfoFromService(service);

            // Calculate tax if applicable
            let taxAmount = 0;
            let taxRate = 0;

            // ***** START OF CORRECTED BLOCK *****
            if (!client.is_tax_exempt && isTaxable) {
              // Use the region derived from tax_rate_id, fallback to location then client default
              const effectiveTaxRegion =
                serviceTaxRegion ??
                (await this.getLocationTaxRegionCode(clientContractLine.location_id)) ??
                (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
                "";
              if (effectiveTaxRegion) {
                // Use TaxService to calculate tax
                // allocatedAmount is already in cents
                const taxResult = await taxServiceInstance.calculateTax(
                  client.client_id,
                  allocatedAmount,
                  servicePeriodEnd,
                  effectiveTaxRegion,
                  true,
                  clientContractLine.currency_code || "USD",
                );
                taxRate = taxResult.taxRate;
                taxAmount = taxResult.taxAmount;
                console.log(
                  `[DEBUG] Service ${service.service_id} - Tax calculated (TaxService): Rate=${taxRate}, Amount=${taxAmount}, Base=${allocatedAmount}, Region=${effectiveTaxRegion}`,
                ); // DEBUG LOG
              } else {
                // No region from service's tax_rate_id AND no client default region.
                console.warn(
                  `[BillingEngine] No tax region found (from service tax_rate_id or client default via getClientDefaultTaxRegionCode) for service ${service.service_id} / client ${clientId}. Using zero tax rate.`,
                );
                taxRate = 0;
                taxAmount = 0;
                console.log(
                  `[DEBUG] Service ${service.service_id} - Tax calculation skipped (No effective region found)`,
                ); // DEBUG LOG
              }
              // Add the pre-tax allocated amount to the total taxable amount
              totalTaxableAmount += allocatedAmount;
            } else {
              // Add to the total non-taxable amount
              totalNonTaxableAmount += allocatedAmount;
              console.log(
                `[DEBUG] Service ${service.service_id} - Tax calculation skipped (Client exempt: ${client.is_tax_exempt} or service not taxable: ${!isTaxable})`,
              ); // DEBUG LOG
            }
            // ***** END OF CORRECTED BLOCK *****

            // Add to the total tax amount
            totalTaxAmount += taxAmount;

            return {
              serviceId: service.service_id,
              serviceName: service.service_name,
              fmv: serviceFMVCents, // Store FMV in cents
              proportion,
              allocatedAmount,
              isTaxable: isTaxable, // Use derived value
              taxRate: taxRate,
              taxAmount, // This is the final calculated tax for this allocation
            };
          }),
        );

        // Log the detailed allocation for audit purposes
        console.log(
          `Fixed fee plan ${clientContractLine.contract_line_id} tax allocation:`,
          {
            baseRate: baseRate, // Dollar amount from database
            baseRateInCents: baseRate * 100, // Converted to cents for calculations
            totalFMVCents,
            totalTaxableAmount,
            totalNonTaxableAmount,
            totalTaxAmount,
            serviceAllocations,
          },
        );

        // Create an array to hold the detailed charges
        const detailedCharges: IFixedPriceCharge[] = [];

        // Iterate through the service allocations and create a detailed charge for each
        for (const allocation of serviceAllocations) {
          // Find the corresponding planService data
          const planService = normalizedPlanServices.find(
            (ps) => ps.service_id === allocation.serviceId,
          );

          if (!planService) {
            console.warn(
              `Could not find planService data for serviceId: ${allocation.serviceId} in plan ${clientContractLine.contract_line_id}`,
            );
            continue; // Skip this allocation if data is missing
          }

          const quantity = Number(planService.quantity ?? 1) || 1;

          const detailedCharge: IFixedPriceCharge = {
            // Common IBillingCharge fields
            type: "fixed",
            serviceId: allocation.serviceId,
            serviceName: allocation.serviceName,
            quantity, // Default to 1 if quantity is null
            rate: allocation.allocatedAmount, // Rate is the PRORATED allocated amount in cents
            total: allocation.allocatedAmount, // Total is the PRORATED allocated amount in cents
            tax_amount: allocation.taxAmount, // Per-allocation tax in cents
            tax_rate: allocation.taxRate, // Per-allocation tax rate
            is_taxable: allocation.isTaxable, // Use the derived isTaxable from the allocation object
            // Determine effective region for the charge record
            // We need the taxRegion derived from the service's tax_rate_id for this specific allocation
            // Let's re-fetch it here for clarity, although it was calculated during allocation.
            // Ideally, the allocation object would carry the derived taxRegion.
            // For now, re-derive with the location fallback chain:
            tax_region:
              (await this.getTaxInfoFromService(planService)).taxRegion ??
              (await this.getLocationTaxRegionCode(clientContractLine.location_id)) ??
              (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
              undefined, // Use derived region, fallback to location then client default
            // planId: clientContractLine.contract_line_id, // Removed - planId not part of IFixedPriceCharge
            client_contract_line_id: clientContractLine.client_contract_line_id, // Link back to the plan assignment

            // Add contract association information for all fixed charges when the plan is covered by a contract assignment
            client_contract_id:
              clientContractLine.client_contract_id || undefined,
            contract_name: clientContractLine.contract_name || undefined,
            location_id: clientContractLine.location_id ?? null,

            // IFixedPriceCharge specific fields (newly added)
            config_id: planService.config_id, // From the modified query
            base_rate: baseRateInCents, // Use the PLAN-LEVEL base rate in cents used for allocation
            enable_proration: planLevelEnableProration, // Use plan-level setting
            fmv: allocation.fmv, // Use FMV directly from allocation (already in cents)
            proportion: allocation.proportion, // Numeric proportion
            allocated_amount: allocation.allocatedAmount, // PRORATED allocated amount in cents

            // taxAllocationDetails: undefined, // Remove this property as details are now fields
          };
          detailedCharges.push(detailedCharge);
        }

        console.log(
          `Detailed fixed price charges for client ${clientId}, plan ${clientContractLine.contract_line_id}:`,
          detailedCharges,
        );
        generatedCharges = detailedCharges;
        generatedChargeAmountsUseCoverage = planLevelEnableProration;
      } else if (!generatedCharges) {
        // This block handles cases where the plan type isn't 'Fixed', but a service within it
        // is configured as 'Fixed'. This might be legacy or an edge case.
        // We should still use the plan-level coverage settings if the plan *was* fixed.
        // If the plan itself isn't fixed, proration likely doesn't apply anyway.
        // TODO: Review if this logic block is still necessary or correct after the refactor.
        console.warn(
          `[BillingEngine] Processing fixed service config for a non-fixed plan type (${contractLineDetails?.contract_line_type}) for plan ${clientContractLine.contract_line_id}. Review this logic.`,
        );

        const fixedCharges: IFixedPriceCharge[] = await Promise.all(
          normalizedPlanServices.map(
            async (service): Promise<IFixedPriceCharge> => {
              const quantity = service.quantity;

              const parsedBaseRate =
                service.service_base_rate !== null &&
                service.service_base_rate !== undefined
                  ? Number(service.service_base_rate)
                  : null;
              // service_base_rate is already stored in cents, no conversion needed
              const baseRateInCents =
                parsedBaseRate !== null && !Number.isNaN(parsedBaseRate)
                  ? Math.round(parsedBaseRate)
                  : Number(service.default_rate ?? 0);
              const total = baseRateInCents * quantity;

              // Determine tax info for this edge-case service
              const { taxRegion: serviceTaxRegion, isTaxable } =
                await this.getTaxInfoFromService(service);

              const charge: IFixedPriceCharge = {
                serviceId: service.service_id,
                serviceName: service.service_name,
                quantity,
                rate: baseRateInCents, // Rate in cents
                total, // Total in cents
                type: "fixed",
                client_contract_line_id: clientContractLine.client_contract_line_id,
                client_contract_id:
                  clientContractLine.client_contract_id || undefined,
                contract_name: clientContractLine.contract_name || undefined,
                location_id: clientContractLine.location_id ?? null,
                tax_amount: 0,
                tax_rate: 0,
                tax_region:
                  serviceTaxRegion ??
                  (await this.getLocationTaxRegionCode(
                    clientContractLine.location_id,
                  )) ??
                  (await this.getClientDefaultTaxRegionCode(
                    client.client_id,
                  )) ??
                  undefined, // Use derived region, fallback to location then client default
                is_taxable: isTaxable, // Use derived value
                // Use plan-level settings fetched earlier, even if plan type isn't strictly 'Fixed' now
                // This maintains consistency if a plan type was changed.
                enable_proration: planLevelEnableProration, // Use plan-level setting
                // Add other relevant fields from IFixedPriceCharge if needed
                config_id: service.config_id,
                base_rate: baseRateInCents,
                // FMV/Proportion/AllocatedAmount might not be relevant here if not a true fixed plan
              };
              // Recalculate tax based on derived info for this edge case
              if (!client.is_tax_exempt && charge.is_taxable) {
                const effectiveTaxRegion = charge.tax_region ?? ""; // Use the already set region (derived or client default fallback)
                if (effectiveTaxRegion) {
                  // Use TaxService instance if available, or instantiate if needed
                  const taxServiceInstance = new TaxService(); // Assuming it's okay to instantiate here
                  const taxResult = await taxServiceInstance.calculateTax(
                    client.client_id,
                    charge.total,
                    servicePeriodEnd,
                    effectiveTaxRegion,
                    true,
                    clientContractLine.currency_code || "USD",
                  );
                  charge.tax_rate = taxResult.taxRate;
                  charge.tax_amount = taxResult.taxAmount;
                } else {
                  console.warn(
                    `No effective tax region found for edge-case fixed service ${service.service_id}, using zero tax rate`,
                  );
                  charge.tax_rate = 0;
                  charge.tax_amount = 0;
                }
              } else {
                console.warn(
                  `No effective tax region found for edge-case fixed service ${service.service_id}, using zero tax rate`,
                );
                charge.tax_rate = 0;
                charge.tax_amount = 0;
              }

              return charge;
            },
          ),
        );

        console.log(
          `Fixed price charges for client ${clientId}:`,
          fixedCharges,
        );
        generatedCharges = fixedCharges;
      }
    }

    if (!generatedCharges || generatedCharges.length === 0) {
      return [];
    }

    const requiresAdvanceTerminationSettlement =
      this.shouldApplyAdvanceTerminationCoverageSettlement(
        clientContractLine,
        billingPeriod,
        lineBillingTiming,
        coverageRatio,
      );
    const chargesAfterSettlement =
      !generatedChargeAmountsUseCoverage &&
      (fixedProrationEnabled || requiresAdvanceTerminationSettlement)
        ? this.applyFixedChargeCoverageSettlement(
            generatedCharges,
            coverageRatio,
            requiresAdvanceTerminationSettlement
              ? "unused_credit_net"
              : "coverage_ratio",
          )
        : generatedCharges;

    const chargesWithMeta = chargesAfterSettlement.map((charge) => ({
      ...charge,
      servicePeriodStart,
      servicePeriodEnd,
      billingTiming: lineBillingTiming,
    }));

    let positiveCharges: IFixedPriceCharge[] = chargesWithMeta;

    if (lineBillingTiming === "advance") {
      const endedBeforeAdvance = clientContractLine.end_date
        ? Temporal.PlainDate.compare(
            toPlainDate(clientContractLine.end_date),
            toPlainDate(servicePeriodStart),
          ) < 0
        : false;

      let existingAdvance = false;
      if (!endedBeforeAdvance) {
        existingAdvance = await this.hasExistingServicePeriodCharge(
          clientContractLine.client_contract_line_id,
          servicePeriodStart,
          servicePeriodEnd,
          "advance",
        );
      }

      if (endedBeforeAdvance || existingAdvance) {
        console.log(
          `[BillingEngine] Skipping advance billing for contract line ${clientContractLine.contract_line_id}: ${endedBeforeAdvance ? "line ends before next period" : "charge already persisted for service period"}`,
        );
        positiveCharges = [];
      }
    }

    return positiveCharges;
  }

  private resolveFixedRecurringChargeTiming(
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): ResolvedRecurringChargeTiming | null {
    return this.resolveRecurringChargeTiming(
      billingPeriod,
      clientContractLine,
      billingCycle,
      recurringTimingSelection,
      recurringTimingSelectionSource,
    );
  }

  private resolveRecurringChargeTiming(
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): ResolvedRecurringChargeTiming | null {
    if (recurringTimingSelection) {
      return recurringTimingSelection;
    }
    if (recurringTimingSelectionSource === "persisted") {
      return null;
    }
    if (!billingCycle) {
      throw new Error(
        "Billing cycle source rule is required when deriving recurring timing outside persisted service periods",
      );
    }
    return this.buildRecurringChargeTimingSelection(
      billingPeriod,
      clientContractLine,
      billingCycle,
    );
  }

  private resolveServiceDrivenChargeTiming(
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle: string | undefined,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): ResolvedRecurringChargeTiming | null {
    if (recurringTimingSelection) {
      return recurringTimingSelection;
    }
    if (recurringTimingSelectionSource === "persisted") {
      return null;
    }
    if (!billingCycle) {
      throw new Error(
        "Billing cycle source rule is required when deriving recurring timing outside persisted service periods",
      );
    }

    return this.buildRecurringChargeTimingSelection(
      billingPeriod,
      clientContractLine,
      billingCycle,
    );
  }

  private buildRecurringTimingSelections(
    billingPeriod: IBillingPeriod,
    clientContractLines: IClientContractLine[],
    billingCycle: string,
  ): RecurringChargeTimingSelections {
    const recurringTimingSelections: RecurringChargeTimingSelections = {};

    for (const clientContractLine of [...clientContractLines].sort((left, right) =>
      left.client_contract_line_id.localeCompare(right.client_contract_line_id),
    )) {
      if (!this.isRecurringTimingEligibleContractLine(clientContractLine)) {
        continue;
      }
      const timingSelection = this.buildRecurringChargeTimingSelection(
        billingPeriod,
        clientContractLine,
        billingCycle,
      );
      if (!timingSelection) {
        continue;
      }
      recurringTimingSelections[clientContractLine.client_contract_line_id] =
        timingSelection;
    }

    return recurringTimingSelections;
  }

  private buildRecurringTimingSelectionsFromPersistedRecords(
    records: PersistedRecurringTimingSelectionRecord[],
  ): RecurringChargeTimingSelections {
    const recurringTimingSelections: RecurringChargeTimingSelections = {};

    for (const record of records) {
      const lineId = record.sourceObligation.obligationId;
      if (recurringTimingSelections[lineId]) {
        throw new Error(
          `${RECURRING_TIMING_ROLLOUT_GUARD_PREFIX}: ${lineId}: multiple persisted due periods matched one runtime obligation`,
        );
      }

      const coveragePeriod = record.activityWindow?.start && record.activityWindow?.end
        ? {
            start: record.activityWindow.start,
            end: record.activityWindow.end,
          }
        : {
            start: record.servicePeriod.start,
            end: record.servicePeriod.end,
          };

      const coverage = calculateServicePeriodCoverage(
        {
          kind: "service_period",
          cadenceOwner: record.cadenceOwner,
          duePosition: record.duePosition,
          sourceObligation: record.sourceObligation,
          start: record.servicePeriod.start,
          end: record.servicePeriod.end,
          semantics: RECURRING_RANGE_SEMANTICS,
        },
        coveragePeriod,
      );

      recurringTimingSelections[lineId] = {
        duePosition: record.duePosition,
        servicePeriodStart: toISODate(toPlainDate(coverage.coveredPeriod.start)),
        servicePeriodEnd: toISODate(
          toPlainDate(coverage.coveredPeriod.end).subtract({ days: 1 }),
        ),
        servicePeriodStartExclusive: toISODate(
          toPlainDate(coverage.coveredPeriod.start),
        ),
        servicePeriodEndExclusive: toISODate(
          toPlainDate(coverage.coveredPeriod.end),
        ),
        coverageRatio: coverage.coverageRatio,
      };
    }

    return recurringTimingSelections;
  }

  private assertPersistedRecurringTimingSelectionsReferenceEligibleLines(
    clientContractLines: IClientContractLine[],
    providedSelections: RecurringChargeTimingSelections,
  ): RecurringChargeTimingSelections {
    const eligibleLineIds = new Set(
      clientContractLines.map((line) => line.client_contract_line_id),
    );
    const unexpectedSelections = Object.keys(providedSelections)
      .filter((lineId) => !eligibleLineIds.has(lineId))
      .sort();

    if (unexpectedSelections.length > 0) {
      throw new Error(
        `${RECURRING_TIMING_ROLLOUT_GUARD_PREFIX}: ${unexpectedSelections
          .map((lineId) => `${lineId}: unexpected persisted selection`)
          .join("; ")}`,
      );
    }

    return providedSelections;
  }

  private assertRecurringTimingSelectionsMatchCanonical(
    canonicalSelections: RecurringChargeTimingSelections,
    providedSelections: RecurringChargeTimingSelections,
  ): RecurringChargeTimingSelections {
    const mismatches: string[] = [];
    const lineIds = Array.from(
      new Set([
        ...Object.keys(canonicalSelections),
        ...Object.keys(providedSelections),
      ]),
    ).sort();

    for (const lineId of lineIds) {
      const canonical = canonicalSelections[lineId];
      const provided = providedSelections[lineId];

      if (!canonical) {
        mismatches.push(`${lineId}: unexpected external selection`);
        continue;
      }

      if (!provided) {
        mismatches.push(`${lineId}: missing canonical selection`);
        continue;
      }

      if (!this.areRecurringChargeTimingsEquivalent(canonical, provided)) {
        mismatches.push(`${lineId}: selection diverged from canonical timing`);
      }
    }

    if (mismatches.length > 0) {
      throw new Error(
        `${RECURRING_TIMING_ROLLOUT_GUARD_PREFIX}: ${mismatches.join("; ")}`,
      );
    }

    return providedSelections;
  }

  private areRecurringChargeTimingsEquivalent(
    left: ResolvedRecurringChargeTiming,
    right: ResolvedRecurringChargeTiming,
  ): boolean {
    return (
      left.duePosition === right.duePosition &&
      left.servicePeriodStart === right.servicePeriodStart &&
      left.servicePeriodEnd === right.servicePeriodEnd &&
      left.servicePeriodStartExclusive === right.servicePeriodStartExclusive &&
      left.servicePeriodEndExclusive === right.servicePeriodEndExclusive &&
      Math.abs(left.coverageRatio - right.coverageRatio) < 1e-9
    );
  }

  private isRecurringTimingEligibleContractLine(
    clientContractLine: IClientContractLine & {
      contract_line_type?: "Fixed" | "Hourly" | "Usage" | string | null;
    },
  ): boolean {
    return (
      clientContractLine.contract_line_type !== "Hourly" &&
      clientContractLine.contract_line_type !== "Usage"
    );
  }

  private buildRecurringChargeTimingSelection(
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle: string,
  ): ResolvedRecurringChargeTiming | null {
    const duePosition = (clientContractLine.billing_timing ?? "arrears") as
      | "arrears"
      | "advance";
    const currentStart = toISODate(toPlainDate(billingPeriod.startDate));
    const currentEndExclusive = toISODate(toPlainDate(billingPeriod.endDate));
    const isSystemManagedDefault = (clientContractLine as { is_system_managed_default?: boolean | null })
      .is_system_managed_default === true;
    const cadenceOwner = isSystemManagedDefault
      ? "client"
      : resolveCadenceOwner(clientContractLine.cadence_owner);
    const sourceObligation = buildClientCadencePostDropObligationRef({
      contractLineId: clientContractLine.client_contract_line_id,
      chargeFamily: "fixed",
      tenant: this.tenant ?? undefined,
    });
    const activityWindow = {
      start: clientContractLine.start_date
        ? toISODate(toPlainDate(clientContractLine.start_date))
        : undefined,
      end: clientContractLine.end_date
        ? toISODate(toPlainDate(clientContractLine.end_date).add({ days: 1 }))
        : undefined,
      semantics: RECURRING_RANGE_SEMANTICS,
    };

    const settlements =
      cadenceOwner === "contract"
        ? this.resolveContractCadenceSettlementsForInvoiceWindow({
            billingPeriod,
            clientContractLine,
            duePosition,
            sourceObligation,
            activityWindow,
            invoiceWindow: {
              kind: "invoice_window",
              cadenceOwner: "contract",
              duePosition,
              start: currentStart,
              end: currentEndExclusive,
              semantics: RECURRING_RANGE_SEMANTICS,
            },
          })
        : resolveRecurringSettlementsForInvoiceWindow({
            servicePeriods: [
              {
                kind: "service_period",
                cadenceOwner,
                duePosition,
                sourceObligation,
                start: this.getPreviousRecurringBoundaryStart(
                  currentStart,
                  currentEndExclusive,
                  billingCycle,
                ),
                end: currentStart,
                semantics: RECURRING_RANGE_SEMANTICS,
              },
              {
                kind: "service_period",
                cadenceOwner,
                duePosition,
                sourceObligation,
                start: currentStart,
                end: currentEndExclusive,
                semantics: RECURRING_RANGE_SEMANTICS,
              },
            ],
            invoiceWindow: {
              kind: "invoice_window",
              cadenceOwner,
              duePosition,
              start: currentStart,
              end: currentEndExclusive,
              semantics: RECURRING_RANGE_SEMANTICS,
            },
            activityWindow,
            duePosition,
          });

    const settlement = settlements[0];
    if (!settlement) {
      return null;
    }

    return {
      duePosition,
      servicePeriodStart: toISODate(
        toPlainDate(settlement.coveredServicePeriod.start),
      ),
      servicePeriodEnd: toISODate(
        toPlainDate(settlement.coveredServicePeriod.end).subtract({ days: 1 }),
      ),
      servicePeriodStartExclusive: toISODate(
        toPlainDate(settlement.coveredServicePeriod.start),
      ),
      servicePeriodEndExclusive: toISODate(
        toPlainDate(settlement.coveredServicePeriod.end),
      ),
      coverageRatio: settlement.coverage.coverageRatio,
    };
  }

  private getPreviousRecurringBoundaryStart(
    currentStart: ISO8601String,
    currentEndExclusive: ISO8601String,
    billingCycle: string,
  ): ISO8601String {
    const currentStartDate = toPlainDate(currentStart);

    switch (billingCycle) {
      case "weekly":
        return toISODate(currentStartDate.subtract({ days: 7 }));
      case "bi-weekly":
        return toISODate(currentStartDate.subtract({ days: 14 }));
      case "monthly":
        return toISODate(currentStartDate.subtract({ months: 1 }));
      case "quarterly":
        return toISODate(currentStartDate.subtract({ months: 3 }));
      case "semi-annually":
        return toISODate(currentStartDate.subtract({ months: 6 }));
      case "annually":
        return toISODate(currentStartDate.subtract({ years: 1 }));
      default:
        return toISODate(
          currentStartDate.subtract({
            days: Math.max(
              toPlainDate(currentStart).until(
                toPlainDate(currentEndExclusive),
                { largestUnit: "days" },
              ).days,
              1,
            ),
          }),
        );
    }
  }

  private resolveContractCadenceSettlementsForInvoiceWindow(input: {
    billingPeriod: IBillingPeriod;
    clientContractLine: IClientContractLine;
    duePosition: "arrears" | "advance";
    sourceObligation: IRecurringObligationRef;
    activityWindow: {
      start?: ISO8601String;
      end?: ISO8601String;
      semantics: typeof RECURRING_RANGE_SEMANTICS;
    };
    invoiceWindow: {
      kind: "invoice_window";
      cadenceOwner: "contract";
      duePosition: "arrears" | "advance";
      start: ISO8601String;
      end: ISO8601String;
      semantics: typeof RECURRING_RANGE_SEMANTICS;
    };
  }) {
    const contractCadence = this.getContractCadenceDefinition(
      input.clientContractLine.billing_frequency,
    );

    if (!contractCadence) {
      throw new Error(
        `Unsupported contract cadence frequency "${input.clientContractLine.billing_frequency ?? "unknown"}" for contract line ${input.clientContractLine.client_contract_line_id}`,
      );
    }

    const anchorDate = resolveContractCadenceAnchorDate({
      assignmentStartDate: toISOTimestamp(
        toPlainDate(input.clientContractLine.start_date),
      ),
    });
    const rangeStart = toISOTimestamp(
      toPlainDate(input.billingPeriod.startDate).subtract({
        months: contractCadence.monthsPerPeriod,
      }),
    );
    const rangeEnd = toISOTimestamp(
      toPlainDate(input.billingPeriod.endDate).add({
        months: contractCadence.monthsPerPeriod,
      }),
    );
    const servicePeriods = contractCadence.generator({
      rangeStart,
      rangeEnd,
      sourceObligation: input.sourceObligation,
      duePosition: input.duePosition,
      anchorDate,
    }).filter((servicePeriod: IRecurringServicePeriod) => {
      const contractInvoiceWindow =
        resolveContractCadenceInvoiceWindowForServicePeriod({
          servicePeriod,
          anchorDate,
          monthsPerPeriod: contractCadence.monthsPerPeriod,
        });

      return (
        toISODate(toPlainDate(contractInvoiceWindow.start)) ===
          input.invoiceWindow.start &&
        toISODate(toPlainDate(contractInvoiceWindow.end)) ===
          input.invoiceWindow.end
      );
    });

    if (servicePeriods.length > 1) {
      throw new Error(
        `Contract cadence produced multiple due service periods for one invoice window on contract line ${input.clientContractLine.client_contract_line_id}`,
      );
    }

    return resolveRecurringSettlementsForInvoiceWindow({
      servicePeriods,
      invoiceWindow: input.invoiceWindow,
      activityWindow: input.activityWindow,
      duePosition: input.duePosition,
    });
  }

  private getContractCadenceDefinition(
    billingFrequency?: string | null,
  ): { monthsPerPeriod: number; generator: ContractCadenceGenerator } | null {
    switch (billingFrequency) {
      case "monthly":
        return {
          monthsPerPeriod: 1,
          generator: generateMonthlyContractCadenceServicePeriods,
        };
      case "quarterly":
        return {
          monthsPerPeriod: 3,
          generator: generateQuarterlyContractCadenceServicePeriods,
        };
      case "semi-annually":
        return {
          monthsPerPeriod: 6,
          generator: generateSemiAnnualContractCadenceServicePeriods,
        };
      case "annually":
        return {
          monthsPerPeriod: 12,
          generator: generateAnnualContractCadenceServicePeriods,
        };
      default:
        return null;
    }
  }

  private async calculateTimeBasedCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): Promise<ITimeBasedCharge[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    // Fetch the contract line details to get plan-wide settings
    const plan = await this.knex("contract_lines")
      .where({
        contract_line_id: clientContractLine.contract_line_id,
        tenant: this.tenant,
      })
      .first();

    if (!plan) {
      throw new Error(
        `Contract Line ${clientContractLine.contract_line_id} not found for client ${clientId}`,
      );
    }

    const tenant = this.tenant; // Capture tenant value for joins
    const knexRef = this.knex; // Closure-friendly knex reference for join callbacks
    const contractCurrency = clientContractLine.currency_code || "USD";
    const clientConfigService = new ClientContractServiceConfigurationService(
      this.knex,
      tenant,
    );
    const clientServiceConfigs =
      await clientConfigService.getConfigurationsForClientContractLine(
        clientContractLine.client_contract_line_id,
      );

    const resolvedBillingCycle =
      billingCycle ??
      (!recurringTimingSelection &&
      recurringTimingSelectionSource !== "persisted"
        ? await this.getBillingCycle(clientId, billingPeriod.startDate)
        : undefined);

    const timingResolution = this.resolveServiceDrivenChargeTiming(
      billingPeriod,
      clientContractLine,
      resolvedBillingCycle,
      recurringTimingSelection,
      recurringTimingSelectionSource,
    );
    if (!timingResolution) {
      return [];
    }

    const servicePeriodStartExclusive = timingResolution.servicePeriodStartExclusive;
    const servicePeriodEndExclusive = timingResolution.servicePeriodEndExclusive;
    const servicePeriodStart = timingResolution.servicePeriodStart;
    const servicePeriodEnd = timingResolution.servicePeriodEnd;

    // Create a map of service IDs to their hourly configurations
    const serviceConfigMap = new Map<
      string,
      {
        config: IContractLineServiceConfiguration &
          IContractLineServiceHourlyConfig;
        userTypeRates: Map<string, number>;
      }
    >();

    for (const configDetails of clientServiceConfigs) {
      if (configDetails.baseConfig.configuration_type !== "Hourly") {
        continue;
      }
      const hourlyDetails = configDetails.typeConfig as
        | (IContractLineServiceHourlyConfig & { hourly_rate?: number | null })
        | null;
      if (!hourlyDetails) {
        continue;
      }

      const userTypeRates = configDetails.userTypeRates ?? [];
      const userRateMap = new Map<string, number>();
      for (const rate of userTypeRates) {
        userRateMap.set(rate.user_type, rate.rate);
      }

      const combinedConfig = {
        config_id: configDetails.baseConfig.config_id,
        contract_line_id: clientContractLine.contract_line_id,
        service_id: configDetails.serviceId,
        configuration_type: "Hourly",
        quantity: configDetails.baseConfig.quantity ?? null,
        custom_rate: configDetails.baseConfig.custom_rate ?? null,
        hourly_rate:
          hourlyDetails.hourly_rate != null
            ? Number(hourlyDetails.hourly_rate)
            : 0,
        minimum_billable_time: hourlyDetails.minimum_billable_time ?? 0,
        round_up_to_nearest: hourlyDetails.round_up_to_nearest ?? 0,
      } as IContractLineServiceConfiguration & IContractLineServiceHourlyConfig;

      serviceConfigMap.set(configDetails.serviceId, {
        config: combinedConfig,
        userTypeRates: userRateMap,
      });
    }
    let configuredServiceIds = Array.from(serviceConfigMap.keys());
    if (configuredServiceIds.length === 0) {
      configuredServiceIds = await this.getServiceIdsForContractLine(
        clientContractLine.contract_line_id,
      );
    }
    if (configuredServiceIds.length === 0) {
      return [];
    }
    const uniquelyAssignableServiceIds =
      await this.getUniquelyAssignableServiceIdsForLine({
        clientId,
        serviceIds: configuredServiceIds,
        contractLineId: clientContractLine.contract_line_id,
        servicePeriodStartExclusive,
        servicePeriodEndExclusive,
      });

    const query = this.knex("time_entries")
      .join("users", function () {
        this.on("time_entries.user_id", "=", "users.user_id").andOn(
          "users.tenant",
          "=",
          "time_entries.tenant",
        );
      })
      .leftJoin("service_catalog", function () {
        this.on(
          "service_catalog.service_id",
          "=",
          "time_entries.service_id",
        ).andOn("service_catalog.tenant", "=", "time_entries.tenant");
      })
      .leftJoin("service_prices as sp", function () {
        this.on("sp.service_id", "=", "service_catalog.service_id")
          .andOn("sp.tenant", "=", "service_catalog.tenant")
          .andOn(
            "sp.currency_code",
            "=",
            knexRef.raw("?", [contractCurrency]),
          );
      })
      .leftJoin("project_ticket_links", function () {
        this.on(
          "time_entries.work_item_id",
          "=",
          "project_ticket_links.ticket_id",
        ).andOn("project_ticket_links.tenant", "=", "time_entries.tenant");
      })
      .leftJoin("project_tasks", function () {
        this.on(
          "time_entries.work_item_id",
          "=",
          "project_tasks.task_id",
        ).andOn("project_tasks.tenant", "=", "time_entries.tenant");
      })
      .leftJoin("project_phases", function () {
        this.on("project_tasks.phase_id", "=", "project_phases.phase_id").andOn(
          "project_phases.tenant",
          "=",
          "project_tasks.tenant",
        );
      })
      .leftJoin("projects", function () {
        this.on("project_phases.project_id", "=", "projects.project_id").andOn(
          "projects.tenant",
          "=",
          "project_phases.tenant",
        );
      })
      .leftJoin("tickets", function () {
        this.on("time_entries.work_item_id", "=", "tickets.ticket_id").andOn(
          "tickets.tenant",
          "=",
          "time_entries.tenant",
        );
      })
      .where({
        "time_entries.tenant": client.tenant,
      })
      .where("time_entries.start_time", ">=", servicePeriodStartExclusive)
      .where("time_entries.end_time", "<", servicePeriodEndExclusive)
      .where("time_entries.invoiced", false)
      .whereIn("time_entries.service_id", configuredServiceIds)
      .where(function (this: Knex.QueryBuilder) {
        // Either the time entry has the specific contract line ID (use contract_line_id for contract associations)
        this.where(
          "time_entries.contract_line_id",
          clientContractLine.contract_line_id,
        );
        if (uniquelyAssignableServiceIds.length > 0) {
          // Unassigned time is allocatable only when service-to-line matching is unique.
          this.orWhere(function (this: Knex.QueryBuilder) {
            this.whereNull("time_entries.contract_line_id").whereIn(
              "time_entries.service_id",
              uniquelyAssignableServiceIds,
            );
          });
        }
      })
      .where(function (this: Knex.QueryBuilder) {
        this.where(function (this: Knex.QueryBuilder) {
          this.where(
            "time_entries.work_item_type",
            "=",
            "project_task",
          ).whereNotNull("project_tasks.task_id");
        }).orWhere(function (this: Knex.QueryBuilder) {
          this.where("time_entries.work_item_type", "=", "ticket").whereNotNull(
            "tickets.ticket_id",
          );
        });
      })
      .where(function (this: Knex.QueryBuilder) {
        this.where("projects.client_id", clientId).orWhere(
          "tickets.client_id",
          clientId,
        );
      })
      .where("time_entries.approval_status", "APPROVED")
      .select(
        "time_entries.*",
        "service_catalog.service_name",
        "service_catalog.default_rate",
        "service_catalog.tax_rate_id",
        "sp.rate as currency_rate",
        this.knex.raw(
          "COALESCE(project_tasks.task_name, tickets.title) as work_item_name",
        ),
      );

    const timeEntries = await query;

    const timeBasedChargesPromises = timeEntries.map(
      async (entry: any): Promise<ITimeBasedCharge> => {
        const startDateTime = Temporal.PlainDateTime.from(
          entry.start_time.toISOString().replace("Z", ""),
        );
        const endDateTime = Temporal.PlainDateTime.from(
          entry.end_time.toISOString().replace("Z", ""),
        );

        // Get the service configuration if available
        const serviceConfig = serviceConfigMap.get(entry.service_id);
        const isSystemManagedDefault = (clientContractLine as { is_system_managed_default?: boolean | null })
          .is_system_managed_default === true;

        // Calculate duration based on configuration settings
        let durationMinutes = startDateTime.until(endDateTime, {
          largestUnit: "minutes",
        }).minutes;

        if (serviceConfig && !isSystemManagedDefault) {
          // Apply minimum billable time
          if (durationMinutes < serviceConfig.config.minimum_billable_time) {
            durationMinutes = serviceConfig.config.minimum_billable_time;
          }

          // Round up to nearest increment
          if (serviceConfig.config.round_up_to_nearest > 0) {
            const remainder =
              durationMinutes % serviceConfig.config.round_up_to_nearest;
            if (remainder > 0) {
              durationMinutes +=
                serviceConfig.config.round_up_to_nearest - remainder;
            }
          }
        }

        // Convert to hours. Bill the fractional hours that remain after the
        // minimum-billable-time and round-up-to-nearest rules above. Previously
        // this used Math.ceil(durationMinutes / 60), which forced every entry up
        // to a whole hour and silently overrode the configured rounding
        // increment (e.g. a 4h10m entry on a 15-minute increment was rounded to
        // 4h15m and then ceiled to 5h). The rate is per hour, so the quantity
        // must carry the partial hour.
        const duration = durationMinutes / 60;

        // Resolve rate, preferring overrides over the currency-specific catalog price.
        // Order: per-entry custom rate → per-user-type rate (contract-line config) → service_prices
        // row in the contract's currency. We deliberately do NOT fall back to the legacy
        // service_catalog.default_rate column because it is currency-untagged and would
        // silently bill a USD-intended amount in a non-USD contract.
        const userTypeRate =
          !isSystemManagedDefault &&
          serviceConfig &&
          serviceConfig.userTypeRates.has(entry.user_type)
            ? (serviceConfig.userTypeRates.get(entry.user_type) as number)
            : undefined;
        const resolvedRate =
          entry.custom_rate ??
          userTypeRate ??
          (entry.currency_rate != null ? Number(entry.currency_rate) : undefined);
        if (resolvedRate === undefined) {
          throw new Error(
            `Missing pricing for time entry on service "${entry.service_name}" (${entry.service_id}) in ${contractCurrency}. ` +
              `Add a ${contractCurrency} price in the service catalog or set a custom rate on the time entry / contract line.`,
          );
        }
        const rate = Math.ceil(Number(resolvedRate) || 0);

        // Check for overtime if applicable
        let total = Math.round(duration * rate);
        // Use plan-wide settings from the fetched 'plan' object
        if (
          plan.enable_overtime &&
          plan.overtime_threshold &&
          duration > plan.overtime_threshold
        ) {
          const regularHours = plan.overtime_threshold;
          const overtimeHours = duration - regularHours;
          // Use plan's overtime_rate, fallback to 1.5x the calculated rate (user or service specific)
          const overtimeRate = plan.overtime_rate || rate * 1.5;
          total = Math.round(
            regularHours * rate + overtimeHours * overtimeRate,
          );
        }

        // Determine tax info using the helper function
        // Pass a minimal service object containing the necessary IDs
        const { taxRegion: serviceTaxRegion, isTaxable } =
          await this.getTaxInfoFromService({
            service_id: entry.service_id,
            tax_rate_id: entry.tax_rate_id, // Pass the fetched tax_rate_id
          });

        // Calculate tax amount (will be recalculated later in invoiceService, but set initial values)
        let taxAmount = 0;
        let taxRate = 0;
        const effectiveTaxRegion =
          serviceTaxRegion ??
          (await this.getLocationTaxRegionCode(clientContractLine.location_id)) ??
          (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
          undefined;

        if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
          try {
            const taxServiceInstance = new TaxService();
            const taxResult = await taxServiceInstance.calculateTax(
              client.client_id,
              total,
              billingPeriod.endDate,
              effectiveTaxRegion,
              true,
              clientContractLine.currency_code || "USD",
            );
            taxRate = taxResult.taxRate;
            taxAmount = taxResult.taxAmount;
          } catch (error) {
            console.error(
              `Error calculating initial tax for time entry ${entry.entry_id}:`,
              error,
            );
          }
        }

        return {
          serviceId: entry.service_id,
          serviceName: entry.service_name,
          config_id: serviceConfig?.config.config_id,
          client_contract_line_id: clientContractLine.client_contract_line_id,
          userId: entry.user_id,
          duration,
          quantity: duration,
          rate,
          total,
          type: "time",
          tax_amount: taxAmount, // Set initial tax amount
          tax_rate: taxRate, // Set initial tax rate
          tax_region: effectiveTaxRegion, // Use derived region, fallback to location then client default
          entryId: entry.entry_id,
          is_taxable: isTaxable, // Use derived value
          servicePeriodStart,
          servicePeriodEnd,
          billingTiming: timingResolution.duePosition,
          // Add contract association information when the plan is covered by a contract assignment
          client_contract_id:
            clientContractLine.client_contract_id || undefined,
          contract_name: clientContractLine.contract_name || undefined,
          location_id: clientContractLine.location_id ?? null,
        };
      },
    );

    const timeBasedCharges = await Promise.all(timeBasedChargesPromises);

    return timeBasedCharges;
  }

  private async calculateUsageBasedCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): Promise<IUsageBasedCharge[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const tenant = this.tenant; // Capture tenant value for joins
    const knexRef = this.knex; // Closure-friendly knex reference for join callbacks
    const contractCurrency = clientContractLine.currency_code || "USD";
    const clientConfigService = new ClientContractServiceConfigurationService(
      this.knex,
      tenant,
    );
    const clientServiceConfigs =
      await clientConfigService.getConfigurationsForClientContractLine(
        clientContractLine.client_contract_line_id,
      );

    const resolvedBillingCycle =
      billingCycle ??
      (!recurringTimingSelection &&
      recurringTimingSelectionSource !== "persisted"
        ? await this.getBillingCycle(clientId, billingPeriod.startDate)
        : undefined);

    const timingResolution = this.resolveServiceDrivenChargeTiming(
      billingPeriod,
      clientContractLine,
      resolvedBillingCycle,
      recurringTimingSelection,
      recurringTimingSelectionSource,
    );
    if (!timingResolution) {
      return [];
    }

    const servicePeriodStartExclusive = timingResolution.servicePeriodStartExclusive;
    const servicePeriodEndExclusive = timingResolution.servicePeriodEndExclusive;
    const servicePeriodStart = timingResolution.servicePeriodStart;
    const servicePeriodEnd = timingResolution.servicePeriodEnd;

    // Create a map of service IDs to their usage configurations and rate tiers
    const serviceConfigMap = new Map<
      string,
      {
        config: IContractLineServiceConfiguration &
          IContractLineServiceUsageConfig;
        rateTiers: IContractLineServiceRateTier[];
      }
    >();

    for (const configDetails of clientServiceConfigs) {
      if (configDetails.baseConfig.configuration_type !== "Usage") {
        continue;
      }
      const usageDetails = configDetails.typeConfig as
        | (IContractLineServiceUsageConfig & { base_rate?: number | null })
        | null;
      if (!usageDetails) {
        continue;
      }
      const rateTiers = configDetails.rateTiers ?? [];
      const normalizedConfig = {
        config_id: configDetails.baseConfig.config_id,
        contract_line_id: clientContractLine.contract_line_id,
        service_id: configDetails.serviceId,
        configuration_type: "Usage",
        quantity: configDetails.baseConfig.quantity ?? null,
        custom_rate:
          configDetails.baseConfig.custom_rate != null
            ? Number(configDetails.baseConfig.custom_rate)
            : null,
        unit_of_measure: usageDetails.unit_of_measure,
        enable_tiered_pricing: Boolean(usageDetails.enable_tiered_pricing),
        minimum_usage: usageDetails.minimum_usage ?? 0,
        base_rate:
          usageDetails.base_rate != null
            ? Number(usageDetails.base_rate)
            : null,
      } as IContractLineServiceConfiguration & IContractLineServiceUsageConfig;

      serviceConfigMap.set(configDetails.serviceId, {
        config: normalizedConfig,
        rateTiers,
      });
    }
    let configuredServiceIds = Array.from(serviceConfigMap.keys());
    if (configuredServiceIds.length === 0) {
      configuredServiceIds = await this.getServiceIdsForContractLine(
        clientContractLine.contract_line_id,
      );
    }
    if (configuredServiceIds.length === 0) {
      return [];
    }
    const uniquelyAssignableServiceIds =
      await this.getUniquelyAssignableServiceIdsForLine({
        clientId,
        serviceIds: configuredServiceIds,
        contractLineId: clientContractLine.contract_line_id,
        servicePeriodStartExclusive,
        servicePeriodEndExclusive,
      });

    const usageRecordQuery = this.knex("usage_tracking")
      .leftJoin("service_catalog", function () {
        this.on(
          "service_catalog.service_id",
          "=",
          "usage_tracking.service_id",
        ).andOn("service_catalog.tenant", "=", "usage_tracking.tenant");
      })
      .leftJoin("service_prices as sp", function () {
        this.on("sp.service_id", "=", "service_catalog.service_id")
          .andOn("sp.tenant", "=", "service_catalog.tenant")
          .andOn(
            "sp.currency_code",
            "=",
            knexRef.raw("?", [contractCurrency]),
          );
      })
      .where({
        "usage_tracking.client_id": clientId,
        "usage_tracking.tenant": this.tenant,
        "usage_tracking.invoiced": false,
      })
      .whereIn("usage_tracking.service_id", configuredServiceIds)
      .where("usage_tracking.usage_date", ">=", servicePeriodStartExclusive)
      .where("usage_tracking.usage_date", "<", servicePeriodEndExclusive)
      .where(function (this: Knex.QueryBuilder) {
        // Either the usage record has the specific contract line ID (use contract_line_id for contract associations)
        this.where(
          "usage_tracking.contract_line_id",
          clientContractLine.contract_line_id,
        );
        if (uniquelyAssignableServiceIds.length > 0) {
          // Unassigned usage is allocatable only when service-to-line matching is unique.
          this.orWhere(function (this: Knex.QueryBuilder) {
            this.whereNull("usage_tracking.contract_line_id").whereIn(
              "usage_tracking.service_id",
              uniquelyAssignableServiceIds,
            );
          });
        }
      })
      .select(
        "usage_tracking.*",
        "service_catalog.service_name",
        "service_catalog.default_rate",
        "service_catalog.tax_rate_id",
        "sp.rate as currency_rate",
      ); // Fetch tax_rate_id

    const usageRecords = await usageRecordQuery;

    const usageBasedChargesPromises = usageRecords.map(
      async (record: any): Promise<IUsageBasedCharge> => {
        // Get the service configuration if available
        const serviceConfig = serviceConfigMap.get(record.service_id);
        const isSystemManagedDefault = (clientContractLine as { is_system_managed_default?: boolean | null })
          .is_system_managed_default === true;

        // Apply minimum usage if configured
        let quantity = record.quantity;
        if (
          !isSystemManagedDefault &&
          serviceConfig &&
          quantity < (serviceConfig.config.minimum_usage ?? 0)
        ) {
          quantity = serviceConfig.config.minimum_usage;
        }

        // Determine rate and calculate total.
        // Order: contract-line configured custom_rate → service_prices row in the contract
        // currency. The legacy service_catalog.default_rate is intentionally not used here
        // because it is currency-untagged and would silently mis-price non-USD contracts.
        const configuredCustomRate =
          !isSystemManagedDefault && serviceConfig && serviceConfig.config.custom_rate
            ? Number(serviceConfig.config.custom_rate)
            : undefined;
        const resolvedRate =
          configuredCustomRate ??
          (record.currency_rate != null ? Number(record.currency_rate) : undefined);
        const willUseTieredPricing =
          !isSystemManagedDefault &&
          serviceConfig &&
          serviceConfig.config.enable_tiered_pricing &&
          serviceConfig.rateTiers.length > 0;
        if (resolvedRate === undefined && !willUseTieredPricing) {
          throw new Error(
            `Missing pricing for usage on service "${record.service_name}" (${record.service_id}) in ${contractCurrency}. ` +
              `Add a ${contractCurrency} price in the service catalog, set a custom rate on the contract line, or enable tiered pricing.`,
          );
        }
        let rate = Math.ceil(resolvedRate ?? 0);
        let total = Math.ceil(quantity * rate);

        // Apply tiered pricing if enabled
        if (
          !isSystemManagedDefault &&
          serviceConfig &&
          serviceConfig.config.enable_tiered_pricing &&
          serviceConfig.rateTiers.length > 0
        ) {
          total = 0;
          let remainingQuantity = quantity;

          for (const tier of serviceConfig.rateTiers) {
            if (remainingQuantity <= 0) break;

            const tierMax = tier.max_quantity || Number.MAX_SAFE_INTEGER;
            const tierQuantity = Math.min(
              remainingQuantity,
              tierMax - tier.min_quantity + 1,
            );

            if (tierQuantity > 0) {
              total += Math.ceil(tierQuantity * tier.rate);
              remainingQuantity -= tierQuantity;
            }
          }
        }

        // Determine tax info using the helper function
        const { taxRegion: serviceTaxRegion, isTaxable } =
          await this.getTaxInfoFromService({
            service_id: record.service_id,
            tax_rate_id: record.tax_rate_id, // Pass the fetched tax_rate_id
          });

        // Calculate tax amount (will be recalculated later)
        let taxAmount = 0;
        let taxRate = 0;
        const effectiveTaxRegion =
          serviceTaxRegion ??
          (await this.getLocationTaxRegionCode(clientContractLine.location_id)) ??
          (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
          undefined;

        if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
          try {
            const taxServiceInstance = new TaxService();
            const taxResult = await taxServiceInstance.calculateTax(
              client.client_id,
              total,
              billingPeriod.endDate,
              effectiveTaxRegion,
              true,
              clientContractLine.currency_code || "USD",
            );
            taxRate = taxResult.taxRate;
            taxAmount = taxResult.taxAmount;
          } catch (error) {
            console.error(
              `Error calculating initial tax for usage record ${record.usage_id}:`,
              error,
            );
          }
        }

        return {
          serviceId: record.service_id,
          config_id: serviceConfig?.config.config_id,
          serviceName: record.service_name,
          client_contract_line_id: clientContractLine.client_contract_line_id,
          quantity,
          rate,
          total,
          tax_region: effectiveTaxRegion, // Use derived region, fallback to location then client default
          type: "usage",
          tax_amount: taxAmount, // Set initial tax amount
          tax_rate: taxRate, // Set initial tax rate
          usageId: record.usage_id,
          is_taxable: isTaxable, // Use derived value
          servicePeriodStart,
          servicePeriodEnd,
          billingTiming: timingResolution.duePosition,
          // Add contract association information when the plan is covered by a contract assignment
          client_contract_id:
            clientContractLine.client_contract_id || undefined,
          contract_name: clientContractLine.contract_name || undefined,
          location_id: clientContractLine.location_id ?? null,
        };
      },
    );

    const usageBasedCharges = await Promise.all(usageBasedChargesPromises);

    return usageBasedCharges;
  }

  private async getUniquelyAssignableServiceIdsForLine(input: {
    clientId: string;
    serviceIds: string[];
    contractLineId: string;
    servicePeriodStartExclusive: ISO8601String;
    servicePeriodEndExclusive: ISO8601String;
  }): Promise<string[]> {
    if (!this.tenant || input.serviceIds.length === 0) {
      return [];
    }

    const rows = await this.knex("client_contracts as cc")
      .join("contracts as c", function () {
        this.on("c.contract_id", "=", "cc.contract_id").andOn(
          "c.tenant",
          "=",
          "cc.tenant",
        );
      })
      .join("contract_lines as cl", function () {
        this.on("cl.contract_id", "=", "c.contract_id").andOn(
          "cl.tenant",
          "=",
          "c.tenant",
        );
      })
      .join("contract_line_services as cls", function () {
        this.on("cls.contract_line_id", "=", "cl.contract_line_id").andOn(
          "cls.tenant",
          "=",
          "cl.tenant",
        );
      })
      .where({
        "cc.tenant": this.tenant,
        "cc.client_id": input.clientId,
      })
      .whereIn("cls.service_id", input.serviceIds)
      .where("cc.start_date", "<", input.servicePeriodEndExclusive)
      .where(function (this: Knex.QueryBuilder) {
        this.whereNull("cc.end_date").orWhere(
          "cc.end_date",
          ">=",
          input.servicePeriodStartExclusive,
        );
      })
      .groupBy("cls.service_id")
      .select(
        "cls.service_id",
        this.knex.raw("COUNT(DISTINCT cl.contract_line_id) as line_count"),
        this.knex.raw("MIN(cl.contract_line_id::text) as only_line_id"),
      );

    return rows
      .filter(
        (row: any) =>
          Number(row.line_count) === 1 &&
          row.only_line_id === input.contractLineId,
      )
      .map((row: any) => row.service_id);
  }

  private async getServiceIdsForContractLine(
    contractLineId: string,
  ): Promise<string[]> {
    if (!this.tenant) {
      return [];
    }

    const rows = await this.knex("contract_line_services")
      .where({
        tenant: this.tenant,
        contract_line_id: contractLineId,
      })
      .select("service_id");

    return rows
      .map((row: any) => row.service_id)
      .filter((serviceId: unknown): serviceId is string =>
        typeof serviceId === "string" && serviceId.length > 0,
      );
  }

  private async calculateRecurringQuantityCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle: string | undefined,
    chargeType: "product" | "license",
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): Promise<Array<IProductCharge | ILicenseCharge>> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const isLicenseCharge = chargeType === "license";

    const resolvedBillingCycle =
      billingCycle ??
      (!recurringTimingSelection &&
      recurringTimingSelectionSource !== "persisted"
        ? await this.getBillingCycle(clientId, billingPeriod.startDate)
        : undefined);

    const timingResolution = this.resolveRecurringChargeTiming(
      billingPeriod,
      clientContractLine,
      resolvedBillingCycle,
      recurringTimingSelection,
      recurringTimingSelectionSource,
    );
    if (!timingResolution) {
      return [];
    }

    const {
      servicePeriodStart,
      servicePeriodEnd,
      coverageRatio,
    } = timingResolution;

    const client = (await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first()) as IClient;

    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const tenant = this.tenant; // Capture tenant value for joins
    const db = this.knex;

    let planServicesQuery = db("contract_line_services as cls")
      .join("contract_line_service_configuration as clsc", function () {
        this.on("clsc.contract_line_id", "=", "cls.contract_line_id")
          .andOn("clsc.service_id", "=", "cls.service_id")
          .andOn("clsc.tenant", "=", "cls.tenant");
      })
      .join("service_catalog as sc", function () {
        this.on("sc.service_id", "=", "cls.service_id").andOn(
          "sc.tenant",
          "=",
          "cls.tenant",
        );
      })
      .leftJoin("service_prices as sp", function () {
        this.on("sp.service_id", "=", "sc.service_id")
          .andOn("sp.tenant", "=", "sc.tenant")
          .andOn(
            "sp.currency_code",
            "=",
            db.raw("?", [clientContractLine.currency_code || "USD"]),
          );
      })
      .where({
        "cls.contract_line_id": clientContractLine.client_contract_line_id,
        "cls.tenant": tenant,
      })
      .andWhere("sc.item_kind", "=", "product");

    if (isLicenseCharge) {
      planServicesQuery = planServicesQuery.andWhere("sc.is_license", true);
    } else {
      planServicesQuery = planServicesQuery.andWhere(function () {
        this.where("sc.is_license", false).orWhereNull("sc.is_license");
      });
    }

    const planServices = await planServicesQuery
      .select(
        "sc.service_id",
        "sc.service_name",
        "sc.default_rate",
        "sc.tax_rate_id",
        "clsc.config_id",
        "cls.quantity as service_quantity",
        "cls.custom_rate as service_line_custom_rate",
        "clsc.quantity as configuration_quantity",
        "clsc.custom_rate as configuration_custom_rate",
        "sp.rate as price_rate",
      );

    if (planServices.length === 0) {
      return [];
    }

    const productChargesPromises = planServices.map(
      async (service: any): Promise<IProductCharge | ILicenseCharge> => {
        // Determine tax info using the helper function
        const { taxRegion: serviceTaxRegion, isTaxable } =
          await this.getTaxInfoFromService({
            service_id: service.service_id,
            tax_rate_id: service.tax_rate_id, // Assuming tax_rate_id is fetched
          });

        const hasOverride =
          service.configuration_custom_rate != null ||
          service.service_line_custom_rate != null;
        const hasCatalogPrice = service.price_rate != null;
        if (!hasOverride && !hasCatalogPrice) {
          const currency = clientContractLine.currency_code || "USD";
          throw new Error(
            `Missing pricing for ${chargeType} "${service.service_name}" (${service.service_id}) in ${currency}. ` +
              `Add a ${currency} price in the product catalog or set a custom rate on the contract line.`,
          );
        }

        const rateCandidate =
          service.configuration_custom_rate ??
          service.service_line_custom_rate ??
          service.price_rate ??
          service.default_rate ??
          0;
        const rate = Math.round(Number(rateCandidate) || 0);

        const quantityCandidate =
          service.configuration_quantity ?? service.service_quantity ?? 1;
        const quantity = Math.max(
          1,
          Math.round(Number(quantityCandidate) || 1),
        );

        const total = rate * quantity;

        // Calculate tax amount (will be recalculated later)
        let taxAmount = 0;
        let taxRate = 0;
        const effectiveTaxRegion =
          serviceTaxRegion ??
          (await this.getLocationTaxRegionCode(clientContractLine.location_id)) ??
          (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
          undefined;

        if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
          try {
            const taxServiceInstance = new TaxService();
            const taxResult = await taxServiceInstance.calculateTax(
              client.client_id,
              total,
              servicePeriodEnd,
              effectiveTaxRegion,
              true,
              clientContractLine.currency_code || "USD",
            );
            taxRate = taxResult.taxRate;
            taxAmount = taxResult.taxAmount;
          } catch (error) {
            console.error(
              `Error calculating initial tax for ${chargeType} service ${service.service_id}:`,
              error,
            );
          }
        }

        const charge: IProductCharge | ILicenseCharge = {
          type: chargeType,
          serviceId: service.service_id,
          config_id: service.config_id,
          client_contract_line_id: clientContractLine.client_contract_line_id,
          serviceName: service.service_name,
          quantity: quantity,
          rate: rate,
          total: total,
          tax_amount: taxAmount,
          tax_rate: taxRate,
          tax_region: effectiveTaxRegion, // Use derived region, fallback to location then client default
          is_taxable: isTaxable,
          servicePeriodStart,
          servicePeriodEnd,
          billingTiming: (clientContractLine.billing_timing ?? "arrears") as
            | "arrears"
            | "advance",
          // Add contract association information when the plan is covered by a contract assignment
          client_contract_id:
            clientContractLine.client_contract_id || undefined,
          contract_name: clientContractLine.contract_name || undefined,
          location_id: clientContractLine.location_id ?? null,
          ...(isLicenseCharge
            ? {
                period_start: servicePeriodStart,
                period_end: servicePeriodEnd,
              }
            : {}),
        };
        return charge;
      },
    );
    const quantityCharges = await Promise.all(productChargesPromises);

    return clientContractLine.enable_proration
      ? this.applyQuantityChargeCoverageSettlement(
          quantityCharges,
          coverageRatio,
        )
      : quantityCharges;
  }

  private async calculateProductCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): Promise<IProductCharge[]> {
    return (await this.calculateRecurringQuantityCharges(
      clientId,
      billingPeriod,
      clientContractLine,
      billingCycle,
      "product",
      recurringTimingSelection,
      recurringTimingSelectionSource,
    )) as IProductCharge[];
  }

  private async calculateLicenseCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    clientContractLine: IClientContractLine,
    billingCycle?: string,
    recurringTimingSelection?: ResolvedRecurringChargeTiming,
    recurringTimingSelectionSource?: CalculateBillingOptions["recurringTimingSelectionSource"],
  ): Promise<ILicenseCharge[]> {
    return (await this.calculateRecurringQuantityCharges(
      clientId,
      billingPeriod,
      clientContractLine,
      billingCycle,
      "license",
      recurringTimingSelection,
      recurringTimingSelectionSource,
    )) as ILicenseCharge[];
  }

  private async calculateMaterialCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    currencyCode: string,
  ): Promise<IProductCharge[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = (await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first()) as IClient;

    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const tenant = this.tenant;

    let materialRows: any[] = [];

    try {
      materialRows = await this.knex
        .select([
          this.knex.raw(`'ticket' as source_type`),
          "tm.ticket_material_id as source_id",
          "tm.service_id",
          "tm.quantity",
          "tm.rate",
          "tm.currency_code",
          "tm.description",
          "tm.created_at",
          "sc.service_name",
          "sc.tax_rate_id",
        ])
        .from("ticket_materials as tm")
        .join("service_catalog as sc", function () {
          this.on("tm.service_id", "=", "sc.service_id").andOn(
            "tm.tenant",
            "=",
            "sc.tenant",
          );
        })
        .where({
          "tm.tenant": tenant,
          "tm.client_id": clientId,
          "tm.is_billed": false,
          "tm.currency_code": currencyCode,
        })
        .where("tm.created_at", ">=", billingPeriod.startDate)
        .andWhere("tm.created_at", "<", billingPeriod.endDate)
        .unionAll([
          this.knex
            .select([
              this.knex.raw(`'project' as source_type`),
              "pm.project_material_id as source_id",
              "pm.service_id",
              "pm.quantity",
              "pm.rate",
              "pm.currency_code",
              "pm.description",
              "pm.created_at",
              "sc.service_name",
              "sc.tax_rate_id",
            ])
            .from("project_materials as pm")
            .join("service_catalog as sc", function () {
              this.on("pm.service_id", "=", "sc.service_id").andOn(
                "pm.tenant",
                "=",
                "sc.tenant",
              );
            })
            .where({
              "pm.tenant": tenant,
              "pm.client_id": clientId,
              "pm.is_billed": false,
              "pm.currency_code": currencyCode,
            })
            .where("pm.created_at", ">=", billingPeriod.startDate)
            .andWhere("pm.created_at", "<", billingPeriod.endDate),
        ]);
    } catch (err: any) {
      if (err?.code === "42P01") {
        return [];
      }
      throw err;
    }

    const chargesPromises = (materialRows as any[]).map(
      async (row): Promise<IProductCharge> => {
        const quantity = Math.max(1, Number(row.quantity || 1));
        const rate = Math.round(Number(row.rate || 0));
        const total = rate * quantity;

        const { taxRegion: serviceTaxRegion, isTaxable } =
          await this.getTaxInfoFromService({
            service_id: row.service_id,
            tax_rate_id: row.tax_rate_id,
          });

        let taxAmount = 0;
        let taxRate = 0;
        const effectiveTaxRegion =
          serviceTaxRegion ??
          (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
          undefined;

        if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
          try {
            const taxServiceInstance = new TaxService();
            const taxResult = await taxServiceInstance.calculateTax(
              client.client_id,
              total,
              billingPeriod.endDate,
              effectiveTaxRegion,
              true,
              currencyCode || "USD",
            );
            taxRate = taxResult.taxRate;
            taxAmount = taxResult.taxAmount;
          } catch (error) {
            console.error(
              `Error calculating initial tax for material ${row.source_type} ${row.source_id}:`,
              error,
            );
          }
        }

        const description = row.description || row.service_name || "Material";

        return {
          type: "product",
          serviceId: row.service_id,
          serviceName: description,
          quantity,
          rate,
          total,
          tax_amount: taxAmount,
          tax_rate: taxRate,
          tax_region: effectiveTaxRegion,
          is_taxable: isTaxable,
          servicePeriodStart: billingPeriod.startDate,
          servicePeriodEnd: billingPeriod.endDate,
          billingTiming: "arrears",
        };
      },
    );

    return Promise.all(chargesPromises);
  }

  private async calculateBucketPlanCharges(
    clientId: string,
    billingPeriod: IBillingPeriod,
    contractLine: IClientContractLine,
  ): Promise<IBucketCharge[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    // Get bucket configurations for this plan
    // Note: client_contract_line_id is actually a contract_line_id value (see getClientContractLinesAndCycle)
    const bucketConfigs = await this.knex(
      "contract_line_service_configuration as clsc",
    )
      .join("contract_line_services as cls", function () {
        this.on("clsc.contract_line_id", "=", "cls.contract_line_id")
          .andOn("clsc.service_id", "=", "cls.service_id")
          .andOn("clsc.tenant", "=", "cls.tenant");
      })
      .leftJoin("contract_line_service_bucket_config as clsbc", function () {
        this.on("clsbc.config_id", "=", "clsc.config_id").andOn(
          "clsbc.tenant",
          "=",
          "clsc.tenant",
        );
      })
      .join("service_catalog as sc", function () {
        this.on("cls.service_id", "=", "sc.service_id").andOn(
          "sc.tenant",
          "=",
          "cls.tenant",
        );
      })
      .where({
        "cls.contract_line_id": contractLine.client_contract_line_id,
        "clsc.configuration_type": "Bucket",
        "cls.tenant": client.tenant,
      })
      .select(
        "clsc.*",
        "clsbc.*",
        "sc.service_name",
        "sc.default_rate",
        "sc.tax_rate_id",
        "sc.unit_of_measure",
        "sc.billing_method",
        "cls.service_id",
      );

    if (!bucketConfigs || bucketConfigs.length === 0) {
      return [];
    }

    // Process each bucket configuration
    const bucketChargesPromises = bucketConfigs.map(
      async (bucketConfig): Promise<IBucketCharge[]> => {
        // Pull usage records captured for bucket plans within this billing period
        const usageRecords = await this.knex("bucket_usage")
          .where({
            tenant: client.tenant,
            client_id: clientId,
            contract_line_id: contractLine.contract_line_id,
            service_catalog_id: bucketConfig.service_id,
          })
          .where("period_start", ">=", billingPeriod.startDate)
          .where("period_end", "<=", billingPeriod.endDate)
          .select("*");

        if (usageRecords.length === 0) {
          return [];
        }

        const usageByPeriod = new Map<
          string,
          {
            periodStart: ISO8601String;
            periodEnd: ISO8601String;
            minutesUsed: number;
            overageMinutes: number;
          }
        >();

        for (const record of usageRecords) {
          const periodStart = record.period_start
            ? toISODate(toPlainDate(record.period_start))
            : toISODate(toPlainDate(billingPeriod.startDate));
          const periodEnd = record.period_end
            ? toISODate(toPlainDate(record.period_end))
            : toISODate(
                toPlainDate(billingPeriod.endDate).subtract({ days: 1 }),
              );
          const periodKey = `${periodStart}:${periodEnd}`;

          if (!usageByPeriod.has(periodKey)) {
            usageByPeriod.set(periodKey, {
              periodStart,
              periodEnd,
              minutesUsed: 0,
              overageMinutes: 0,
            });
          }

          const periodUsage = usageByPeriod.get(periodKey)!;
          const recordMinutesUsed =
            Number(record.minutes_used ?? 0) ||
            (record.hours_used !== undefined
              ? Number(record.hours_used) * 60
              : 0);
          const recordOverageMinutes =
            Number(record.overage_minutes ?? 0) ||
            (record.overage_hours !== undefined
              ? Number(record.overage_hours) * 60
              : 0);

          periodUsage.minutesUsed += recordMinutesUsed;
          periodUsage.overageMinutes += recordOverageMinutes;
        }

        const isUsageBucket =
          contractLine.contract_line_type === "Usage" ||
          bucketConfig.billing_method === "usage";
        const configuredQuantity = isUsageBucket
          ? Number(bucketConfig.total_minutes ?? 0)
          : bucketConfig.total_minutes ??
            (bucketConfig.total_hours !== undefined
              ? Number(bucketConfig.total_hours) * 60
              : 0);

        const bucketCharges: IBucketCharge[] = [];
        for (const usagePeriod of Array.from(usageByPeriod.values()).sort((a, b) =>
          a.periodStart.localeCompare(b.periodStart),
        )) {
          let overageQuantity = usagePeriod.overageMinutes;
          if (!overageQuantity && configuredQuantity) {
            overageQuantity = Math.max(
              0,
              usagePeriod.minutesUsed - configuredQuantity,
            );
          }

          const unitsUsed = usagePeriod.minutesUsed;
          const overageUnits = overageQuantity;
          const hoursUsed = usagePeriod.minutesUsed / 60;
          const overageHours = overageQuantity / 60;
          if ((isUsageBucket ? overageUnits : overageHours) <= 0) {
            continue;
          }

          const { taxRegion: serviceTaxRegion, isTaxable } =
            await this.getTaxInfoFromService({
              service_id: bucketConfig.service_id,
              tax_rate_id: bucketConfig.tax_rate_id,
            });

          const overageRate = Math.ceil(bucketConfig.overage_rate);
          const total = Math.ceil((isUsageBucket ? overageUnits : overageHours) * overageRate);

          let taxAmount = 0;
          let taxRate = 0;
          const effectiveTaxRegion =
            serviceTaxRegion ??
            (await this.getLocationTaxRegionCode(contractLine.location_id)) ??
            (await this.getClientDefaultTaxRegionCode(client.client_id)) ??
            undefined;

          if (!client.is_tax_exempt && isTaxable && effectiveTaxRegion) {
            try {
              const taxServiceInstance = new TaxService();
              const taxResult = await taxServiceInstance.calculateTax(
                client.client_id,
                total,
                usagePeriod.periodEnd,
                effectiveTaxRegion,
                true,
                contractLine.currency_code || "USD",
              );
              taxRate = taxResult.taxRate;
              taxAmount = taxResult.taxAmount;
            } catch (error) {
              console.error(
                `Error calculating initial tax for bucket service ${bucketConfig.service_id}:`,
                error,
              );
            }
          }

          bucketCharges.push({
            type: "bucket",
            service_catalog_id: bucketConfig.service_id,
            serviceName: bucketConfig.service_name,
            client_contract_line_id: contractLine.client_contract_line_id,
            rate: overageRate,
            total: total,
            hoursUsed: hoursUsed,
            overageHours: overageHours,
            overageRate: overageRate,
            quantity: isUsageBucket ? overageUnits : undefined,
            isUsageBucket,
            unitOfMeasure: bucketConfig.unit_of_measure ?? null,
            unitsUsed: isUsageBucket ? unitsUsed : undefined,
            includedUnits: isUsageBucket ? Math.max(0, unitsUsed - overageUnits) : undefined,
            overageUnits: isUsageBucket ? overageUnits : undefined,
            tax_rate: taxRate,
            tax_region: effectiveTaxRegion,
            serviceId: bucketConfig.service_id,
            config_id: bucketConfig.config_id,
            tax_amount: taxAmount,
            is_taxable: isTaxable,
            servicePeriodStart: usagePeriod.periodStart,
            servicePeriodEnd: usagePeriod.periodEnd,
            billingTiming: "arrears",
            client_contract_id: contractLine.client_contract_id || undefined,
            contract_name: contractLine.contract_name || undefined,
            location_id: contractLine.location_id ?? null,
          });
        }

        return bucketCharges;
      },
    );

    const bucketCharges = (await Promise.all(bucketChargesPromises)).flat();

    return bucketCharges;
  }

  private async hasExistingServicePeriodCharge(
    clientContractLineId: string,
    servicePeriodStart: ISO8601String,
    servicePeriodEnd: ISO8601String,
    billingTiming: "arrears" | "advance",
  ): Promise<boolean> {
    await this.initKnex();
    if (!this.knex || !this.tenant) {
      throw new Error("Database connection not initialized");
    }

    // Note: client_contract_line_id is actually a contract_line_id value (see getClientContractLinesAndCycle)
    const existing = await this.knex("invoice_charge_details as iid")
      .join("contract_line_service_configuration as clsc", function () {
        this.on("iid.config_id", "=", "clsc.config_id").andOn(
          "iid.tenant",
          "=",
          "clsc.tenant",
        );
      })
      .where("iid.tenant", this.tenant)
      .andWhere("clsc.contract_line_id", clientContractLineId)
      .andWhere("iid.service_period_start", servicePeriodStart)
      .andWhere("iid.service_period_end", servicePeriodEnd)
      .andWhere("iid.billing_timing", billingTiming)
      .first();

    if (existing) {
      return true;
    }

    const servicePeriodEndExclusive = toISODate(
      toPlainDate(servicePeriodEnd).add({ days: 1 }),
    );
    const obligationCandidates = buildPostDropRecurringObligationCandidates({
      contractLineId: clientContractLineId,
      chargeFamily: "fixed",
    });

    const recurringLinkedCharge = await this.knex("recurring_service_periods")
      .where("tenant", this.tenant)
      .where("charge_family", "fixed")
      .where("due_position", billingTiming)
      .whereNotNull("invoice_id")
      .where(function matchObligationCandidates() {
        for (const [index, candidate] of obligationCandidates.entries()) {
          if (index === 0) {
            this.where(function matchCandidate() {
              this.where("obligation_type", candidate.obligationType).andWhere(
                "obligation_id",
                candidate.obligationId,
              );
            });
            continue;
          }

          this.orWhere(function matchCandidate() {
            this.where("obligation_type", candidate.obligationType).andWhere(
              "obligation_id",
              candidate.obligationId,
            );
          });
        }
      })
      .where("service_period_start", servicePeriodStart)
      .where(function matchServicePeriodEnd() {
        this.where("service_period_end", servicePeriodEnd).orWhere(
          "service_period_end",
          servicePeriodEndExclusive,
        );
      })
      .first("record_id");

    return Boolean(recurringLinkedCharge);
  }

  private shouldApplyAdvanceTerminationCoverageSettlement(
    clientContractLine: IClientContractLine,
    billingPeriod: IBillingPeriod,
    billingTiming: "arrears" | "advance",
    coverageRatio: number,
  ): boolean {
    if (
      billingTiming !== "advance" ||
      !clientContractLine.end_date ||
      coverageRatio >= 1
    ) {
      return false;
    }

    const lineEndExclusive = toPlainDate(clientContractLine.end_date).add({
      days: 1,
    });
    const currentPeriodEndExclusive = toPlainDate(billingPeriod.endDate);
    return (
      Temporal.PlainDate.compare(lineEndExclusive, currentPeriodEndExclusive) <
      0
    );
  }

  private applyFixedChargeCoverageSettlement(
    charges: IFixedPriceCharge[],
    coverageRatio: number,
    roundingMode: "coverage_ratio" | "unused_credit_net",
  ): IFixedPriceCharge[] {
    return charges
      .map((charge) => {
        const settledTotal = this.settleFixedChargeAmount(
          charge.total ?? 0,
          coverageRatio,
          roundingMode,
        );
        const settledTax = this.settleFixedChargeAmount(
          charge.tax_amount ?? 0,
          coverageRatio,
          roundingMode,
        );
        const settledRate = this.settleFixedChargeAmount(
          charge.rate ?? charge.total ?? 0,
          coverageRatio,
          roundingMode,
        );
        const settledAllocatedAmount =
          charge.allocated_amount === undefined
            ? undefined
            : this.settleFixedChargeAmount(
                charge.allocated_amount,
                coverageRatio,
                roundingMode,
              );

        if (settledTotal === 0 && settledTax === 0) {
          return null;
        }

        return {
          ...charge,
          total: settledTotal,
          tax_amount: settledTax,
          rate: settledRate,
          ...(settledAllocatedAmount === undefined
            ? {}
            : { allocated_amount: settledAllocatedAmount }),
        };
      })
      .filter((charge): charge is IFixedPriceCharge => charge !== null);
  }

  private applyQuantityChargeCoverageSettlement<
    TCharge extends IProductCharge | ILicenseCharge,
  >(charges: TCharge[], coverageRatio: number): TCharge[] {
    const boundedCoverageRatio = Math.max(0, Math.min(coverageRatio, 1));

    return charges.map((charge) => {
      const originalTotal = Math.ceil(charge.total ?? 0);
      const originalTax = Math.ceil(charge.tax_amount ?? 0);
      const proratedTotal = Math.ceil(originalTotal * boundedCoverageRatio);
      const proratedTax = Math.ceil(originalTax * boundedCoverageRatio);
      const quantity = Math.max(1, Math.round(charge.quantity ?? 1));
      const derivedRate =
        quantity > 0 ? Math.ceil(proratedTotal / quantity) : 0;

      return {
        ...charge,
        rate: derivedRate,
        total: derivedRate * quantity,
        tax_amount: proratedTax,
      };
    });
  }

  private settleFixedChargeAmount(
    amount: number,
    coverageRatio: number,
    roundingMode: "coverage_ratio" | "unused_credit_net",
  ): number {
    if (!Number.isFinite(amount) || amount === 0) {
      return 0;
    }

    const boundedCoverageRatio = Math.max(0, Math.min(coverageRatio, 1));
    const sign = amount < 0 ? -1 : 1;
    const absoluteAmount = Math.abs(amount);

    if (roundingMode === "unused_credit_net") {
      const unusedRatio = 1 - boundedCoverageRatio;
      return sign * (absoluteAmount - Math.round(absoluteAmount * unusedRatio));
    }

    return sign * Math.round(absoluteAmount * boundedCoverageRatio);
  }

  private calculatePeriodDaysExclusive(
    start: ISO8601String,
    end: ISO8601String,
  ): number {
    const startPlain = toPlainDate(start);
    const endPlain = toPlainDate(end);
    if (Temporal.PlainDate.compare(endPlain, startPlain) <= 0) {
      return 0;
    }
    return startPlain.until(endPlain, { largestUnit: "days" }).days;
  }

  private async applyDiscountsAndAdjustments(
    billingResult: IBillingResult,
    clientId: string,
    billingPeriod: IBillingPeriod,
  ): Promise<IBillingResult> {
    // Fetch applicable discounts within the billing period
    const discounts = await this.fetchDiscounts(
      clientId,
      billingPeriod,
      billingResult.charges,
    );

    let discountTotal = 0;
    for (const discount of discounts) {
      if (discount.discount_type === "percentage") {
        discount.amount = billingResult.totalAmount * discount.value;
      } else if (discount.discount_type === "fixed") {
        discount.amount = discount.value;
      }
      discountTotal += discount.amount || 0;
    }

    const finalAmount = billingResult.totalAmount - discountTotal;

    return {
      ...billingResult,
      discounts,
      adjustments: [], // Implement adjustments if needed
      finalAmount,
    };
  }

  private async fetchDiscounts(
    clientId: string,
    billingPeriod: IBillingPeriod,
    charges: IBillingCharge[] = [],
  ): Promise<IDiscount[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const discountWindowsByContractLine =
      this.buildDiscountEvaluationWindowsByContractLine(charges);
    const { start: candidateStart, endInclusive: candidateEnd } =
      this.getDiscountCandidateQueryBounds(
        billingPeriod,
        discountWindowsByContractLine,
      );

    // Query discounts via client_contracts -> contracts -> contract_lines
    // instead of the deprecated client_contract_lines table
    const discountRows = (await this.knex("discounts")
      .join("contract_line_discounts", function () {
        this.on(
          "discounts.discount_id",
          "=",
          "contract_line_discounts.discount_id",
        ).andOn("contract_line_discounts.tenant", "=", "discounts.tenant");
      })
      .join("contract_lines as cl", function () {
        this.on(
          "cl.contract_line_id",
          "=",
          "contract_line_discounts.contract_line_id",
        ).andOn("cl.tenant", "=", "contract_line_discounts.tenant");
      })
      .join("contracts as c", function () {
        this.on("c.contract_id", "=", "cl.contract_id").andOn(
          "c.tenant",
          "=",
          "cl.tenant",
        );
      })
      .join("client_contracts as cc", function () {
        this.on("cc.contract_id", "=", "c.contract_id").andOn(
          "cc.tenant",
          "=",
          "c.tenant",
        );
      })
      .where({
        "cc.client_id": clientId,
        "cc.tenant": client.tenant,
        "discounts.is_active": true,
      })
      .andWhere("discounts.start_date", "<=", candidateEnd)
      .andWhere(function (this: Knex.QueryBuilder) {
        this.whereNull("discounts.end_date").orWhere(
          "discounts.end_date",
          ">",
          candidateStart,
        );
      })
      .select(
        "discounts.*",
        "contract_line_discounts.contract_line_id",
      )) as DiscountQueryRow[];

    const filteredDiscounts = discountRows.filter((discount) =>
      this.discountMatchesEvaluationWindow(
        discount,
        billingPeriod,
        discountWindowsByContractLine,
      ),
    );

    return Array.from(
      new Map(
        filteredDiscounts.map((discount) => [
          discount.discount_id,
          {
            ...discount,
            contract_line_id: undefined,
          } as IDiscount,
        ]),
      ).values(),
    );
  }

  private buildDiscountEvaluationWindowsByContractLine(
    charges: IBillingCharge[],
  ): Map<string, Array<{ start: ISO8601String; endInclusive: ISO8601String }>> {
    const windowsByContractLine = new Map<
      string,
      Array<{ start: ISO8601String; endInclusive: ISO8601String }>
    >();

    for (const charge of charges) {
      if (
        !charge.client_contract_line_id ||
        !charge.servicePeriodStart ||
        !charge.servicePeriodEnd
      ) {
        continue;
      }

      const contractLineId = charge.client_contract_line_id;
      const window = {
        start: toISODate(toPlainDate(charge.servicePeriodStart)),
        endInclusive: toISODate(toPlainDate(charge.servicePeriodEnd)),
      };
      const existingWindows = windowsByContractLine.get(contractLineId) ?? [];
      const alreadyPresent = existingWindows.some(
        (existingWindow) =>
          existingWindow.start === window.start &&
          existingWindow.endInclusive === window.endInclusive,
      );

      if (!alreadyPresent) {
        existingWindows.push(window);
        windowsByContractLine.set(contractLineId, existingWindows);
      }
    }

    return windowsByContractLine;
  }

  private getDiscountCandidateQueryBounds(
    billingPeriod: IBillingPeriod,
    discountWindowsByContractLine: Map<
      string,
      Array<{ start: ISO8601String; endInclusive: ISO8601String }>
    >,
  ): { start: ISO8601String; endInclusive: ISO8601String } {
    const invoiceWindow = {
      start: toISODate(toPlainDate(billingPeriod.startDate)),
      endInclusive: toISODate(toPlainDate(billingPeriod.endDate)),
    };

    const candidateWindows = [
      invoiceWindow,
      ...Array.from(discountWindowsByContractLine.values()).flat(),
    ];

    return candidateWindows.reduce(
      (bounds, window) => ({
        start: window.start < bounds.start ? window.start : bounds.start,
        endInclusive:
          window.endInclusive > bounds.endInclusive
            ? window.endInclusive
            : bounds.endInclusive,
      }),
      invoiceWindow,
    );
  }

  private discountMatchesEvaluationWindow(
    discount: DiscountQueryRow,
    billingPeriod: IBillingPeriod,
    discountWindowsByContractLine: Map<
      string,
      Array<{ start: ISO8601String; endInclusive: ISO8601String }>
    >,
  ): boolean {
    const invoiceWindow = {
      start: toISODate(toPlainDate(billingPeriod.startDate)),
      endInclusive: toISODate(toPlainDate(billingPeriod.endDate)),
    };
    const contractLineWindows = discount.contract_line_id
      ? discountWindowsByContractLine.get(discount.contract_line_id)
      : undefined;
    const evaluationWindows =
      contractLineWindows && contractLineWindows.length > 0
        ? contractLineWindows
        : [invoiceWindow];

    const discountStart = toISODate(toPlainDate(discount.start_date));
    const discountEndExclusive = discount.end_date
      ? toISODate(toPlainDate(discount.end_date))
      : null;

    return evaluationWindows.some(
      (window) =>
        discountStart <= window.endInclusive &&
        (discountEndExclusive == null || discountEndExclusive > window.start),
    );
  }

  private async fetchAdjustments(clientId: string): Promise<IAdjustment[]> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const client = await this.knex("clients")
      .where({
        client_id: clientId,
        tenant: this.tenant,
      })
      .first();
    if (!client) {
      throw new Error(`Client ${clientId} not found in tenant ${this.tenant}`);
    }

    const adjustments = await this.knex("adjustments").where({
      client_id: clientId,
      tenant: client.tenant,
    });
    return Array.isArray(adjustments) ? adjustments : [];
  }

  /**
   * Recalculates an entire invoice, including tax amounts and totals.
   * This is used when updating manual items to ensure all calculations are consistent.
   */
  async recalculateInvoice(invoiceId: string): Promise<void> {
    await this.initKnex();
    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    let tenant = this.tenant;

    console.log(`Recalculating invoice ${invoiceId}`);

    if (!this.tenant) {
      throw new Error("tenant context not found");
    }

    const invoice = await this.knex("invoices")
      .where({
        invoice_id: invoiceId,
        tenant: this.tenant,
      })
      .first();

    if (!invoice) {
      throw new Error(
        `Invoice ${invoiceId} not found in tenant ${this.tenant}`,
      );
    }

    const client = await this.knex("clients")
      .where({
        client_id: invoice.client_id,
        tenant: this.tenant,
      })
      .first();

    if (!client) {
      throw new Error(
        `Client ${invoice.client_id} not found in tenant ${this.tenant}`,
      );
    }

    // Removed direct use of TaxService here.
    // Removed subtotal and totalTax accumulation logic.

    console.log("Starting invoice recalculation:", {
      invoiceId,
      client: {
        id: client.client_id,
        name: client.client_name,
        isTaxExempt: client.is_tax_exempt,
        // region_code is still on client table for default fallback, but not primary source for service tax
      },
    });

    // Recalculation is intentionally financial-only. Canonical recurring
    // invoice_charge_details rows remain the persisted source of service-period
    // truth after invoice creation; this path should only update tax and totals.
    await this.knex.transaction(async (trx) => {
      // Step 1: Recalculate and distribute tax across all items using the service function
      console.log(
        `[recalculateInvoice] Calling calculateAndDistributeTax for invoice ${invoiceId}`,
      );
      const taxService = new TaxService(); // Instantiate TaxService here
      await calculateAndDistributeTax(
        trx,
        invoiceId,
        client,
        taxService,
        tenant,
      ); // Pass client object and taxService instance
      console.log(
        `[recalculateInvoice] Finished calculateAndDistributeTax for invoice ${invoiceId}`,
      );

      // Step 2: Update invoice totals and record the transaction using the service function
      console.log(
        `[recalculateInvoice] Calling updateInvoiceTotalsAndRecordTransaction for invoice ${invoiceId}`,
      );
      await updateInvoiceTotalsAndRecordTransaction(
        trx,
        invoiceId,
        client, // Pass client object
        tenant, // Pass tenant
        invoice.invoice_number, // Pass invoice number
        undefined,
        {
          transactionType: "invoice_adjustment",
          description: `Adjusted invoice ${invoice.invoice_number}`,
        },
      );
      console.log(
        `[recalculateInvoice] Finished updateInvoiceTotalsAndRecordTransaction for invoice ${invoiceId}`,
      );

      // Note: The original logic for processing discount items and updating their net_amount
      // based on percentages is removed. It's assumed that calculateAndDistributeTax
      // handles the correct net amounts and tax distribution, including discounts.
      // If discount amounts need recalculation based on the new subtotal *before* tax distribution,
      // that logic would need to be added back here or integrated into calculateAndDistributeTax.
      // For now, we follow the instruction to delegate fully.
    });

    // Removed console log referencing deleted variables subtotal/totalTax

    // Event emission removed - moved back to invoiceModification.ts
  }
}
