/**
 * Sync debug logging — gated behind localStorage flag.
 *
 * Enable in browser console:
 *   localStorage.setItem("openchamber:sync:debug", "1")
 *
 * Disable:
 *   localStorage.removeItem("openchamber:sync:debug")
 *
 * All checks are early-returns on the hot path — zero cost when disabled.
 */

const FLAG_KEY = "openchamber:sync:debug"

let _enabled: boolean | undefined

function isSyncDebugEnabled(): boolean {
  if (_enabled !== undefined) return _enabled
  try {
    _enabled = typeof localStorage !== "undefined" && localStorage.getItem(FLAG_KEY) === "1"
  } catch {
    _enabled = false
  }
  return _enabled
}
type SyncDebugCategory = "pipeline" | "reducer" | "dispatch" | "recovery"

function log(cat: SyncDebugCategory, ...args: unknown[]): void {
  if (!isSyncDebugEnabled()) return
  const tag = `%c[sync:${cat}]`
  const style = "color: #888"
  console.log(tag, style, ...args)
}

export const syncDebug = {
  pipeline: {
    /** Event coalesced (replaced an earlier event in the queue). */
    coalesced: (eventType: string, coalesceKey: string) =>
      log("pipeline", "coalesced", eventType, coalesceKey),

    /** Flush batch dispatched. */
    flush: (count: number) =>
      log("pipeline", "flush", `${count} events`),
  },

  reducer: {
    /** message.updated skipped because role/finish/completed matched existing. */
    messageUpdatedUnchanged: (sessionID: string, messageID: string, role: string, finish: unknown, completed: unknown) =>
      log("reducer", "message.updated UNCHANGED (skipped)", { sessionID, messageID, role, finish, completed }),

    /** message.part.updated arrived but no parts array exists for this messageID. */
    partUpdatedNoExistingParts: (messageID: string, partID: string, partType: string) =>
      log("reducer", "message.part.updated NO EXISTING PARTS", { messageID, partID, partType }),

    /** message.part.delta arrived but parts array missing — silently dropped. */
    partDeltaNoParts: (messageID: string, partID: string) =>
      log("reducer", "message.part.delta DROPPED (no parts array)", { messageID, partID }),

    /** message.part.delta arrived but partID not found in parts array. */
    partDeltaNotFound: (messageID: string, partID: string) =>
      log("reducer", "message.part.delta DROPPED (partID not found)", { messageID, partID }),

    /** SKIP_PARTS filtered out a part. */
    partSkipped: (messageID: string, partID: string, partType: string) =>
      log("reducer", "message.part.updated SKIPPED (type filtered)", { messageID, partID, partType }),
  },

  dispatch: {
    /** Event dispatched to store but reducer returned false (no state change). */
    eventNoChange: (eventType: string, sessionID?: string, messageID?: string) =>
      log("dispatch", "event → no change", { eventType, sessionID, messageID }),

    /** Event applied to store successfully. */
    eventApplied: (eventType: string, sessionID?: string, messageID?: string) =>
      log("dispatch", "event → applied", { eventType, sessionID, messageID }),
  },

  recovery: {
    /** A scoped session snapshot fetch is starting because live state looked incomplete. */
    materializing: (details: { reason: string; directory: string; sessionID: string; messageID?: string; partID?: string }) =>
      log("recovery", "materializing session", details),
  },
} as const
