import { createHash, timingSafeEqual } from "node:crypto";
import { spawn } from "node:child_process";
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { unzipSync } from "node:zlib";

import {
  EVENT_SOURCES,
  EVENT_TYPES,
  GIFT_CONFIRMATION_STATUS,
  createId,
  nowIso,
  toEpochMs
} from "../../shared-types/src/index.js";
import {
  createP1CaptureStateEvent,
  createP1EvidenceSourceDescriptor,
  NdjsonRawP1EvidenceStore,
  normalizeTiktokWebcastP1Payload,
  withP1RawReference
} from "../../live-analytics-p1-evidence/src/index.js";

const nodeRequire = createRequire(import.meta.url);
let tiktokConnectorSchemaCache;
let nodeWebSocketConstructorCache;

export const GIFT_SOURCE_ROLES = Object.freeze({
  MOCK: "mock",
  PRIMARY_REALTIME: "primary_realtime",
  BACKUP_REALTIME: "backup_realtime",
  LIVE_TOOL_GUARDIAN: "live_tool_guardian",
  OFFICIAL_BACKEND: "official_backend",
  MANUAL_CORRECTION: "manual_correction"
});

export const GIFT_SOURCE_HEALTH = Object.freeze({
  HEALTHY: "healthy",
  LAGGING: "lagging",
  DISCONNECTED: "disconnected",
  NEEDS_REVIEW: "needs_review"
});

export const GAMEPLAY_SENSITIVITY = Object.freeze({
  NORMAL: "normal",
  HIGH: "high"
});

export const GIFT_ROUTE_DECISIONS = Object.freeze({
  USE_PRIMARY: "use_primary",
  USE_BACKUP: "use_backup",
  WAIT_FOR_CONFIRMATION: "wait_for_confirmation",
  NEEDS_REVIEW: "needs_review"
});

export const GIFT_SOURCE_CONTRACT_LEVELS = Object.freeze({
  READY: "ready",
  ATTENTION: "attention",
  BLOCKED: "blocked"
});

export const GIFT_SOURCE_CAPABILITIES = Object.freeze({
  RAW_EVENT_BACKUP: "raw_event_backup",
  NORMALIZED_GIFT_DETAIL: "normalized_gift_detail",
  ROOM_IDENTITY: "room_identity",
  LIVE_STATUS: "live_status",
  OFFICIAL_RECORDS: "official_records",
  MANUAL_REVIEW: "manual_review"
});

export const LOCAL_GIFT_SOURCE_INBOX_FILES = Object.freeze({
  [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: "primary.gifts.jsonl",
  [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: "backup.gifts.jsonl",
  [GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN]: "guardian.gifts.jsonl",
  [GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]: "official-review.gifts.jsonl"
});

export const TIKTOK_GIFT_CONNECTOR_MODES = Object.freeze({
  WEBCAST_WEBSOCKET_PRIMARY: "webcast_websocket_primary",
  LOCAL_BROWSER_OBSERVER_BACKUP: "local_browser_observer_backup",
  MANAGED_WEBSOCKET_BACKUP: "managed_websocket_backup",
  LIVE_STUDIO_GUARDIAN: "live_studio_guardian",
  MCA_REPORT_IMPORT: "mca_report_import",
  // Legacy alias kept so older saved connector plans can still be read.
  LIVE_STUDIO_PRIMARY: "live_studio_primary",
  LOCAL_BACKUP_READER: "local_backup_reader",
  OFFICIAL_AFTER_LIVE_REVIEW: "official_after_live_review"
});

export const TIKTOK_GIFT_RESILIENCE_DECISIONS = Object.freeze({
  PRIMARY_ACTIVE: "primary_active",
  BACKUP_TAKEOVER: "backup_takeover",
  HOLD_FOR_CONFIRMATION: "hold_for_confirmation",
  AFTER_LIVE_BACKFILL_REQUIRED: "after_live_backfill_required",
  MANUAL_REVIEW_REQUIRED: "manual_review_required"
});

export const TIKTOK_GIFT_RISK_LEVELS = Object.freeze({
  NORMAL: "normal",
  BIG_GIFT: "big_gift",
  COMBO_IN_PROGRESS: "combo_in_progress",
  HIGH_FREQUENCY: "high_frequency",
  MISMATCH: "mismatch"
});

export const TIKTOK_GIFT_BACKFILL_ACTIONS = Object.freeze({
  ACCEPT_PRIMARY: "accept_primary",
  ACCEPT_BACKUP_TAKEOVER: "accept_backup_takeover",
  IMPORT_AFTER_LIVE: "import_after_live",
  CREATE_ADJUSTMENT: "create_adjustment",
  MANUAL_REVIEW: "manual_review"
});

export const TIKTOK_WEBCAST_COLLECTOR_STATES = Object.freeze({
  IDLE: "idle",
  RESOLVING_ROOM: "resolving_room",
  STANDBY: "standby_waiting_for_live",
  CONNECTING: "connecting",
  CONNECTED: "connected",
  RECONNECTING: "reconnecting",
  STOPPED: "stopped",
  NEEDS_REVIEW: "needs_review"
});

export const TIKTOK_WEBCAST_DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";

export const DOUYIN_LIVE_DATA_MSG_TYPES = Object.freeze({
  COMMENT: "live_comment",
  GIFT: "live_gift",
  LIKE: "live_like",
  FANSCLUB: "live_fansclub"
});

export const DOUYIN_LIVE_DATA_ACK_TYPES = Object.freeze({
  RECEIVED: 1,
  PROCESSED: 2
});

export const DOUYIN_LIVE_DATA_TASK_STATUS = Object.freeze({
  NOT_FOUND: 1,
  NOT_STARTED: 2,
  RUNNING: 3
});

export const DOUYIN_LIVE_DATA_ENDPOINTS = Object.freeze({
  PRODUCTION_BASE_URL: "https://webcast.bytedance.com",
  START_TASK: "/api/live_data/task/start",
  STOP_TASK: "/api/live_data/task/stop",
  QUERY_TASK: "/api/live_data/task/get",
  QUERY_FAILED_DATA: "/api/live_data/task/fail_data/get",
  REPORT_ACK: "/api/live_data/ack"
});

export const DOUYIN_LIVE_DATA_RELIABILITY = Object.freeze({
  level: "official_best_effort_with_compensation",
  customer_label: "官方推送 + 失败回查",
  absolute_accuracy_guarantee: false,
  LEVEL: "official_best_effort_with_compensation",
  CUSTOMER_LABEL: "官方推送 + 失败回查",
  ABSOLUTE_ACCURACY_GUARANTEE: false
});

function stableStringify(value) {
  if (Array.isArray(value)) {
    return `[${value.map(stableStringify).join(",")}]`;
  }
  if (value && typeof value === "object") {
    return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
  }
  return JSON.stringify(value);
}

function firstDefined(...values) {
  return values.find((value) => value !== undefined && value !== null);
}

function stringOrNull(value) {
  if (value === undefined || value === null) {
    return null;
  }
  const text = String(value).trim();
  return text ? text : null;
}

function numberOrNull(value) {
  if (value === undefined || value === null || value === "") {
    return null;
  }
  const number = Number(value);
  return Number.isFinite(number) ? number : null;
}

function numberOrZero(value) {
  return numberOrNull(value) ?? 0;
}

function booleanOrNull(value) {
  if (value === undefined || value === null) {
    return null;
  }
  if (typeof value === "boolean") {
    return value;
  }
  if (value === 1 || value === "1" || value === "true") {
    return true;
  }
  if (value === 0 || value === "0" || value === "false") {
    return false;
  }
  return null;
}

function hasHeadlessBrowserLaunchArg(args = []) {
  const values = Array.isArray(args)
    ? args
    : String(args ?? "").split(/\s+/u);
  return values.some((value) => /^--headless(?:=|$)/u.test(String(value ?? "").trim()));
}

export function hashRawGiftPayload(value) {
  return createHash("sha256").update(stableStringify(value ?? {})).digest("hex");
}

export function createGiftSourceDescriptor(input = {}) {
  const payload = {
    source_id: input.source_id ?? createId("gift_source"),
    role: input.role ?? GIFT_SOURCE_ROLES.MOCK,
    source: input.source ?? sourceForRole(input.role ?? GIFT_SOURCE_ROLES.MOCK),
    display_name: input.display_name ?? input.role ?? GIFT_SOURCE_ROLES.MOCK,
    connected: input.connected ?? true,
    health: input.health ?? GIFT_SOURCE_HEALTH.HEALTHY,
    event_lag_ms_p95: input.event_lag_ms_p95 ?? 0,
    event_count: input.event_count ?? 0,
    last_event_at: input.last_event_at ?? null,
    updated_at: input.updated_at ?? nowIso()
  };
  return payload;
}

export function createGiftSourceAdapterContract(input = {}) {
  const role = input.role ?? GIFT_SOURCE_ROLES.MOCK;
  const capabilities = input.capabilities ?? defaultCapabilitiesForRole(role);
  return {
    contract_id: input.contract_id ?? createId("gift_contract"),
    role,
    source_id: input.source_id ?? createId("gift_source"),
    display_name: input.display_name ?? role,
    capabilities,
    structured_detail: input.structured_detail ?? capabilities.includes(GIFT_SOURCE_CAPABILITIES.NORMALIZED_GIFT_DETAIL),
    can_drive_live_score: input.can_drive_live_score ?? canRoleDriveLiveScore(role),
    local_raw_backup_required: input.local_raw_backup_required ?? rawBackupRequiredForRole(role),
    local_raw_backup_path: input.local_raw_backup_path ?? null,
    upload_raw_default: input.upload_raw_default ?? false,
    target_lag_ms_p95: Number(input.target_lag_ms_p95 ?? defaultTargetLagForRole(role)),
    confirmation_window_ms: Number(input.confirmation_window_ms ?? defaultConfirmationWindowForRole(role)),
    field_map: input.field_map ?? defaultFieldMapForRole(role),
    health: input.health ?? GIFT_SOURCE_HEALTH.HEALTHY,
    connected: input.connected ?? true,
    created_at: input.created_at ?? nowIso()
  };
}

export function validateGiftSourceAdapterContract(contract = {}) {
  const issues = [];
  if (!Object.values(GIFT_SOURCE_ROLES).includes(contract.role)) {
    issues.push(createContractIssue("role", "unknown_source_role"));
  }
  if (!Array.isArray(contract.capabilities) || contract.capabilities.length === 0) {
    issues.push(createContractIssue("capabilities", "capability_required"));
  }
  if (contract.local_raw_backup_required && !contract.local_raw_backup_path) {
    issues.push(createContractIssue("local_raw_backup_path", "local_raw_backup_required"));
  }
  if (contract.upload_raw_default === true) {
    issues.push(createContractIssue("upload_raw_default", "raw_upload_disabled_by_default"));
  }
  if (contract.can_drive_live_score && !contract.structured_detail) {
    issues.push(createContractIssue("structured_detail", "live_score_requires_structured_detail"));
  }
  if (contract.role === GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN && contract.can_drive_live_score) {
    issues.push(createContractIssue("can_drive_live_score", "guardian_not_live_score_source_by_default"));
  }
  if ([GIFT_SOURCE_ROLES.PRIMARY_REALTIME, GIFT_SOURCE_ROLES.BACKUP_REALTIME].includes(contract.role)) {
    const requiredFields = ["source_event_id", "event_time", "gifter_user_id", "gift_id", "gift_count", "gift_total_value", "live_room_id"];
    for (const field of requiredFields) {
      if (!contract.field_map?.[field]) {
        issues.push(createContractIssue(`field_map.${field}`, "gift_detail_field_required"));
      }
    }
    if (!contract.capabilities.includes(GIFT_SOURCE_CAPABILITIES.NORMALIZED_GIFT_DETAIL)) {
      issues.push(createContractIssue("capabilities", "normalized_gift_detail_required"));
    }
  }
  if (contract.role === GIFT_SOURCE_ROLES.OFFICIAL_BACKEND && !contract.capabilities.includes(GIFT_SOURCE_CAPABILITIES.OFFICIAL_RECORDS)) {
    issues.push(createContractIssue("capabilities", "official_records_required"));
  }

  return {
    ok: issues.length === 0,
    level: issues.length === 0 ? GIFT_SOURCE_CONTRACT_LEVELS.READY : GIFT_SOURCE_CONTRACT_LEVELS.BLOCKED,
    issues
  };
}

export function validateGiftSourceContractSet(contracts = []) {
  const issues = [];
  const roles = new Set(contracts.map((contract) => contract.role));
  for (const role of [GIFT_SOURCE_ROLES.PRIMARY_REALTIME, GIFT_SOURCE_ROLES.BACKUP_REALTIME, GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN, GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]) {
    if (!roles.has(role)) {
      issues.push(createContractIssue(role, "required_source_role_missing"));
    }
  }
  for (const contract of contracts) {
    const result = validateGiftSourceAdapterContract(contract);
    issues.push(...result.issues.map((issue) => ({
      ...issue,
      source_id: contract.source_id,
      role: contract.role
    })));
  }

  return {
    ok: issues.length === 0,
    level: issues.length === 0 ? GIFT_SOURCE_CONTRACT_LEVELS.READY : GIFT_SOURCE_CONTRACT_LEVELS.BLOCKED,
    contract_count: contracts.length,
    roles: [...roles],
    issues
  };
}

export function createGiftSourceRunPlan({
  contracts = createDefaultGiftSourceContracts(),
  storagePlan = null,
  roomOwnershipReady = false,
  ledgerReady = false,
  sensitivity = GAMEPLAY_SENSITIVITY.NORMAL
} = {}) {
  const contractSet = validateGiftSourceContractSet(contracts);
  const descriptors = contracts.map((contract) => createGiftSourceDescriptor({
    source_id: contract.source_id,
    role: contract.role,
    display_name: contract.display_name,
    connected: contract.connected,
    health: contract.health,
    event_lag_ms_p95: contract.target_lag_ms_p95
  }));
  const route = selectGiftSourceRoute({
    sources: descriptors,
    sensitivity
  });
  const blockingReasons = [];
  if (!contractSet.ok) {
    blockingReasons.push("source_contract_not_ready");
  }
  if (!roomOwnershipReady) {
    blockingReasons.push("room_ownership_required");
  }
  if (!ledgerReady) {
    blockingReasons.push("ledger_required");
  }
  if (!storagePlan?.raw_realtime || !storagePlan?.raw_guardian) {
    blockingReasons.push("raw_backup_path_required");
  }
  if (![GIFT_ROUTE_DECISIONS.USE_PRIMARY, GIFT_ROUTE_DECISIONS.USE_BACKUP, GIFT_ROUTE_DECISIONS.WAIT_FOR_CONFIRMATION].includes(route.decision)) {
    blockingReasons.push("source_route_not_ready");
  }

  return {
    run_plan_id: createId("gift_run_plan"),
    created_at: nowIso(),
    ready_for_formal: blockingReasons.length === 0,
    level: blockingReasons.length === 0 ? GIFT_SOURCE_CONTRACT_LEVELS.READY : GIFT_SOURCE_CONTRACT_LEVELS.BLOCKED,
    route,
    contracts: contracts.map((contract) => ({
      source_id: contract.source_id,
      role: contract.role,
      can_drive_live_score: contract.can_drive_live_score,
      local_raw_backup_required: contract.local_raw_backup_required,
      local_raw_backup_path: contract.local_raw_backup_path
    })),
    raw_backup_paths: {
      realtime: storagePlan?.raw_realtime ?? null,
      guardian: storagePlan?.raw_guardian ?? null,
      manifest: storagePlan?.raw_manifest ?? null
    },
    local_write_before_confirm: true,
    upload_defaults: {
      raw_realtime: false,
      raw_guardian: false
    },
    customer_message: blockingReasons.length === 0 ? "礼物记录准备完成。" : "礼物记录准备中，请稍等。",
    blocking_reasons: blockingReasons,
    contract_issues: contractSet.issues
  };
}

export function createDefaultGiftSourceContracts({ baseDir = "data", sessionId = "session" } = {}) {
  return [
    createGiftSourceAdapterContract({
      role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
      source_id: "source_primary_realtime",
      local_raw_backup_path: `${baseDir}/raw/${sessionId}.primary.ndjson`
    }),
    createGiftSourceAdapterContract({
      role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
      source_id: "source_backup_realtime",
      local_raw_backup_path: `${baseDir}/raw/${sessionId}.backup.ndjson`
    }),
    createGiftSourceAdapterContract({
      role: GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN,
      source_id: "source_live_tool_guardian",
      local_raw_backup_path: `${baseDir}/raw/${sessionId}.guardian.ndjson`
    }),
    createGiftSourceAdapterContract({
      role: GIFT_SOURCE_ROLES.OFFICIAL_BACKEND,
      source_id: "source_official_backend",
      local_raw_backup_required: false
    }),
    createGiftSourceAdapterContract({
      role: GIFT_SOURCE_ROLES.MANUAL_CORRECTION,
      source_id: "source_manual_correction",
      local_raw_backup_required: false
    })
  ];
}

export function createRawGiftBackupRecord({
  rawGift = {},
  sourceDescriptor = createGiftSourceDescriptor(),
  sessionContext = {},
  normalizedGift = null,
  sequenceNo = 1,
  receivedAt = nowIso()
} = {}) {
  const eventTime = rawGift.event_time ?? rawGift.timestamp ?? receivedAt;
  const sourceEventId = rawGift.source_event_id ?? rawGift.id ?? null;
  return {
    raw_record_id: createId("raw_gift"),
    tenant_id: sessionContext.tenant_id ?? null,
    live_room_id: rawGift.live_room_id ?? sessionContext.live_room_id ?? null,
    session_id: sessionContext.session_id ?? null,
    source: rawGift.source ?? sourceDescriptor.source,
    source_role: sourceDescriptor.role,
    source_id: sourceDescriptor.source_id,
    source_event_id: sourceEventId,
    event_time: eventTime,
    received_at: receivedAt,
    sequence_no: Number(sequenceNo),
    raw_payload_hash: hashRawGiftPayload(rawGift),
    raw_payload: rawGift,
    normalized_preview: normalizedGift ? createNormalizedGiftPreview(normalizedGift) : null
  };
}

export class NdjsonRawGiftBackupStore {
  constructor({ filePath, sourceDescriptor = createGiftSourceDescriptor(), sessionContext = {} } = {}) {
    if (!filePath) {
      throw new Error("raw gift backup filePath is required");
    }
    this.filePath = filePath;
    this.sourceDescriptor = sourceDescriptor;
    this.sessionContext = sessionContext;

    mkdirSync(dirname(filePath), { recursive: true });
    if (!existsSync(filePath)) {
      writeFileSync(filePath, "", "utf8");
    }
  }

  append(rawGift, { normalizedGift = null, receivedAt = nowIso() } = {}) {
    if (!this.nextSequenceNo) {
      this.nextSequenceNo = countNdjsonRecords(this.filePath) + 1;
    }
    const sequenceNo = this.nextSequenceNo;
    const record = createRawGiftBackupRecord({
      rawGift,
      sourceDescriptor: this.sourceDescriptor,
      sessionContext: this.sessionContext,
      normalizedGift,
      sequenceNo,
      receivedAt
    });
    appendFileSync(this.filePath, `${JSON.stringify(record)}\n`, "utf8");
    this.nextSequenceNo += 1;
    return record;
  }

  listRecords() {
    return readFileSync(this.filePath, "utf8")
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean)
      .map((line) => JSON.parse(line));
  }

  createManifest(input = {}) {
    return createRawGiftBackupManifest({
      filePath: this.filePath,
      records: this.listRecords(),
      sourceDescriptor: this.sourceDescriptor,
      sessionContext: this.sessionContext,
      ...input
    });
  }
}

function countNdjsonRecords(filePath) {
  return readFileSync(filePath, "utf8")
    .split("\n")
    .filter((line) => line.trim())
    .length;
}

function createProductionRawBackupStore({ session, role, descriptor }) {
  return new NdjsonRawGiftBackupStore({
    filePath: join(
      ".runtime",
      "gift-source-production",
      session.tenant_id,
      `${session.session_id}.${role}.ndjson`
    ),
    sourceDescriptor: descriptor,
    sessionContext: {
      tenant_id: session.tenant_id,
      live_room_id: session.live_room_id,
      session_id: session.session_id
    }
  });
}

function createProductionP1RawStore({ session, role, descriptor }) {
  return new NdjsonRawP1EvidenceStore({
    filePath: join(
      ".runtime",
      "p1-evidence-production",
      session.tenant_id,
      `${session.session_id}.${role}.p1.ndjson`
    ),
    sourceDescriptor: descriptor,
    sessionContext: {
      tenant_id: session.tenant_id,
      live_room_id: session.live_room_id,
      session_id: session.session_id
    }
  });
}

export function createLocalGiftSourceInboxPlan({
  inboxDir,
  roles = [
    GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    GIFT_SOURCE_ROLES.BACKUP_REALTIME,
    GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN,
    GIFT_SOURCE_ROLES.OFFICIAL_BACKEND
  ],
  fileNames = LOCAL_GIFT_SOURCE_INBOX_FILES,
  createdAt = nowIso()
} = {}) {
  if (!inboxDir) {
    throw new Error("gift source inboxDir is required");
  }
  return {
    plan_id: createId("gift_inbox_plan"),
    created_at: createdAt,
    inbox_dir: inboxDir,
    local_only: true,
    upload_raw_default: false,
    scan_mode: "append_only_local_files",
    files: roles.map((role) => {
      const fileName = fileNames[role] ?? `${role}.gifts.jsonl`;
      const filePath = join(inboxDir, fileName);
      return {
        role,
        file_name: fileName,
        file_path: filePath,
        exists: existsSync(filePath),
        customer_label: labelForLocalInboxRole(role)
      };
    }),
    customer_message: "本地礼物记录入口已准备。"
  };
}

export function validateLocalGiftBridgeEvent(event = {}, {
  requireLiveRoomId = true
} = {}) {
  const issues = [];
  for (const field of ["source_event_id", "event_time", "gifter_user_id", "gift_id"]) {
    if (event[field] === undefined || event[field] === null || String(event[field]).trim() === "") {
      issues.push(createContractIssue(field, "gift_bridge_field_required"));
    }
  }
  if (requireLiveRoomId && (!event.live_room_id || String(event.live_room_id).trim() === "")) {
    issues.push(createContractIssue("live_room_id", "gift_bridge_field_required"));
  }
  if (event.event_time && Number.isNaN(Date.parse(event.event_time))) {
    issues.push(createContractIssue("event_time", "gift_bridge_time_invalid"));
  }
  const giftCount = Number(event.gift_count ?? event.count);
  if (!Number.isFinite(giftCount) || giftCount <= 0) {
    issues.push(createContractIssue("gift_count", "gift_bridge_count_invalid"));
  }
  const hasUnitValue = event.gift_unit_value !== undefined || event.unit_value !== undefined || event.gift_value !== undefined;
  const hasTotalValue = event.gift_total_value !== undefined;
  if (!hasUnitValue && !hasTotalValue) {
    issues.push(createContractIssue("gift_value", "gift_bridge_value_required"));
  }
  if (hasUnitValue) {
    const unitValue = Number(event.gift_unit_value ?? event.unit_value ?? event.gift_value);
    if (!Number.isFinite(unitValue) || unitValue < 0) {
      issues.push(createContractIssue("gift_unit_value", "gift_bridge_value_invalid"));
    }
  }
  if (hasTotalValue) {
    const totalValue = Number(event.gift_total_value);
    if (!Number.isFinite(totalValue) || totalValue < 0) {
      issues.push(createContractIssue("gift_total_value", "gift_bridge_value_invalid"));
    }
  }

  return {
    ok: issues.length === 0,
    issues
  };
}

export function createDouyinLiveDataSignature({
  headers = {},
  body = "",
  secret
} = {}) {
  if (!secret) {
    throw new Error("douyin live data signature secret is required");
  }
  const normalizedHeaders = normalizeDouyinHeaders(headers);
  const signedHeaderKeys = ["x-msg-type", "x-nonce-str", "x-roomid", "x-timestamp"]
    .filter((key) => normalizedHeaders[key] !== undefined && normalizedHeaders[key] !== null)
    .sort();
  const headerText = signedHeaderKeys
    .map((key) => `${key}=${normalizedHeaders[key]}`)
    .join("&");
  const rawData = `${headerText}${bodyToUtf8String(body)}${secret}`;
  return createHash("md5").update(rawData, "utf8").digest("base64");
}

export function verifyDouyinLiveDataSignature({
  headers = {},
  body = "",
  secret,
  nowMs = null,
  maxClockSkewMs = 5 * 60 * 1000
} = {}) {
  const normalizedHeaders = normalizeDouyinHeaders(headers);
  const issues = [];
  const provided = normalizedHeaders["x-signature"];
  if (!secret) {
    issues.push(createContractIssue("secret", "douyin_signature_secret_required"));
  }
  if (!provided) {
    issues.push(createContractIssue("x-signature", "douyin_signature_required"));
  }
  for (const field of ["x-msg-type", "x-nonce-str", "x-roomid", "x-timestamp"]) {
    if (!normalizedHeaders[field]) {
      issues.push(createContractIssue(field, "douyin_signature_header_required"));
    }
  }
  const timestamp = Number(normalizedHeaders["x-timestamp"]);
  if (normalizedHeaders["x-timestamp"] && !Number.isFinite(timestamp)) {
    issues.push(createContractIssue("x-timestamp", "douyin_signature_timestamp_invalid"));
  }
  if (Number.isFinite(timestamp) && nowMs !== null && maxClockSkewMs !== null) {
    const skewMs = Math.abs(Number(nowMs) - timestamp);
    if (skewMs > Number(maxClockSkewMs)) {
      issues.push(createContractIssue("x-timestamp", "douyin_signature_timestamp_out_of_window"));
    }
  }
  if (issues.length > 0) {
    return {
      ok: false,
      signature_valid: false,
      expected_signature: null,
      provided_signature: provided ?? null,
      issues
    };
  }

  const expected = createDouyinLiveDataSignature({
    headers: normalizedHeaders,
    body,
    secret
  });
  const valid = constantTimeEqual(expected, provided);
  return {
    ok: valid,
    signature_valid: valid,
    expected_signature: expected,
    provided_signature: provided,
    issues: valid ? [] : [createContractIssue("x-signature", "douyin_signature_mismatch")]
  };
}

export function normalizeDouyinLiveGiftPayload(payload = {}, {
  roomId = null,
  sourceDescriptor = createGiftSourceDescriptor({
    role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    source_id: "source_douyin_live_data"
  }),
  receivedAt = nowIso(),
  preferMagicGiftId = true
} = {}) {
  const giftCount = Number(payload.gift_num ?? payload.gift_count);
  const giftTotalValue = Number(payload.gift_value ?? payload.gift_total_value);
  const selectedGiftId = preferMagicGiftId && payload.sec_magic_gift_id
    ? payload.sec_magic_gift_id
    : payload.sec_gift_id ?? payload.gift_id;
  const eventTime = timestampToIso(payload.timestamp ?? payload.event_time) ?? receivedAt;
  const recipientUserId = payload.audience_sec_open_id ?? payload.recipient_user_id ?? null;
  return {
    source: sourceDescriptor.source,
    source_event_id: String(payload.msg_id ?? payload.source_event_id ?? ""),
    event_time: eventTime,
    observed_at: receivedAt,
    live_room_id: String(roomId ?? payload.roomid ?? payload.room_id ?? ""),
    gifter_user_id: String(payload.sec_openid ?? payload.gifter_user_id ?? payload.user_id ?? ""),
    gifter_display_name: payload.nickname ?? payload.gifter_display_name ?? payload.display_name ?? "Guest",
    gift_id: String(selectedGiftId ?? ""),
    gift_name: payload.gift_name ?? selectedGiftId ?? "gift",
    gift_unit_value: giftCount > 0 && Number.isFinite(giftTotalValue) ? giftTotalValue / giftCount : Number(payload.gift_unit_value ?? 0),
    gift_count: giftCount,
    gift_total_value: giftTotalValue,
    gift_combo_id: payload.gift_combo_id ?? null,
    combo_count: payload.combo_count ?? null,
    is_combo_final: payload.is_combo_final ?? false,
    audience_sec_open_id: recipientUserId,
    recipient_user_id: recipientUserId,
    recipient_source: recipientUserId ? "platform_audience_sec_open_id" : null,
    platform_payload: {
      platform: "douyin",
      msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT,
      msg_id: payload.msg_id ?? null,
      sec_gift_id: payload.sec_gift_id ?? null,
      sec_magic_gift_id: payload.sec_magic_gift_id ?? null,
      test: payload.test === true
    }
  };
}

export function validateDouyinLiveGiftPayload(payload = {}, {
  allowTestData = false
} = {}) {
  const issues = [];
  for (const field of ["msg_id", "sec_openid", "sec_gift_id", "timestamp"]) {
    if (payload[field] === undefined || payload[field] === null || String(payload[field]).trim() === "") {
      issues.push(createContractIssue(field, "douyin_gift_field_required"));
    }
  }
  const giftCount = Number(payload.gift_num);
  if (!Number.isFinite(giftCount) || giftCount <= 0) {
    issues.push(createContractIssue("gift_num", "douyin_gift_count_invalid"));
  }
  const giftValue = Number(payload.gift_value);
  if (!Number.isFinite(giftValue) || giftValue < 0) {
    issues.push(createContractIssue("gift_value", "douyin_gift_value_invalid"));
  }
  if (payload.timestamp !== undefined && timestampToIso(payload.timestamp) === null) {
    issues.push(createContractIssue("timestamp", "douyin_gift_timestamp_invalid"));
  }
  if (payload.test === true && allowTestData !== true) {
    issues.push(createContractIssue("test", "douyin_test_gift_filtered"));
  }
  return {
    ok: issues.length === 0,
    issues
  };
}

export function normalizeDouyinLiveGiftCallback({
  headers = {},
  body = "",
  secret,
  expectedRoomId = null,
  allowTestData = false,
  sourceDescriptor = createGiftSourceDescriptor({
    role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    source_id: "source_douyin_live_data"
  }),
  receivedAt = nowIso(),
  nowMs = null
} = {}) {
  const normalizedHeaders = normalizeDouyinHeaders(headers);
  const bodyText = bodyToUtf8String(body);
  const signature = verifyDouyinLiveDataSignature({
    headers: normalizedHeaders,
    body: bodyText,
    secret,
    nowMs
  });
  const parsed = parseJsonMaybe(bodyText);
  const issues = [];
  const rejected = [];
  const events = [];

  if (normalizedHeaders["x-msg-type"] !== DOUYIN_LIVE_DATA_MSG_TYPES.GIFT) {
    issues.push(createContractIssue("x-msg-type", "douyin_callback_msg_type_not_gift"));
  }
  if (expectedRoomId && normalizedHeaders["x-roomid"] && String(normalizedHeaders["x-roomid"]) !== String(expectedRoomId)) {
    issues.push(createContractIssue("x-roomid", "douyin_callback_room_mismatch"));
  }
  if (!signature.ok) {
    issues.push(...signature.issues);
  }
  if (parsed.error) {
    issues.push(createContractIssue("body", "douyin_callback_body_invalid_json"));
  }

  const payloads = extractDouyinPayloadList(parsed.value);
  payloads.forEach((payload, index) => {
    const validation = validateDouyinLiveGiftPayload(payload, {
      allowTestData
    });
    if (!validation.ok) {
      rejected.push({
        index,
        source_event_id: payload?.msg_id ?? null,
        issues: validation.issues
      });
      return;
    }
    events.push(normalizeDouyinLiveGiftPayload(payload, {
      roomId: normalizedHeaders["x-roomid"] ?? expectedRoomId,
      sourceDescriptor,
      receivedAt
    }));
  });

  const ackItems = payloads
    .map((payload) => payload?.msg_id)
    .filter(Boolean)
    .map((msgId) => createDouyinLiveDataAckItem({
      msg_id: msgId,
      msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT,
      client_time: Date.parse(receivedAt)
    }));

  return {
    ok: signature.ok && issues.length === 0 && rejected.length === 0,
    provider: "douyin_live_data",
    msg_type: normalizedHeaders["x-msg-type"] ?? null,
    room_id: normalizedHeaders["x-roomid"] ?? expectedRoomId ?? null,
    received_at: receivedAt,
    payload_count: payloads.length,
    event_count: events.length,
    rejected_count: rejected.length,
    signature,
    events,
    rejected,
    ack_items: ackItems,
    issues,
    audit_record: createDouyinLiveDataAuditRecord({
      headers: normalizedHeaders,
      body: bodyText,
      result: {
        signature,
        event_count: events.length,
        rejected_count: rejected.length,
        rejected,
        issues
      },
      receivedAt
    })
  };
}

export function normalizeDouyinFailedGiftDataPage(response = {}, {
  sourceDescriptor = createGiftSourceDescriptor({
    role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
    source_id: "source_douyin_failed_data"
  }),
  receivedAt = nowIso(),
  allowTestData = false
} = {}) {
  const rows = response?.data?.data_list ?? response?.data_list ?? [];
  const events = [];
  const rejected = [];
  rows.forEach((row, index) => {
    const payloads = extractDouyinPayloadList(row.payload ?? row.body ?? row.data ?? row);
    payloads.forEach((payload, payloadIndex) => {
      const validation = validateDouyinLiveGiftPayload(payload, { allowTestData });
      if (!validation.ok) {
        rejected.push({
          index,
          payload_index: payloadIndex,
          source_event_id: payload?.msg_id ?? null,
          issues: validation.issues
        });
        return;
      }
      events.push(normalizeDouyinLiveGiftPayload(payload, {
        roomId: row.roomid ?? row.room_id ?? payload.roomid,
        sourceDescriptor,
        receivedAt
      }));
    });
  });
  return {
    ok: rejected.length === 0,
    provider: "douyin_live_data_failed_query",
    page_num: response?.data?.page_num ?? response?.page_num ?? null,
    total_count: response?.data?.total_count ?? response?.total_count ?? rows.length,
    row_count: rows.length,
    event_count: events.length,
    rejected_count: rejected.length,
    events,
    rejected
  };
}

export function createDouyinLiveDataAckItem({
  msg_id,
  msg_type = DOUYIN_LIVE_DATA_MSG_TYPES.GIFT,
  client_time = Date.now()
} = {}) {
  return {
    msg_id: String(msg_id),
    msg_type,
    client_time: Number(client_time)
  };
}

export function createDouyinLiveDataAuditRecord({
  headers = {},
  body = "",
  result = {},
  receivedAt = nowIso()
} = {}) {
  const normalizedHeaders = normalizeDouyinHeaders(headers);
  const rejectedIssues = (result.rejected ?? []).flatMap((item) => item.issues ?? []);
  const issues = [...(result.issues ?? []), ...rejectedIssues];
  return {
    audit_id: createId("douyin_gift_audit"),
    provider: "douyin_live_data",
    received_at: receivedAt,
    room_id: normalizedHeaders["x-roomid"] ?? null,
    msg_type: normalizedHeaders["x-msg-type"] ?? null,
    signature_valid: result.signature?.signature_valid === true,
    raw_body_hash: hashRawGiftPayload(bodyToUtf8String(body)),
    accepted_count: Number(result.event_count ?? 0),
    rejected_count: Number(result.rejected_count ?? 0),
    issue_codes: [...new Set(issues.map((issue) => issue.reason_code))],
    raw_payload_included: false,
    local_only: true,
    customer_message: result.signature?.signature_valid === true ? "官方礼物记录已接收。" : "官方礼物记录需要检查。"
  };
}

export function createDouyinOfficialDataReadiness(input = {}) {
  const checks = [
    createReadinessCheck("app_id", Boolean(input.app_id), "配置抖音直播玩法 appid"),
    createReadinessCheck("access_token", Boolean(input.access_token), "配置服务端 access-token"),
    createReadinessCheck("callback_secret", Boolean(input.callback_secret), "配置回调签名秘钥"),
    createReadinessCheck("callback_url", Boolean(input.callback_url), "在开放平台配置回调地址"),
    createReadinessCheck("gift_task_running", input.gift_task_status === DOUYIN_LIVE_DATA_TASK_STATUS.RUNNING, "启动礼物数据推送任务"),
    createReadinessCheck("failed_query_ready", input.failed_query_ready === true, "开启失败数据分页回查"),
    createReadinessCheck("ack_enabled", input.ack_enabled === true, "开启收到和处理完成履约上报")
  ];
  const ready = checks.every((check) => check.ready);
  return {
    ok: ready,
    ready_for_commercial_live_data: ready,
    reliability: DOUYIN_LIVE_DATA_RELIABILITY,
    checks,
    next_actions: checks.filter((check) => !check.ready).map((check) => check.action),
    customer_message: ready ? "官方礼物数据接入已准备。" : "官方礼物数据接入需要继续配置。"
  };
}

export function createDouyinLiveDataBacktestReport({
  liveEvents = [],
  failedQueryEvents = [],
  officialReviewEvents = [],
  rejected = []
} = {}) {
  const recovered = mergeRealtimeSourceEvents({
    primaryEvents: liveEvents,
    backupEvents: failedQueryEvents
  });
  const review = createOfficialGiftReviewStatus({
    realtimeEvents: recovered.accepted,
    officialEvents: officialReviewEvents,
    expectedLiveRoomId: liveEvents[0]?.live_room_id ?? failedQueryEvents[0]?.live_room_id ?? officialReviewEvents[0]?.live_room_id ?? null
  });
  const unresolvedCount = review.realtime_only_count + review.official_only_count + review.adjustment_count + review.room_mismatch_count;
  return {
    ok: unresolvedCount === 0 && rejected.length === 0,
    provider: "douyin_live_data",
    platform_absolute_accuracy_guarantee: false,
    accuracy_contract: DOUYIN_LIVE_DATA_RELIABILITY.LEVEL,
    live_event_count: liveEvents.length,
    failed_query_recovered_count: failedQueryEvents.length,
    merged_event_count: recovered.accepted.length,
    duplicate_count: recovered.duplicates.length,
    mismatch_count: recovered.mismatches.length,
    rejected_count: rejected.length,
    official_review: review,
    unresolved_count: unresolvedCount,
    next_actions: unresolvedCount === 0 && rejected.length === 0
      ? ["保留本场审计记录，继续观察官方推送和回查成功率"]
      : ["回查失败数据分页", "导入下播官方复核记录", "复核被过滤或字段异常的礼物记录"]
  };
}

export function normalizeTiktokWebcastGiftPayload(
  payload = {},
  {
    sourceDescriptor = createGiftSourceDescriptor({
      role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
      source_id: "source_tiktok_webcast_primary"
    }),
    liveRoomId = null,
    provider = "tiktok_webcast",
    receivedAt = nowIso()
  } = {}
) {
  const shape = extractTiktokWebcastGiftShape(payload);
  const eventTime = normalizeTiktokEventTime(firstDefined(
    shape.event_time,
    shape.timestamp,
    shape.create_time,
    shape.createTime,
    shape.created_at,
    receivedAt
  ));
  const repeatCount = numberOrNull(firstDefined(
    shape.repeat_count,
    shape.repeatCount,
    shape.combo_count,
    shape.count
  ));
  const giftCount = numberOrNull(firstDefined(
    shape.gift_count,
    shape.giftNum,
    shape.gift_num,
    repeatCount,
    1
  )) ?? 1;
  const unitValue = numberOrNull(firstDefined(
    shape.gift_unit_value,
    shape.unit_value,
    shape.diamond_count,
    shape.diamondCount,
    shape.gift?.diamond_count,
    shape.gift?.diamondCount,
    shape.gift?.diamond,
    shape.gift_value,
    1
  )) ?? 1;
  const platformEventId = stringOrNull(firstDefined(
    shape.platform_event_id,
    shape.msg_id,
    shape.msgId,
    shape.message_id,
    shape.messageId,
    shape.id
  ));
  const repeatEnd = booleanOrNull(firstDefined(
    shape.repeat_end,
    shape.repeatEnd,
    shape.is_combo_final,
    shape.combo_end,
    shape.comboEnd
  ));
  const giftType = numberOrNull(firstDefined(shape.gift_type, shape.giftType, shape.gift?.type));
  const comboId = stringOrNull(firstDefined(
    shape.gift_combo_id,
    shape.combo_id,
    shape.comboId,
    shape.group_id,
    shape.groupId,
    shape.streak_id,
    shape.streakId,
    shape.repeat_id,
    shape.repeatId
  ));
  const user = shape.user ?? shape.from_user ?? shape.sender ?? {};
  const gift = shape.gift ?? {};
  const recipient = shape.recipient ?? shape.to_user ?? shape.receiver ?? {};
  const normalizedInput = {
    source: sourceDescriptor.source,
    platform_event_id: platformEventId,
    source_event_id: stringOrNull(firstDefined(shape.source_event_id, platformEventId)) ?? createTiktokSyntheticEventId({
      liveRoomId: firstDefined(shape.live_room_id, shape.room_id, shape.roomId, liveRoomId),
      userId: firstDefined(shape.gifter_user_id, shape.user_id, shape.userId, shape.user_id_str, user.user_id, user.userId, user.id),
      giftId: firstDefined(shape.gift_id, shape.giftId, gift.id, gift.gift_id, gift.giftId),
      eventTime,
      giftCount,
      repeatEnd
    }),
    event_time: eventTime,
    observed_at: receivedAt,
    live_room_id: stringOrNull(firstDefined(shape.live_room_id, shape.room_id, shape.roomId, shape.room?.id, liveRoomId)),
    gifter_user_id: stringOrNull(firstDefined(
      shape.gifter_user_id,
      shape.user_id,
      shape.userId,
      shape.user_id_str,
      user.user_id,
      user.userId,
      user.id,
      user.unique_id,
      user.uniqueId
    )),
    gifter_display_name: stringOrNull(firstDefined(
      shape.gifter_display_name,
      shape.nickname,
      shape.nickName,
      shape.unique_id,
      shape.uniqueId,
      user.nickname,
      user.nickName,
      user.unique_id,
      user.uniqueId
    )) ?? "Guest",
    gift_id: stringOrNull(firstDefined(shape.gift_id, shape.giftId, gift.id, gift.gift_id, gift.giftId)),
    gift_name: stringOrNull(firstDefined(shape.gift_name, shape.giftName, gift.name, gift.gift_name, gift.giftName, shape.gift_id, shape.giftId)),
    gift_unit_value: unitValue,
    gift_count: giftCount,
    gift_total_value: numberOrNull(firstDefined(shape.gift_total_value, shape.total_value, shape.totalValue)) ?? unitValue * giftCount,
    gift_combo_id: comboId,
    combo_count: repeatCount,
    is_combo_final: repeatEnd ?? (giftType === 1 ? false : Boolean(comboId && repeatCount)),
    audience_sec_open_id: stringOrNull(firstDefined(
      shape.audience_sec_open_id,
      shape.recipient_user_id,
      shape.to_user_id,
      recipient.user_id,
      recipient.userId,
      recipient.id
    )),
    recipient_user_id: stringOrNull(firstDefined(
      shape.recipient_user_id,
      shape.to_user_id,
      recipient.user_id,
      recipient.userId,
      recipient.id
    )),
    recipient_source: stringOrNull(firstDefined(shape.recipient_source, shape.to_user_source, recipient.source)),
    platform_payload: {
      platform: "tiktok",
      provider,
      msg_id: platformEventId,
      room_id: stringOrNull(firstDefined(shape.live_room_id, shape.room_id, shape.roomId, liveRoomId)),
      repeat_end: repeatEnd,
      gift_type: giftType,
      raw_payload_hash: hashRawGiftPayload(payload),
      raw_payload_included: false
    }
  };

  return normalizeGiftEvent(normalizedInput, sourceDescriptor);
}

export function validateTiktokWebcastGiftPayload(payload = {}, options = {}) {
  const normalized = normalizeTiktokWebcastGiftPayload(payload, options);
  const issues = [];
  const expectedLiveRoomId = options.liveRoomId ?? options.expectedLiveRoomId ?? null;
  const shape = extractTiktokWebcastGiftShape(payload);
  const platformEventId = stringOrNull(firstDefined(
    shape.platform_event_id,
    shape.msg_id,
    shape.msgId,
    shape.message_id,
    shape.messageId,
    shape.id,
    normalized.platform_event_id
  ));
  if (!platformEventId) {
    issues.push(createContractIssue("platform_event_id", "tiktok_platform_event_id_required"));
  }
  const bridgeValidation = validateLocalGiftBridgeEvent(normalized);
  issues.push(...bridgeValidation.issues);
  if (expectedLiveRoomId && normalized.live_room_id && normalized.live_room_id !== expectedLiveRoomId) {
    issues.push(createContractIssue("live_room_id", "tiktok_webcast_room_mismatch"));
  }
  return {
    ok: issues.length === 0,
    normalized,
    issues
  };
}

export function createTiktokWebcastCollectorReadiness(input = {}) {
  const checks = [
    createReadinessCheck("room_resolved", input.room_resolved === true || Boolean(input.live_room_id), "解析 TikTok 直播间 room id"),
    createReadinessCheck("primary_collector", input.primary_collector_ready === true, "启动主 Webcast/WebSocket 采集器"),
    createReadinessCheck("backup_collector", input.backup_collector_ready === true, "启动独立备用采集器或本地桥接备线"),
    createReadinessCheck("raw_backup", input.raw_backup_ready === true, "开启本地 raw backup"),
    createReadinessCheck("route_health", input.route_health_ready === true, "上报主备分角色心跳"),
    createReadinessCheck("after_live_import", input.after_live_import_ready === true, "准备下播复核导入")
  ];
  const ready = checks.every((check) => check.ready);
  return {
    ok: ready,
    ready_for_tiktok_live_trial: ready,
    platform: "tiktok",
    realtime_data_entry: "webcast_websocket",
    live_studio_role: "broadcast_and_overlay_guardian",
    checks,
    next_actions: checks.filter((check) => !check.ready).map((check) => check.action),
    customer_message: ready ? "TikTok 礼物数据采集准备完成。" : "TikTok 礼物数据采集还需要继续配置。"
  };
}

export function evaluateTiktokGiftRouteResilience({
  sources = [],
  primaryEvents = [],
  backupEvents = [],
  event = null,
  recentEvents = [],
  sensitivity = GAMEPLAY_SENSITIVITY.HIGH,
  expectedLiveRoomId = null,
  bigGiftValueThreshold = 100000,
  highFrequencyEventsPerSecondThreshold = 20
} = {}) {
  const route = selectGiftSourceRoute({ sources, sensitivity });
  const merged = mergeRealtimeSourceEvents({ primaryEvents, backupEvents });
  const gift = event ?? merged.accepted[0] ?? null;
  const risk = classifyTiktokGiftRisk(gift, {
    recentEvents,
    bigGiftValueThreshold,
    highFrequencyEventsPerSecondThreshold,
    mismatches: merged.mismatches
  });
  const giftMatchKey = gift ? (gift.match_key ?? makeGiftMatchKey(gift)) : null;
  const backupConfirmed = giftMatchKey
    ? merged.confirmations.some((confirmation) => confirmation.match_key === giftMatchKey || confirmation.idempotency_key === gift?.idempotency_key)
      || merged.accepted.some((candidate) => (candidate.match_key ?? makeGiftMatchKey(candidate)) === giftMatchKey && (candidate.source_confirmations ?? []).length > 1)
    : false;
  const routeIssues = [];
  if (expectedLiveRoomId && gift?.live_room_id && gift.live_room_id !== expectedLiveRoomId) {
    routeIssues.push(createContractIssue("live_room_id", "tiktok_room_mismatch"));
  }
  if (route.decision === GIFT_ROUTE_DECISIONS.NEEDS_REVIEW) {
    routeIssues.push(createContractIssue("route", "tiktok_no_realtime_route_available"));
  }
  if (route.decision === GIFT_ROUTE_DECISIONS.WAIT_FOR_CONFIRMATION) {
    routeIssues.push(createContractIssue("route", "tiktok_route_confirmation_required"));
  }
  if (risk.risk_reasons.includes("big_gift") && !backupConfirmed && route.decision !== GIFT_ROUTE_DECISIONS.USE_BACKUP) {
    routeIssues.push(createContractIssue("backup_confirmation", "tiktok_big_gift_backup_confirmation_required"));
  }
  if (risk.risk_reasons.includes("big_gift") && route.decision === GIFT_ROUTE_DECISIONS.USE_BACKUP) {
    routeIssues.push(createContractIssue("after_live_review", "tiktok_big_gift_backup_takeover_requires_review"));
  }
  if (risk.risk_reasons.includes("combo_in_progress")) {
    routeIssues.push(createContractIssue("repeat_end", "tiktok_combo_wait_repeat_end"));
  }
  if (risk.risk_reasons.includes("high_frequency")) {
    routeIssues.push(createContractIssue("event_rate", "tiktok_high_frequency_review_required"));
  }
  if (risk.risk_reasons.includes("mismatch")) {
    routeIssues.push(createContractIssue("mismatch", "tiktok_primary_backup_mismatch"));
  }

  let decision = TIKTOK_GIFT_RESILIENCE_DECISIONS.MANUAL_REVIEW_REQUIRED;
  let canDriveLiveScore = false;
  if (routeIssues.some((issue) => issue.reason_code === "tiktok_primary_backup_mismatch")) {
    decision = TIKTOK_GIFT_RESILIENCE_DECISIONS.MANUAL_REVIEW_REQUIRED;
  } else if (routeIssues.some((issue) => [
    "tiktok_big_gift_backup_confirmation_required",
    "tiktok_combo_wait_repeat_end",
    "tiktok_route_confirmation_required"
  ].includes(issue.reason_code))) {
    decision = TIKTOK_GIFT_RESILIENCE_DECISIONS.HOLD_FOR_CONFIRMATION;
  } else if (route.decision === GIFT_ROUTE_DECISIONS.USE_PRIMARY) {
    decision = TIKTOK_GIFT_RESILIENCE_DECISIONS.PRIMARY_ACTIVE;
    canDriveLiveScore = true;
  } else if (route.decision === GIFT_ROUTE_DECISIONS.USE_BACKUP) {
    decision = TIKTOK_GIFT_RESILIENCE_DECISIONS.BACKUP_TAKEOVER;
    canDriveLiveScore = true;
  } else if (route.decision === GIFT_ROUTE_DECISIONS.NEEDS_REVIEW) {
    decision = TIKTOK_GIFT_RESILIENCE_DECISIONS.AFTER_LIVE_BACKFILL_REQUIRED;
  }

  return {
    ok: canDriveLiveScore && routeIssues.length === 0,
    decision,
    can_drive_live_score: canDriveLiveScore,
    active_source_id: route.active_source_id,
    active_role: route.decision === GIFT_ROUTE_DECISIONS.USE_BACKUP
      ? GIFT_SOURCE_ROLES.BACKUP_REALTIME
      : route.decision === GIFT_ROUTE_DECISIONS.USE_PRIMARY
        ? GIFT_SOURCE_ROLES.PRIMARY_REALTIME
        : null,
    route,
    risk,
    backup_confirmed: backupConfirmed,
    requires_backup_confirmation: risk.risk_reasons.includes("big_gift"),
    needs_after_live_review: decision !== TIKTOK_GIFT_RESILIENCE_DECISIONS.PRIMARY_ACTIVE || risk.risk_reasons.length > 0,
    merge: {
      accepted_count: merged.accepted.length,
      confirmation_count: merged.confirmations.length,
      mismatch_count: merged.mismatches.length
    },
    issues: routeIssues,
    next_actions: tiktokResilienceActions(decision, routeIssues)
  };
}

export function createTiktokGiftBackfillPlan({
  primaryEvents = [],
  backupEvents = [],
  officialEvents = [],
  expectedLiveRoomId = null,
  generatedAt = nowIso()
} = {}) {
  const merged = mergeRealtimeSourceEvents({ primaryEvents, backupEvents });
  const realtimeEvents = merged.accepted;
  const review = createOfficialGiftReviewStatus({
    realtimeEvents,
    officialEvents,
    expectedLiveRoomId,
    generatedAt
  });
  const primaryByKey = eventsByMatchKey(primaryEvents);
  const backupByKey = eventsByMatchKey(backupEvents);
  const realtimeByKey = eventsByReviewKey(realtimeEvents);
  const officialByKey = eventsByReviewKey(officialEvents);
  const primaryKeys = new Set(primaryByKey.keys());
  const backupKeys = new Set(backupByKey.keys());
  const realtimeKeys = new Set(realtimeByKey.keys());
  const officialKeys = new Set(officialByKey.keys());
  const primaryOnlyKeys = [...primaryKeys].filter((key) => !backupKeys.has(key));
  const backupOnlyKeys = [...backupKeys].filter((key) => !primaryKeys.has(key));
  const officialOnlyKeys = [...officialKeys].filter((key) => !realtimeKeys.has(key));
  const realtimeOnlyKeys = [...realtimeKeys].filter((key) => !officialKeys.has(key));
  const adjustmentKeys = [...officialKeys].filter((key) => {
    if (!realtimeByKey.has(key)) {
      return false;
    }
    const realtime = realtimeByKey.get(key);
    const official = officialByKey.get(key);
    return Number(realtime.gift_total_value) !== Number(official.gift_total_value)
      || Number(realtime.gift_count) !== Number(official.gift_count);
  });
  const corrections = [
    ...backupOnlyKeys.map((key) => createTiktokBackfillCorrection({
      action: TIKTOK_GIFT_BACKFILL_ACTIONS.ACCEPT_BACKUP_TAKEOVER,
      reason_code: "primary_missed_backup_record",
      event: backupByKey.get(key),
      matchKey: key,
      generatedAt
    })),
    ...officialOnlyKeys.map((key) => createTiktokBackfillCorrection({
      action: TIKTOK_GIFT_BACKFILL_ACTIONS.IMPORT_AFTER_LIVE,
      reason_code: "realtime_missed_official_record",
      event: officialByKey.get(key),
      matchKey: key,
      generatedAt
    })),
    ...adjustmentKeys.map((key) => createTiktokBackfillCorrection({
      action: TIKTOK_GIFT_BACKFILL_ACTIONS.CREATE_ADJUSTMENT,
      reason_code: "official_adjusts_realtime_value",
      event: officialByKey.get(key),
      realtimeEvent: realtimeByKey.get(key),
      matchKey: key,
      generatedAt
    })),
    ...realtimeOnlyKeys.map((key) => createTiktokBackfillCorrection({
      action: TIKTOK_GIFT_BACKFILL_ACTIONS.MANUAL_REVIEW,
      reason_code: "realtime_record_missing_after_live_review",
      event: realtimeByKey.get(key),
      matchKey: key,
      generatedAt
    }))
  ];
  const unresolvedCount = review.realtime_only_count
    + review.official_only_count
    + review.adjustment_count
    + review.room_mismatch_count
    + merged.mismatches.length;

  return {
    ok: unresolvedCount === 0,
    platform: "tiktok",
    generated_at: generatedAt,
    expected_live_room_id: expectedLiveRoomId,
    primary_count: primaryEvents.length,
    backup_count: backupEvents.length,
    merged_realtime_count: realtimeEvents.length,
    official_count: officialEvents.length,
    primary_only_count: primaryOnlyKeys.length,
    backup_only_count: backupOnlyKeys.length,
    confirmed_realtime_count: merged.confirmations.length,
    mismatch_count: merged.mismatches.length,
    after_live_recovered_count: officialOnlyKeys.length,
    adjustment_count: adjustmentKeys.length,
    unresolved_count: unresolvedCount,
    ready_for_settlement: unresolvedCount === 0 && officialEvents.length > 0,
    review,
    corrections,
    next_actions: tiktokBackfillActions({
      officialCount: officialEvents.length,
      backupOnlyCount: backupOnlyKeys.length,
      officialOnlyCount: officialOnlyKeys.length,
      adjustmentCount: adjustmentKeys.length,
      realtimeOnlyCount: realtimeOnlyKeys.length,
      mismatchCount: merged.mismatches.length,
      roomMismatchCount: review.room_mismatch_count
    })
  };
}

export function createTiktokGiftResilienceBacktestReport({
  primaryEvents = [],
  backupEvents = [],
  officialEvents = [],
  rejected = [],
  routeReports = [],
  chaosScenarios = [],
  requiredChaosScenarioIds = [],
  expectedLiveRoomId = null,
  generatedAt = nowIso()
} = {}) {
  const backfill = createTiktokGiftBackfillPlan({
    primaryEvents,
    backupEvents,
    officialEvents,
    expectedLiveRoomId,
    generatedAt
  });
  const routeReportReady = routeReports.length > 0
    && routeReports.every((report) => {
      return report.decision !== TIKTOK_GIFT_RESILIENCE_DECISIONS.MANUAL_REVIEW_REQUIRED
        && report.decision !== TIKTOK_GIFT_RESILIENCE_DECISIONS.AFTER_LIVE_BACKFILL_REQUIRED
        && (report.can_drive_live_score || report.decision === TIKTOK_GIFT_RESILIENCE_DECISIONS.HOLD_FOR_CONFIRMATION);
    });
  const chaosIds = new Set(chaosScenarios.map((scenario) => scenario.scenario_id).filter(Boolean));
  const requiredChaosReady = requiredChaosScenarioIds.every((scenarioId) => chaosIds.has(scenarioId));
  const chaosReady = chaosScenarios.length > 0
    && chaosScenarios.every((scenario) => scenario.ok === true)
    && requiredChaosReady;
  const gates = [
    createBacktestGate("primary_backup_merge", backfill.mismatch_count === 0, "主备礼物记录没有冲突"),
    createBacktestGate("after_live_review", backfill.ready_for_settlement, "下播复核记录和直播记录一致"),
    createBacktestGate("route_resilience", routeReportReady, "主备路由可切换或可安全等待"),
    createBacktestGate("chaos_scenarios", chaosReady, "复杂网络场景全部通过"),
    createBacktestGate("rejected_records", rejected.length === 0, "没有被拒绝的礼物记录")
  ];
  const failedGates = gates.filter((gate) => !gate.ok);
  const score = failedGates.length === 0
    ? 100
    : Math.max(0, 100 - failedGates.length * 20 - backfill.unresolved_count * 5 - rejected.length * 5);
  return {
    ok: score === 100,
    platform: "tiktok",
    score,
    generated_at: generatedAt,
    primary_event_count: primaryEvents.length,
    backup_event_count: backupEvents.length,
    official_event_count: officialEvents.length,
    rejected_count: rejected.length,
    route_report_count: routeReports.length,
    chaos_scenario_count: chaosScenarios.length,
    required_chaos_scenario_ids: requiredChaosScenarioIds,
    gates,
    failed_gate_count: failedGates.length,
    backfill,
    route_reports: routeReports,
    chaos_scenarios: chaosScenarios,
    next_actions: score === 100
      ? ["保留本场审计记录，继续双线路监控"]
      : [...new Set([
          ...failedGates.map((gate) => gate.action),
          ...backfill.next_actions
        ])]
  };
}

export function createTiktokWebcastCollectorConfig(input = {}) {
  const reconnect = input.reconnect ?? {};
  const standby = input.standby ?? {};
  const env = typeof process !== "undefined" ? process.env ?? {} : {};
  return {
    collector_id: input.collector_id ?? createId("tiktok_webcast_collector"),
    platform: "tiktok",
    provider: input.provider ?? "tiktok_webcast",
    role: input.role ?? GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    creator_handle: sanitizeTiktokHandle(input.creator_handle ?? input.unique_id ?? input.username ?? null),
    live_room_url: input.live_room_url ?? input.url ?? null,
    live_room_id: input.live_room_id ?? input.room_id ?? null,
    websocket_url: input.websocket_url ?? input.webSocketUrl ?? input.ws_url ?? null,
    websocket_url_template: input.websocket_url_template ?? input.ws_url_template ?? null,
    signer_url: input.signer_url ?? input.signerUrl ?? null,
    signer_headers: input.signer_headers ?? {},
    signer_timeout_ms: Number(input.signer_timeout_ms ?? input.signerTimeoutMs ?? 10000),
    allowed_websocket_hosts: input.allowed_websocket_hosts ?? input.allowedWebsocketHosts ?? [],
    session_cookie: input.session_cookie ?? null,
    tt_target_idc: input.tt_target_idc ?? input.ttTargetIdc ?? null,
    authenticate_ws: input.authenticate_ws ?? input.authenticateWs ?? false,
    user_agent: input.user_agent ?? TIKTOK_WEBCAST_DEFAULT_USER_AGENT,
    protocol: input.protocol ?? null,
    headers: input.headers ?? {},
    cursor: input.cursor ?? null,
    heartbeat_interval_ms: Number(input.heartbeat_interval_ms ?? 5000),
    connect_timeout_ms: Number(input.connect_timeout_ms ?? 15000),
    auto_connector_enabled: input.auto_connector_enabled ?? input.autoConnectorEnabled ?? true,
    connector_provider: input.connector_provider ?? input.connectorProvider ?? "tiktok-live-connector",
    connector_process_initial_data: input.connector_process_initial_data ?? input.connectorProcessInitialData ?? false,
    connector_fetch_room_info_on_connect: input.connector_fetch_room_info_on_connect ?? input.connectorFetchRoomInfoOnConnect ?? false,
    connector_enable_extended_gift_info: input.connector_enable_extended_gift_info ?? input.connectorEnableExtendedGiftInfo ?? false,
    connector_enable_request_polling: input.connector_enable_request_polling ?? input.connectorEnableRequestPolling ?? true,
    connector_request_polling_interval_ms: Number(input.connector_request_polling_interval_ms ?? input.connectorRequestPollingIntervalMs ?? 1000),
    connector_connect_with_unique_id: input.connector_connect_with_unique_id ?? input.connectorConnectWithUniqueId ?? false,
    connector_disable_euler_fallbacks: input.connector_disable_euler_fallbacks ?? input.connectorDisableEulerFallbacks ?? false,
    connector_log_fetch_fallback_errors: input.connector_log_fetch_fallback_errors ?? input.connectorLogFetchFallbackErrors ?? false,
    connector_sign_api_key: input.connector_sign_api_key ?? input.sign_api_key ?? env.LIVE_RUSH_TIKTOK_SIGN_API_KEY ?? env.SIGN_API_KEY ?? null,
    connector_sign_api_url: input.connector_sign_api_url ?? input.sign_api_url ?? env.LIVE_RUSH_TIKTOK_SIGN_API_URL ?? env.SIGN_API_URL ?? null,
    connector_signed_websocket_provider: input.connector_signed_websocket_provider ?? input.signedWebSocketProvider ?? null,
    connector_web_client_params: input.connector_web_client_params ?? input.webClientParams ?? {},
    connector_web_client_headers: input.connector_web_client_headers ?? input.webClientHeaders ?? {},
    connector_web_client_options: input.connector_web_client_options ?? input.webClientOptions ?? {},
    connector_ws_client_params: input.connector_ws_client_params ?? input.wsClientParams ?? {},
    connector_ws_client_headers: input.connector_ws_client_headers ?? input.wsClientHeaders ?? {},
    connector_ws_client_options: input.connector_ws_client_options ?? input.wsClientOptions ?? {},
    big_gift_value_threshold: Number(input.big_gift_value_threshold ?? input.bigGiftValueThreshold ?? 100000),
    high_frequency_events_per_second_threshold: Number(input.high_frequency_events_per_second_threshold ?? input.highFrequencyEventsPerSecondThreshold ?? 20),
    reconnect: {
      enabled: reconnect.enabled ?? true,
      max_attempts: Number(reconnect.max_attempts ?? input.max_reconnect_attempts ?? 5),
      max_total_attempts: Number(reconnect.max_total_attempts ?? input.max_total_reconnect_attempts ?? 50),
      base_delay_ms: Number(reconnect.base_delay_ms ?? reconnect.backoff_ms ?? input.reconnect_base_delay_ms ?? 500),
      max_delay_ms: Number(reconnect.max_delay_ms ?? input.reconnect_max_delay_ms ?? 5000)
    },
    standby: {
      enabled: standby.enabled ?? input.standby_enabled ?? true,
      poll_interval_ms: Number(standby.poll_interval_ms ?? input.standby_poll_interval_ms ?? 5000),
      max_wait_ms: Number(standby.max_wait_ms ?? input.standby_max_wait_ms ?? 0),
      retry_delay_ms_by_reason_code: standby.retry_delay_ms_by_reason_code
        ?? standby.retryDelayMsByReasonCode
        ?? input.standby_retry_delay_ms_by_reason_code
        ?? input.standbyRetryDelayMsByReasonCode
        ?? {},
      retry_reason_codes: standby.retry_reason_codes ?? input.standby_retry_reason_codes ?? [
        "tiktok_live_room_id_not_found",
        "tiktok_live_room_fetch_failed",
        "tiktok_live_room_fetch_blocked",
        "tiktok_live_room_http_error",
        "tiktok_webcast_signer_failed",
        "tiktok_live_connector_connect_failed",
        "tiktok_live_connector_room_not_live",
        "tiktok_live_connector_signer_failed",
        "tiktok_live_connector_signer_rate_limited",
        "tiktok_live_connector_fetch_blocked",
        "tiktok_live_connector_timeout",
        "tiktok_live_connector_stream_end",
        "tiktok_live_connector_disconnected"
      ]
    },
    raw_backup_required: input.raw_backup_required ?? true,
    local_only: true,
    created_at: input.created_at ?? nowIso()
  };
}

export function createTiktokBrowserObserverCollectorConfig(input = {}) {
  const reconnect = input.reconnect ?? {};
  const base = createTiktokWebcastCollectorConfig({
    provider: "tiktok_browser_observer",
    role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
    ...input
  });
  const browser = input.browser ?? {};
  const browserKeepVisible = browser.keep_visible ?? browser.keepVisible ?? input.browser_keep_visible ?? input.browserKeepVisible ?? false;
  const attachAuxiliaryTargets = browser.attach_auxiliary_targets
    ?? browser.attachAuxiliaryTargets
    ?? input.browser_attach_auxiliary_targets
    ?? input.browserAttachAuxiliaryTargets
    ?? false;
  const runtimeCaptureEnabled = browser.runtime_capture_enabled
    ?? browser.runtimeCaptureEnabled
    ?? input.browser_runtime_capture_enabled
    ?? input.browserRuntimeCaptureEnabled
    ?? false;
  const remoteDebuggingPort = Number(browser.remote_debugging_port
    ?? browser.remoteDebuggingPort
    ?? input.remote_debugging_port
    ?? input.remoteDebuggingPort
    ?? 9223);
  const browserLaunchArgs = browser.launch_args ?? browser.launchArgs ?? input.browser_launch_args ?? input.browserLaunchArgs ?? [];
  const browserHeadless = booleanOrNull(browser.headless ?? browser.browser_headless ?? input.browser_headless ?? input.browserHeadless)
    ?? hasHeadlessBrowserLaunchArg(browserLaunchArgs);
  const explicitLiveRoomUrl = stringOrNull(base.live_room_url);
  const tiktokLiveRoomUrl = isTiktokUrl(explicitLiveRoomUrl) ? explicitLiveRoomUrl : null;
  const creatorHandle = sanitizeTiktokHandle(base.creator_handle ?? input.unique_id ?? input.username ?? null)
    ?? sanitizeTiktokHandleFromTiktokUrl(tiktokLiveRoomUrl);
  const liveRoomUrl = tiktokLiveRoomUrl ?? (creatorHandle ? `https://www.tiktok.com/@${creatorHandle}/live` : null);
  return {
    ...base,
    provider: "tiktok_browser_observer",
    creator_handle: creatorHandle,
    live_room_url: liveRoomUrl,
    invalid_live_room_url: explicitLiveRoomUrl && !tiktokLiveRoomUrl ? explicitLiveRoomUrl : null,
    browser_executable_path: browser.executable_path ?? browser.executablePath ?? input.browser_executable_path ?? input.browserExecutablePath ?? null,
    browser_user_data_dir: browser.user_data_dir ?? browser.userDataDir ?? input.browser_user_data_dir ?? input.browserUserDataDir ?? defaultTiktokBrowserObserverUserDataDir(),
    browser_launch_args: browserLaunchArgs,
    browser_headless: browserHeadless,
    browser_interactive_recovery_available: browserHeadless !== true,
    browser_close_on_stop: browser.close_on_stop ?? browser.closeOnStop ?? input.browser_close_on_stop ?? input.browserCloseOnStop ?? false,
    browser_open_as_app: browser.open_as_app ?? browser.openAsApp ?? input.browser_open_as_app ?? input.browserOpenAsApp ?? true,
    browser_keep_visible: browserKeepVisible,
    browser_auto_minimize_after_live: browser.auto_minimize_after_live
      ?? browser.autoMinimizeAfterLive
      ?? input.browser_auto_minimize_after_live
      ?? input.browserAutoMinimizeAfterLive
      ?? !browserKeepVisible,
    remote_debugging_port: remoteDebuggingPort,
    cdp_http_url: browser.cdp_http_url ?? browser.cdpHttpUrl ?? input.cdp_http_url ?? input.cdpHttpUrl ?? `http://127.0.0.1:${remoteDebuggingPort}`,
    cdp_websocket_url: browser.cdp_websocket_url ?? browser.cdpWebSocketUrl ?? input.cdp_websocket_url ?? input.cdpWebSocketUrl ?? null,
    cdp_command_timeout_ms: Number(browser.cdp_command_timeout_ms ?? browser.cdpCommandTimeoutMs ?? input.cdp_command_timeout_ms ?? input.cdpCommandTimeoutMs ?? 10000),
    cdp_attach_timeout_ms: Number(browser.cdp_attach_timeout_ms ?? browser.cdpAttachTimeoutMs ?? input.cdp_attach_timeout_ms ?? input.cdpAttachTimeoutMs ?? 15000),
    browser_attach_auxiliary_targets: Boolean(attachAuxiliaryTargets),
    browser_auxiliary_target_types: browser.auxiliary_target_types
      ?? browser.auxiliaryTargetTypes
      ?? input.browser_auxiliary_target_types
      ?? input.browserAuxiliaryTargetTypes
      ?? ["service_worker", "worker", "shared_worker"],
    browser_accept_unmapped_auxiliary_frames: browser.accept_unmapped_auxiliary_frames
      ?? browser.acceptUnmappedAuxiliaryFrames
      ?? input.browser_accept_unmapped_auxiliary_frames
      ?? input.browserAcceptUnmappedAuxiliaryFrames
      ?? Boolean(attachAuxiliaryTargets),
    browser_auto_attach_child_targets: browser.auto_attach_child_targets
      ?? browser.autoAttachChildTargets
      ?? input.browser_auto_attach_child_targets
      ?? input.browserAutoAttachChildTargets
      ?? Boolean(attachAuxiliaryTargets),
    browser_preload_runtime_capture: browser.preload_runtime_capture
      ?? browser.preloadRuntimeCapture
      ?? input.browser_preload_runtime_capture
      ?? input.browserPreloadRuntimeCapture
      ?? Boolean(runtimeCaptureEnabled),
    browser_target_poll_interval_ms: Number(browser.target_poll_interval_ms
      ?? browser.targetPollIntervalMs
      ?? input.browser_target_poll_interval_ms
      ?? input.browserTargetPollIntervalMs
      ?? 2000),
    relevant_websocket_url_patterns: browser.relevant_websocket_url_patterns
      ?? browser.relevantWebSocketUrlPatterns
      ?? input.relevant_websocket_url_patterns
      ?? input.relevantWebSocketUrlPatterns
      ?? ["webcast", "im-ws", "frontier", "bsync", "tiktok"],
    require_relevant_websocket_url: browser.require_relevant_websocket_url
      ?? browser.requireRelevantWebSocketUrl
      ?? input.require_relevant_websocket_url
      ?? input.requireRelevantWebSocketUrl
      ?? false,
    browser_runtime_capture_enabled: Boolean(runtimeCaptureEnabled),
    browser_runtime_binding_name: browser.runtime_binding_name
      ?? browser.runtimeBindingName
      ?? input.browser_runtime_binding_name
      ?? input.browserRuntimeBindingName
      ?? "__liveRushTiktokCapture",
    browser_polling_response_url_patterns: browser.polling_response_url_patterns
      ?? browser.pollingResponseUrlPatterns
      ?? input.browser_polling_response_url_patterns
      ?? input.browserPollingResponseUrlPatterns
      ?? ["webcast/im/fetch", "/webcast/im/fetch", "webcast/fetch", "webcast/im", "/webcast/", "live/im", "im/fetch", "im/push", "frontier", "bsync"],
    browser_live_event_url_patterns: browser.live_event_url_patterns
      ?? browser.liveEventUrlPatterns
      ?? input.browser_live_event_url_patterns
      ?? input.browserLiveEventUrlPatterns
      ?? ["webcast/im/fetch", "/webcast/im/fetch", "webcast/im/push", "/webcast/im/push", "im/fetch", "im/push", "im-ws"],
    browser_non_event_url_patterns: browser.non_event_url_patterns
      ?? browser.nonEventUrlPatterns
      ?? input.browser_non_event_url_patterns
      ?? input.browserNonEventUrlPatterns
      ?? ["webcast/feed", "/webcast/feed", "room/check_alive", "game_feed", "epiphron/feature", "bsync/cache"],
    browser_page_health_check_enabled: browser.page_health_check_enabled
      ?? browser.pageHealthCheckEnabled
      ?? input.browser_page_health_check_enabled
      ?? input.browserPageHealthCheckEnabled
      ?? true,
    browser_page_health_check_interval_ms: Number(browser.page_health_check_interval_ms
      ?? browser.pageHealthCheckIntervalMs
      ?? input.browser_page_health_check_interval_ms
      ?? input.browserPageHealthCheckIntervalMs
      ?? 15000),
    browser_page_blocked_text_patterns: browser.page_blocked_text_patterns
      ?? browser.pageBlockedTextPatterns
      ?? input.browser_page_blocked_text_patterns
      ?? input.browserPageBlockedTextPatterns
      ?? [
        "verification timed out",
        "please close this window",
        "verify to continue",
        "verification required",
        "captcha",
        "unusual traffic",
        "access denied",
        "couldn't load live",
        "could not load live",
        "login to continue",
        "log in to continue"
      ],
    reconnect: {
      enabled: reconnect.enabled ?? base.reconnect.enabled,
      max_attempts: Number(reconnect.max_attempts ?? input.max_reconnect_attempts ?? 20),
      max_total_attempts: Number(reconnect.max_total_attempts ?? input.max_total_reconnect_attempts ?? 200),
      base_delay_ms: Number(reconnect.base_delay_ms ?? reconnect.backoff_ms ?? input.reconnect_base_delay_ms ?? 1000),
      max_delay_ms: Number(reconnect.max_delay_ms ?? input.reconnect_max_delay_ms ?? 10000)
    },
    local_only: true
  };
}

export function hasTiktokWebcastManagedConnectionTarget(config = {}) {
  return Boolean(config.websocket_url || config.webSocketUrl || config.ws_url
    || config.websocket_url_template || config.ws_url_template
    || config.signer_url || config.signerUrl);
}

export function shouldUseTiktokLiveConnector(config = {}) {
  const collectorConfig = createTiktokWebcastCollectorConfig(config);
  return collectorConfig.auto_connector_enabled !== false
    && !hasTiktokWebcastManagedConnectionTarget(collectorConfig)
    && Boolean(deriveTiktokConnectorUniqueId(collectorConfig));
}

export async function resolveTiktokLiveRoomId({
  creatorHandle = null,
  liveRoomUrl = null,
  html = null,
  fetchImpl = globalThis.fetch,
  userAgent = TIKTOK_WEBCAST_DEFAULT_USER_AGENT
} = {}) {
  if (html) {
    return extractTiktokLiveRoomIdFromHtml(html);
  }
  const explicitLiveRoomUrl = stringOrNull(liveRoomUrl);
  if (explicitLiveRoomUrl && !isTiktokUrl(explicitLiveRoomUrl)) {
    return {
      ok: false,
      reason_code: "tiktok_live_room_url_not_tiktok",
      live_room_id: null,
      live_room_url: explicitLiveRoomUrl
    };
  }
  const targetUrl = explicitLiveRoomUrl ?? (creatorHandle ? `https://www.tiktok.com/@${sanitizeTiktokHandle(creatorHandle)}/live` : null);
  if (!targetUrl) {
    return {
      ok: false,
      reason_code: "tiktok_live_room_target_required",
      live_room_id: null
    };
  }
  if (!fetchImpl) {
    return {
      ok: false,
      reason_code: "fetch_not_available",
      live_room_id: null,
      live_room_url: targetUrl
    };
  }
  try {
    const response = await fetchImpl(targetUrl, {
      headers: {
        "user-agent": userAgent
      }
    });
    const text = await response.text();
    const extracted = extractTiktokLiveRoomIdFromHtml(text);
    if (extracted.ok || !response.status || response.status < 400) {
      return {
        ...extracted,
        live_room_url: targetUrl,
        http_status: response.status ?? null
      };
    }
    return {
      ...extracted,
      reason_code: response.status === 403 || response.status === 429
        ? "tiktok_live_room_fetch_blocked"
        : "tiktok_live_room_http_error",
      live_room_url: targetUrl,
      http_status: response.status ?? null
    };
  } catch {
    return {
      ok: false,
      reason_code: "tiktok_live_room_fetch_failed",
      live_room_id: null,
      live_room_url: targetUrl
    };
  }
}

export function extractTiktokLiveRoomIdFromHtml(html = "") {
  const text = String(html ?? "");
  const patterns = [
    /"roomId"\s*:\s*"(\d+)"/,
    /"room_id"\s*:\s*"(\d+)"/,
    /"room_id"\s*:\s*(\d+)/,
    /"roomID"\s*:\s*"(\d+)"/,
    /room_id=(\d+)/,
    /webcast\/room\/info\/\?room_id=(\d+)/
  ];
  for (const pattern of patterns) {
    const match = text.match(pattern);
    if (match?.[1]) {
      return {
        ok: true,
        live_room_id: match[1],
        reason_code: "tiktok_live_room_id_found"
      };
    }
  }
  const sigmaMatch = text.match(/SIGI_STATE__"\s*>\s*({[\s\S]*?})\s*<\/script>/);
  if (sigmaMatch?.[1]) {
    try {
      const parsed = JSON.parse(sigmaMatch[1]);
      const found = findNestedRoomId(parsed);
      if (found) {
        return {
          ok: true,
          live_room_id: found,
          reason_code: "tiktok_live_room_id_found"
        };
      }
    } catch {
      // Fall through to the not-found response.
    }
  }
  return {
    ok: false,
    live_room_id: null,
    reason_code: "tiktok_live_room_id_not_found"
  };
}

export async function createTiktokWebcastConnectionTarget({
  config = createTiktokWebcastCollectorConfig(),
  fetchImpl = globalThis.fetch
} = {}) {
  const collectorConfig = createTiktokWebcastCollectorConfig(config);
  let liveRoomId = collectorConfig.live_room_id;
  if (!liveRoomId) {
    const room = await resolveTiktokLiveRoomId({
      creatorHandle: collectorConfig.creator_handle,
      liveRoomUrl: collectorConfig.live_room_url,
      fetchImpl,
      userAgent: collectorConfig.user_agent
    });
    if (!room.ok) {
      return {
        ok: false,
        ...room,
        live_room_id: null
      };
    }
    liveRoomId = room.live_room_id;
  }

  if (collectorConfig.websocket_url) {
    const validation = validateTiktokWebcastTargetUrl({
      websocketUrl: collectorConfig.websocket_url,
      liveRoomId,
      allowedHosts: collectorConfig.allowed_websocket_hosts
    });
    if (!validation.ok) {
      return {
        ok: false,
        reason_code: validation.reason_code,
        live_room_id: liveRoomId
      };
    }
    return {
      ok: true,
      provider: collectorConfig.provider,
      live_room_id: liveRoomId,
      websocket_url: collectorConfig.websocket_url,
      headers: collectorConfig.headers,
      protocol: collectorConfig.protocol,
      source: "configured_websocket_url"
    };
  }

  if (collectorConfig.websocket_url_template) {
    const websocketUrl = fillTiktokWebcastUrlTemplate(collectorConfig.websocket_url_template, {
      roomId: liveRoomId,
      cursor: collectorConfig.cursor
    });
    const validation = validateTiktokWebcastTargetUrl({
      websocketUrl,
      liveRoomId,
      allowedHosts: collectorConfig.allowed_websocket_hosts
    });
    if (!validation.ok) {
      return {
        ok: false,
        reason_code: validation.reason_code,
        live_room_id: liveRoomId
      };
    }
    return {
      ok: true,
      provider: collectorConfig.provider,
      live_room_id: liveRoomId,
      websocket_url: websocketUrl,
      headers: collectorConfig.headers,
      protocol: collectorConfig.protocol,
      source: "configured_websocket_url_template"
    };
  }

  if (collectorConfig.signer_url) {
    if (!fetchImpl) {
      return {
        ok: false,
        reason_code: "fetch_not_available",
        live_room_id: liveRoomId
      };
    }
    try {
      const response = await fetchWithOptionalTimeout(fetchImpl, collectorConfig.signer_url, {
        method: "POST",
        headers: {
          "content-type": "application/json",
          ...collectorConfig.signer_headers
        },
        body: JSON.stringify({
          room_id: liveRoomId,
          creator_handle: collectorConfig.creator_handle,
          live_room_url: collectorConfig.live_room_url,
          cursor: collectorConfig.cursor,
          user_agent: collectorConfig.user_agent,
          provider: collectorConfig.provider
        })
      }, collectorConfig.signer_timeout_ms);
      const payload = await parseFetchJson(response);
      const websocketUrl = payload?.websocket_url ?? payload?.ws_url ?? payload?.url ?? payload?.data?.websocket_url ?? payload?.data?.ws_url;
      if (!response.ok || !websocketUrl) {
        return {
          ok: false,
          reason_code: "tiktok_webcast_signer_failed",
          live_room_id: liveRoomId,
          http_status: response.status ?? null
        };
      }
      const validation = validateTiktokWebcastTargetUrl({
        websocketUrl,
        liveRoomId,
        allowedHosts: collectorConfig.allowed_websocket_hosts
      });
      if (!validation.ok) {
        return {
          ok: false,
          reason_code: validation.reason_code,
          live_room_id: liveRoomId,
          http_status: response.status ?? null
        };
      }
      return {
        ok: true,
        provider: collectorConfig.provider,
        live_room_id: liveRoomId,
        websocket_url: websocketUrl,
        headers: {
          ...collectorConfig.headers,
          ...(payload.headers ?? payload.data?.headers ?? {})
        },
        protocol: payload.protocol ?? payload.data?.protocol ?? collectorConfig.protocol,
        cursor: payload.cursor ?? payload.data?.cursor ?? collectorConfig.cursor,
        source: "signer_url"
      };
    } catch {
      return {
        ok: false,
        reason_code: "tiktok_webcast_signer_failed",
        live_room_id: liveRoomId
      };
    }
  }

  return {
    ok: false,
    reason_code: "tiktok_webcast_connection_target_required",
    live_room_id: liveRoomId
  };
}

export function decodeTiktokWebcastFrame(frame, {
  sourceDescriptor = createGiftSourceDescriptor({
    role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    source_id: "source_tiktok_webcast_primary"
  }),
  tenantId = null,
  sessionId = null,
  liveRoomId = null,
  provider = "tiktok_webcast",
  receivedAt = nowIso(),
  includeP1RawItems = false
} = {}) {
  const decoded = decodeWebcastFramePayload(frame);
  if (!decoded.ok) {
    return {
      ok: false,
      frame_hash: hashRawGiftPayload(decoded.hash_input ?? null),
      event_count: 0,
      events: [],
      rejected: [],
      issues: [createContractIssue("frame", decoded.reason_code)],
      raw_payload_included: false
    };
  }
  const items = extractTiktokWebcastMessageItems(decoded.payload);
  const giftItems = items.filter(isTiktokGiftMessage);
  const p1 = normalizeTiktokWebcastP1Payload(decoded.payload, {
    tenantId,
    sessionId,
    liveRoomId,
    provider,
    sourceRole: sourceDescriptor.role,
    sourceId: sourceDescriptor.source_id,
    receivedAt
  });
  const events = [];
  const rejected = [];
  for (const item of giftItems) {
    const validation = validateTiktokWebcastGiftPayload(item, {
      sourceDescriptor,
      liveRoomId,
      provider,
      receivedAt
    });
    if (validation.ok) {
      events.push(validation.normalized);
    } else {
      rejected.push({
        source_event_id: validation.normalized?.source_event_id ?? null,
        reason_codes: validation.issues.map((issue) => issue.reason_code),
        issues: validation.issues
      });
    }
  }
  return {
    ok: rejected.length === 0,
    provider,
    frame_hash: hashRawGiftPayload(decoded.payload),
    message_count: items.length,
    event_count: events.length,
    events,
    p1_event_count: p1.event_count,
    p1_events: p1.events,
    ...(includeP1RawItems ? { p1_raw_items: items.filter((item) => !isTiktokGiftMessage(item)) } : {}),
    p1_rejected: p1.rejected,
    rejected,
    issues: [
      ...rejected.flatMap((item) => item.issues),
      ...p1.issues
    ],
    cursor: decoded.payload?.cursor ?? decoded.payload?.internalExt ?? decoded.payload?.data?.cursor ?? null,
    transport_frame_type: decoded.payload?.transport_frame_type ?? null,
    transport_metadata: decoded.payload?.transport_metadata ?? null,
    raw_payload_included: false
  };
}

function rawP1ItemForEvent(event = {}, rawItems = []) {
  const sourceEventId = event.payload?.source_event_id ?? null;
  if (!sourceEventId) {
    return rawItems[0] ?? null;
  }
  return rawItems.find((item) => {
    const shape = item?.payload && typeof item.payload === "object" ? item.payload : item;
    return String(firstDefined(
      shape?.source_event_id,
      shape?.msg_id,
      shape?.msgId,
      shape?.message_id,
      shape?.messageId,
      shape?.id,
      shape?.event_id,
      shape?.eventId
    ) ?? "") === String(sourceEventId);
  }) ?? rawItems[0] ?? null;
}

export class TiktokWebcastGiftCollector {
  constructor({
    config = {},
    sourceDescriptor = null,
    fetchImpl = globalThis.fetch,
    WebSocketImpl = globalThis.WebSocket,
    messageDecoder = decodeTiktokWebcastFrame,
    onGift = null,
    onP1Evidence = null,
    p1RawStore = null,
    onDecodedFrame = null,
    onStatus = null,
    onIssue = null,
    setTimeoutImpl = globalThis.setTimeout,
    clearTimeoutImpl = globalThis.clearTimeout,
    now = nowIso
  } = {}) {
    this.config = createTiktokWebcastCollectorConfig(config);
    this.sourceDescriptor = sourceDescriptor ?? createGiftSourceDescriptor({
      role: this.config.role,
      source_id: sourceIdForProductionRole(this.config.role)
    });
    this.fetchImpl = fetchImpl;
    this.WebSocketImpl = WebSocketImpl;
    this.messageDecoder = messageDecoder;
    this.onGift = onGift;
    this.onP1Evidence = onP1Evidence;
    this.p1RawStore = p1RawStore;
    this.onDecodedFrame = onDecodedFrame;
    this.onStatus = onStatus;
    this.onIssue = onIssue;
    this.setTimeoutImpl = setTimeoutImpl;
    this.clearTimeoutImpl = clearTimeoutImpl;
    this.now = now;
    this.state = TIKTOK_WEBCAST_COLLECTOR_STATES.IDLE;
    this.socket = null;
    this.reconnectTimer = null;
    this.standbyTimer = null;
    this.reconnectAttempt = 0;
    this.stats = {
      event_count: 0,
      accepted_count: 0,
      p1_event_count: 0,
      rejected_count: 0,
      frame_count: 0,
      reconnect_count: 0,
      standby_poll_count: 0,
      standby_started_at: null,
      live_detected_at: null,
      standby_last_reason_code: null,
      standby_last_http_status: null,
      last_event_at: null,
      first_frame_at: null,
      last_frame_at: null,
      last_issue_code: null
    };
  }

  async start() {
    this.#clearStandbyTimer();
    return this.#connectOnce();
  }

  async #connectOnce({ fromStandby = false } = {}) {
    if (!this.WebSocketImpl) {
      this.#emitIssue("websocket_not_available");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "websocket_not_available"
      });
      return {
        ok: false,
        reason_code: "websocket_not_available"
      };
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RESOLVING_ROOM, { connected: false });
    const target = await createTiktokWebcastConnectionTarget({
      config: this.config,
      fetchImpl: this.fetchImpl
    });
    if (!target.ok) {
      if (this.#canEnterStandby(target)) {
        return this.#enterStandby(target);
      }
      this.#emitIssue(target.reason_code);
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: target.reason_code
      });
      return target;
    }
    this.target = target;
    this.config.live_room_id = target.live_room_id;
    this.config.cursor = target.cursor ?? this.config.cursor;
    if (fromStandby || this.stats.standby_started_at) {
      this.stats.live_detected_at = this.stats.live_detected_at ?? this.now();
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTING, {
      connected: false,
      live_room_id: target.live_room_id,
      ...(fromStandby ? { reason_code: "standby_live_detected" } : {})
    });
    try {
      this.socket = this.#createSocket(target);
    } catch {
      this.#emitIssue("tiktok_webcast_socket_create_failed");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
        connected: false,
        reason_code: "tiktok_webcast_socket_create_failed"
      });
      this.#scheduleReconnect();
      return {
        ok: false,
        reason_code: "tiktok_webcast_socket_create_failed"
      };
    }
    this.#bindSocket(this.socket);
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      live_room_id: target.live_room_id,
      websocket_source: target.source
    };
  }

  stop({ code = 1000, reason = "collector_stop" } = {}) {
    this.#clearStandbyTimer();
    if (this.reconnectTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    const socket = this.socket;
    this.socket = null;
    if (socket && typeof socket.close === "function") {
      try {
        socket.close(code, reason);
      } catch {
        // Closing is best effort.
      }
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED, {
      connected: false,
      reason_code: reason
    });
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      stats: this.snapshot().stats
    };
  }

  snapshot() {
    return {
      ok: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      collector_id: this.config.collector_id,
      provider: this.config.provider,
      role: this.config.role,
      state: this.state,
      live_room_id: this.config.live_room_id ?? null,
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      stats: { ...this.stats },
      target_source: this.target?.source ?? null
    };
  }

  #canEnterStandby(target) {
    if (this.config.standby.enabled !== true) {
      return false;
    }
    const retryReasonCodes = new Set(this.config.standby.retry_reason_codes ?? []);
    if (!retryReasonCodes.has(target.reason_code)) {
      return false;
    }
    return Boolean(
      this.config.creator_handle
      || this.config.live_room_url
      || (this.config.live_room_id && this.config.signer_url)
    );
  }

  #enterStandby(target) {
    const observedAt = this.now();
    this.stats.standby_started_at = this.stats.standby_started_at ?? observedAt;
    this.stats.standby_last_reason_code = target.reason_code ?? null;
    this.stats.standby_last_http_status = target.http_status ?? null;
    if (this.#standbyTimedOut(observedAt)) {
      this.#emitIssue("tiktok_webcast_standby_timeout", {
        last_reason_code: target.reason_code,
        http_status: target.http_status ?? null,
        live_room_id: target.live_room_id ?? this.config.live_room_id ?? null
      });
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_webcast_standby_timeout",
        last_reason_code: target.reason_code,
        http_status: target.http_status ?? null,
        live_room_id: target.live_room_id ?? this.config.live_room_id ?? null
      });
      return {
        ok: false,
        reason_code: "tiktok_webcast_standby_timeout",
        last_reason_code: target.reason_code,
        state: this.state
      };
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY, {
      connected: false,
      reason_code: "standby_waiting_for_live",
      last_reason_code: target.reason_code,
      http_status: target.http_status ?? null,
      live_room_id: target.live_room_id ?? this.config.live_room_id ?? null
    });
    this.#scheduleStandbyPoll();
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      reason_code: "standby_waiting_for_live",
      last_reason_code: target.reason_code,
      live_room_id: target.live_room_id ?? this.config.live_room_id ?? null
    };
  }

  #standbyTimedOut(observedAt) {
    const maxWaitMs = Number(this.config.standby.max_wait_ms ?? 0);
    if (!maxWaitMs || maxWaitMs < 0 || !this.stats.standby_started_at) {
      return false;
    }
    return Date.parse(observedAt) - Date.parse(this.stats.standby_started_at) >= maxWaitMs;
  }

  #scheduleStandbyPoll() {
    if (!this.setTimeoutImpl || this.standbyTimer) {
      return;
    }
    const delay = standbyRetryDelayMs(this.config.standby, this.stats.standby_last_reason_code);
    this.standbyTimer = this.setTimeoutImpl(async () => {
      this.standbyTimer = null;
      if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED) {
        return;
      }
      this.stats.standby_poll_count += 1;
      await this.#connectOnce({ fromStandby: true });
    }, delay);
  }

  #clearStandbyTimer() {
    if (this.standbyTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.standbyTimer);
    }
    this.standbyTimer = null;
  }

  #createSocket(target) {
    const protocol = target.protocol ?? this.config.protocol ?? undefined;
    try {
      return protocol
        ? new this.WebSocketImpl(target.websocket_url, protocol, { headers: target.headers })
        : new this.WebSocketImpl(target.websocket_url, { headers: target.headers });
    } catch {
      return new this.WebSocketImpl(target.websocket_url);
    }
  }

  #bindSocket(socket) {
    const add = (name, handler) => {
      if (typeof socket.addEventListener === "function") {
        socket.addEventListener(name, handler);
        return;
      }
      if (typeof socket.on === "function") {
        socket.on(name, handler);
        return;
      }
      socket[`on${name}`] = handler;
    };
    add("open", () => this.#handleOpen());
    add("message", (event) => this.#handleMessage(event));
    add("error", (event) => this.#handleError(event));
    add("close", (event) => this.#handleClose(event));
  }

  #handleOpen() {
    this.reconnectAttempt = 0;
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED, {
      connected: true
    });
  }

  async #handleMessage(event) {
    const receivedAt = this.now();
    this.stats.frame_count += 1;
    this.stats.first_frame_at = this.stats.first_frame_at ?? receivedAt;
    this.stats.last_frame_at = receivedAt;
    const frame = event?.data ?? event;
    let decoded;
    try {
      decoded = this.messageDecoder(frame, {
        sourceDescriptor: this.sourceDescriptor,
        tenantId: this.config.tenant_id ?? null,
        sessionId: this.config.session_id ?? null,
        liveRoomId: this.config.live_room_id,
        provider: this.config.provider,
        receivedAt,
        includeP1RawItems: Boolean(this.p1RawStore)
      });
    } catch (error) {
      this.stats.rejected_count += 1;
      this.#emitIssue("tiktok_webcast_decoder_failed", {
        message: error?.message ?? null
      });
      this.#emitStatus({
        connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
        observed_at: receivedAt,
        reason_code: "tiktok_webcast_decoder_failed"
      });
      return;
    }
    if (this.onDecodedFrame) {
      try {
        await this.onDecodedFrame(decoded, { collector: this, receivedAt, frame, semanticFrame });
      } catch (error) {
        this.#emitIssue("tiktok_webcast_decoded_frame_handler_failed", {
          message: error?.message ?? null
        });
      }
    }
    if (!decoded.ok && decoded.issues?.length) {
      this.stats.rejected_count += decoded.rejected?.length ?? 0;
      this.#emitIssue(decoded.issues[0].reason_code, decoded.issues[0]);
    }
    for (const gift of decoded.events ?? []) {
      this.stats.event_count += 1;
      this.stats.accepted_count += 1;
      this.stats.last_event_at = gift.event_time;
      if (this.onGift) {
        try {
          await this.onGift({
            event: gift,
            decoded,
            collector: this,
            receivedAt
          });
        } catch (error) {
          this.#emitIssue("tiktok_webcast_gift_handler_failed", {
            source_event_id: gift.source_event_id,
            message: error?.message ?? null
          });
        }
      }
    }
    for (const p1Event of decoded.p1_events ?? []) {
      let eventToEmit = p1Event;
      if (this.p1RawStore) {
        const rawItem = rawP1ItemForEvent(p1Event, decoded.p1_raw_items ?? []);
        try {
          const rawRecord = this.p1RawStore.append(rawItem ?? {
            type: p1Event.event_type,
            source_event_id: p1Event.payload?.source_event_id,
            event_time: p1Event.event_time,
            live_room_id: p1Event.live_room_id
          }, {
            normalizedEvent: p1Event,
            receivedAt
          });
          eventToEmit = withP1RawReference(p1Event, { rawRecord });
        } catch (error) {
          this.stats.rejected_count += 1;
          this.#emitIssue("tiktok_webcast_p1_raw_backup_failed", {
            event_type: p1Event.event_type,
            source_event_id: p1Event.payload?.source_event_id ?? null,
            message: error?.message ?? null
          });
          await this.#emitP1CaptureState({
            eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
            reasonCode: "p1_raw_backup_failed",
            receivedAt
          });
          continue;
        }
      }
      this.stats.p1_event_count += 1;
      if (this.onP1Evidence) {
        try {
          await this.onP1Evidence({
            event: eventToEmit,
            decoded,
            collector: this,
            receivedAt
          });
        } catch (error) {
          this.#emitIssue("tiktok_webcast_p1_handler_failed", {
            event_type: eventToEmit.event_type,
            message: error?.message ?? null
          });
        }
      }
    }
    if ((decoded.p1_rejected ?? []).length > 0) {
      this.stats.rejected_count += decoded.p1_rejected.length;
      this.#emitIssue("tiktok_webcast_p1_frame_rejected", decoded.p1_rejected[0]);
      await this.#emitP1CaptureState({
        eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
        reasonCode: "p1_schema_drift_or_invalid_payload",
        receivedAt
      });
    }
    if ((decoded.rejected ?? []).length > 0) {
      this.stats.rejected_count += decoded.rejected.length;
      this.#emitIssue(decoded.rejected[0].reason_codes?.[0] ?? "tiktok_webcast_frame_rejected", decoded.rejected[0]);
    }
    this.#emitStatus({
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      observed_at: receivedAt
    });
  }

  async #emitP1CaptureState({ eventType, reasonCode, receivedAt }) {
    if (!this.onP1Evidence) {
      return;
    }
    const event = createP1CaptureStateEvent({
      tenantId: this.config.tenant_id ?? null,
      liveRoomId: this.config.live_room_id ?? null,
      sessionId: this.config.session_id ?? null,
      eventType,
      sourceRole: this.sourceDescriptor.role,
      sourceId: this.sourceDescriptor.source_id,
      reasonCode,
      observedAt: receivedAt,
      metrics: {
        frame_count: this.stats.frame_count,
        p1_event_count: this.stats.p1_event_count,
        rejected_count: this.stats.rejected_count
      }
    });
    await this.onP1Evidence({
      event,
      decoded: null,
      collector: this,
      receivedAt
    });
  }

  #handleError(event) {
    this.#emitIssue("tiktok_webcast_socket_error", event);
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
      connected: false,
      reason_code: "tiktok_webcast_socket_error"
    });
    this.#scheduleReconnect();
  }

  #handleClose(event) {
    if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED) {
      return;
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
      connected: false,
      reason_code: "tiktok_webcast_socket_closed",
      close_code: event?.code ?? null
    });
    this.#scheduleReconnect();
  }

  #scheduleReconnect() {
    if (this.config.reconnect.enabled !== true
      || this.reconnectAttempt >= this.config.reconnect.max_attempts
      || this.stats.reconnect_count >= this.config.reconnect.max_total_attempts) {
      this.#emitIssue("tiktok_webcast_reconnect_exhausted");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_webcast_reconnect_exhausted"
      });
      return;
    }
    this.reconnectAttempt += 1;
    this.stats.reconnect_count += 1;
    const delay = Math.min(
      this.config.reconnect.max_delay_ms,
      this.config.reconnect.base_delay_ms * (2 ** Math.max(0, this.reconnectAttempt - 1))
    );
    if (!this.setTimeoutImpl) {
      return;
    }
    this.reconnectTimer = this.setTimeoutImpl(async () => {
      this.reconnectTimer = null;
      const result = await this.start();
      if (result?.ok === false && this.state !== TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED && !this.reconnectTimer) {
        this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
          connected: false,
          reason_code: result.reason_code
        });
        this.#scheduleReconnect();
      }
    }, delay);
  }

  #setState(state, patch = {}) {
    this.state = state;
    this.#emitStatus({
      connected: state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      ...patch
    });
  }

  #emitIssue(reasonCode, detail = {}) {
    this.stats.last_issue_code = reasonCode;
    if (this.onIssue) {
      this.onIssue({
        reason_code: reasonCode,
        detail,
        observed_at: this.now(),
        collector_id: this.config.collector_id
      });
    }
  }

  #emitStatus(patch = {}) {
    if (!this.onStatus) {
      return;
    }
    this.onStatus({
      collector_id: this.config.collector_id,
      source_role: this.config.role,
      live_room_id: this.config.live_room_id ?? null,
      state: this.state,
      connected: patch.connected ?? this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      event_count: this.stats.event_count,
      accepted_count: this.stats.accepted_count,
      p1_event_count: this.stats.p1_event_count,
      rejected_count: this.stats.rejected_count,
      reconnect_count: this.stats.reconnect_count,
      frame_count: this.stats.frame_count,
      first_frame_at: this.stats.first_frame_at,
      last_frame_at: this.stats.last_frame_at,
      last_event_at: this.stats.last_event_at,
      observed_at: patch.observed_at ?? this.now(),
      reason_code: patch.reason_code ?? null,
      ...patch
    });
  }
}

export class TiktokBrowserObserverCollector {
  constructor({
    config = {},
    sourceDescriptor = null,
    fetchImpl = globalThis.fetch,
    WebSocketImpl = null,
    CdpClientImpl = null,
    browserLauncher = defaultTiktokBrowserObserverLauncher,
    messageDecoder = decodeTiktokWebcastFrame,
    onGift = null,
    onP1Evidence = null,
    p1RawStore = null,
    onDecodedFrame = null,
    onStatus = null,
    onIssue = null,
    setTimeoutImpl = globalThis.setTimeout,
    clearTimeoutImpl = globalThis.clearTimeout,
    now = nowIso
  } = {}) {
    this.config = createTiktokBrowserObserverCollectorConfig(config);
    this.sourceDescriptor = sourceDescriptor ?? createGiftSourceDescriptor({
      role: this.config.role,
      source_id: sourceIdForProductionRole(this.config.role),
      source: "tiktok_browser_observer"
    });
    this.fetchImpl = fetchImpl;
    this.WebSocketImpl = resolveWebSocketConstructor(WebSocketImpl);
    this.CdpClientImpl = CdpClientImpl;
    this.browserLauncher = browserLauncher;
    this.messageDecoder = messageDecoder;
    this.onGift = onGift;
    this.onP1Evidence = onP1Evidence;
    this.p1RawStore = p1RawStore;
    this.onDecodedFrame = onDecodedFrame;
    this.onStatus = onStatus;
    this.onIssue = onIssue;
    this.setTimeoutImpl = setTimeoutImpl;
    this.clearTimeoutImpl = clearTimeoutImpl;
    this.now = now;
    this.state = TIKTOK_WEBCAST_COLLECTOR_STATES.IDLE;
    this.cdpClient = null;
    this.cdpClients = new Map();
    this.cdpSessionMetadata = new Map();
    this.browserProcess = null;
    this.reconnectTimer = null;
    this.targetPollTimer = null;
    this.pageHealthTimer = null;
    this.pageBlockedActive = false;
    this.reconnectAttempt = 0;
    this.browserMinimizedForClient = false;
    this.requestUrls = new Map();
    this.pollingResponseRequests = new Map();
    this.stats = {
      event_count: 0,
      accepted_count: 0,
      p1_event_count: 0,
      rejected_count: 0,
      frame_count: 0,
      raw_frame_count: 0,
      decoded_frame_count: 0,
      browser_event_transport_frame_count: 0,
      filtered_frame_count: 0,
      browser_websocket_count: 0,
      browser_cdp_target_count: 0,
      browser_auxiliary_target_count: 0,
      browser_auxiliary_session_count: 0,
      browser_target_attach_count: 0,
      browser_target_attach_failed_count: 0,
      browser_target_poll_count: 0,
      browser_polling_response_count: 0,
      browser_polling_response_body_unavailable_count: 0,
      browser_runtime_capture_count: 0,
      browser_runtime_capture_failed_count: 0,
      browser_runtime_capture_target_gone_count: 0,
      browser_auxiliary_target_gone_count: 0,
      browser_page_health_check_count: 0,
      browser_page_blocked_count: 0,
      browser_not_logged_in_count: 0,
      browser_detached_count: 0,
      browser_relaunch_count: 0,
      browser_auto_minimized_count: 0,
      reconnect_count: 0,
      standby_poll_count: 0,
      standby_started_at: null,
      live_detected_at: null,
      browser_last_detached_at: null,
      browser_last_relaunch_at: null,
      browser_last_minimized_at: null,
      browser_last_page_blocked_at: null,
      browser_last_not_logged_in_at: null,
      browser_last_event_transport_frame_at: null,
      standby_last_reason_code: null,
      standby_last_http_status: null,
      last_event_at: null,
      first_frame_at: null,
      last_frame_at: null,
      last_issue_code: null
    };
  }

  async start() {
    if (this.reconnectTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    return this.#connectOnce();
  }

  async #connectOnce({ fromReconnect = false } = {}) {
    const targetUrl = this.#targetUrl();
    if (!targetUrl && this.config.invalid_live_room_url) {
      this.#emitIssue("tiktok_browser_observer_target_not_tiktok", {
        target_url: this.config.invalid_live_room_url
      });
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_browser_observer_target_not_tiktok",
        target_url: this.config.invalid_live_room_url
      });
      return {
        ok: false,
        reason_code: "tiktok_browser_observer_target_not_tiktok"
      };
    }
    if (!targetUrl) {
      this.#emitIssue("tiktok_browser_observer_target_required");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_browser_observer_target_required"
      });
      return {
        ok: false,
        reason_code: "tiktok_browser_observer_target_required"
      };
    }
    this.#dropCdpClient();
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTING, {
      connected: false,
      target_url: targetUrl,
      transport: "local_browser_cdp",
      ...(fromReconnect ? { reason_code: "tiktok_browser_observer_reconnecting" } : {})
    });
    let launched;
    try {
      launched = await this.browserLauncher({
        config: this.config,
        targetUrl,
        fetchImpl: this.fetchImpl,
        now: this.now
      });
    } catch (error) {
      launched = {
        ok: false,
        reason_code: "tiktok_browser_observer_launch_failed",
        error
      };
    }
    if (!launched?.ok) {
      const reasonCode = launched?.reason_code ?? "tiktok_browser_observer_launch_failed";
      this.#emitIssue(reasonCode, serializeConnectorError(launched?.error));
      return this.#retryTransientBrowserFailure(reasonCode, { targetUrl });
    }
    if (fromReconnect) {
      this.stats.browser_relaunch_count += 1;
      this.stats.browser_last_relaunch_at = this.now();
    }
    this.browserProcess = launched.process ?? this.browserProcess;
    const shouldPreloadRuntimeCapture = this.config.browser_preload_runtime_capture === true
      && this.config.browser_runtime_capture_enabled === true
      && launched.navigated !== true;
    const cdpLookupTargetUrl = shouldPreloadRuntimeCapture ? "about:blank" : targetUrl;
    const webSocketDebuggerUrl = launched.web_socket_debugger_url
      ?? launched.webSocketDebuggerUrl
      ?? await resolveTiktokBrowserObserverCdpWebSocketUrl({
        config: this.config,
        fetchImpl: this.fetchImpl,
        targetUrl: cdpLookupTargetUrl
      });
    if (!webSocketDebuggerUrl) {
      this.#emitIssue("tiktok_browser_observer_cdp_target_not_found", {
        cdp_http_url: this.config.cdp_http_url,
        remote_debugging_port: this.config.remote_debugging_port
      });
      return this.#retryTransientBrowserFailure("tiktok_browser_observer_cdp_target_not_found", { targetUrl });
    }
    try {
      this.cdpClient = this.#createCdpClient(webSocketDebuggerUrl);
      this.browserMinimizedForClient = false;
      await this.cdpClient.connect();
      const primaryTarget = {
        targetKey: webSocketDebuggerUrl,
        targetType: "page",
        targetUrl,
        primary: true
      };
      this.#registerCdpClient(webSocketDebuggerUrl, this.cdpClient, primaryTarget);
      this.#bindCdpClient(this.cdpClient, primaryTarget);
      await this.cdpClient.send("Network.enable");
      await this.cdpClient.send("Page.enable");
      await this.#enableTargetAutoAttach(this.cdpClient, primaryTarget);
      await this.#installRuntimeCapture(this.cdpClient, primaryTarget);
      if (launched.navigated !== true) {
        await this.cdpClient.send("Page.navigate", { url: targetUrl });
      }
      await this.#attachDiscoveredCdpTargets(targetUrl);
      this.#startTargetPolling(targetUrl);
      this.#startPageHealthPolling();
    } catch (error) {
      this.#emitIssue("tiktok_browser_observer_cdp_attach_failed", {
        message: error?.message ?? null
      });
      this.#dropCdpClient();
      return this.#retryTransientBrowserFailure("tiktok_browser_observer_cdp_attach_failed", { targetUrl });
    }
    this.reconnectAttempt = 0;
    this.stats.standby_started_at = this.stats.standby_started_at ?? this.now();
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY, {
      connected: true,
      target_url: targetUrl,
      transport: "local_browser_cdp",
      reason_code: "tiktok_browser_observer_attached_waiting_for_live"
    });
    await this.#checkBrowserPageHealth();
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      live_room_id: this.config.live_room_id ?? null,
      target_url: targetUrl,
      browser_observer_source: "local_browser_cdp"
    };
  }

  stop({ reason = "collector_stop" } = {}) {
    if (this.reconnectTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    this.#dropCdpClient();
    this.#closeBrowserProcessIfRequested();
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED, {
      connected: false,
      reason_code: reason
    });
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      stats: this.snapshot().stats
    };
  }

  snapshot() {
    const targetUrl = this.#targetUrl();
    const attached = this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED
      || this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY;
    const eventStreamConnected = numberOrZero(this.stats.browser_event_transport_frame_count) > 0
      || numberOrZero(this.stats.event_count) > 0
      || numberOrZero(this.stats.p1_event_count) > 0;
    const usableRealtimeFacts = numberOrZero(this.stats.event_count) > 0
      || numberOrZero(this.stats.p1_event_count) > 0;
    const browserLoginRequired = numberOrZero(this.stats.browser_not_logged_in_count) > 0
      && !eventStreamConnected
      && !usableRealtimeFacts;
    const recoveryRequired = this.pageBlockedActive === true
      || browserLoginRequired
      || (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW
        && this.stats.browser_page_blocked_count > 0);
    return {
      ok: attached,
      collector_id: this.config.collector_id,
      provider: this.config.provider,
      role: this.config.role,
      state: this.state,
      live_room_id: this.config.live_room_id ?? null,
      creator_handle: this.config.creator_handle ?? null,
      connected: attached,
      attached,
      event_stream_connected: eventStreamConnected,
      usable_realtime_facts: usableRealtimeFacts,
      transport: "local_browser_cdp",
      stats: { ...this.stats },
      target_url: targetUrl,
      target_source: "tiktok_browser_observer",
      browser_user_data_dir: this.config.browser_user_data_dir ?? null,
      browser_manual_recovery_url: targetUrl,
      browser_recovery_required: recoveryRequired,
      browser_login_required: browserLoginRequired,
      browser_headless: this.config.browser_headless === true,
      browser_interactive_recovery_available: this.config.browser_interactive_recovery_available !== false,
      browser_can_minimize: true,
      browser_can_close: false,
      browser_open_as_app: this.config.browser_open_as_app === true,
      browser_keep_visible: this.config.browser_keep_visible === true
    };
  }

  async openRecoveryPage({ navigate = true } = {}) {
    const targetUrl = this.#targetUrl();
    if (!targetUrl) {
      return {
        ok: false,
        reason_code: "tiktok_browser_observer_target_required"
      };
    }
    if (!this.cdpClient) {
      const started = await this.#connectOnce({ fromReconnect: true });
      if (started?.ok === false && !this.cdpClient) {
        return {
          ok: false,
          reason_code: started.reason_code ?? "tiktok_browser_observer_recovery_attach_failed",
          target_url: targetUrl,
          browser_user_data_dir: this.config.browser_user_data_dir ?? null
        };
      }
    }
    try {
      await this.cdpClient.send("Page.bringToFront");
    } catch (error) {
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_recovery_bring_to_front_failed",
        error: {
          message: error?.message ?? null
        }
      });
    }
    if (navigate) {
      try {
        await this.cdpClient.send("Page.navigate", { url: targetUrl });
      } catch (error) {
        this.#emitIssue("tiktok_browser_observer_recovery_navigate_failed", {
          target_url: targetUrl,
          message: error?.message ?? null
        });
        return {
          ok: false,
          reason_code: "tiktok_browser_observer_recovery_navigate_failed",
          target_url: targetUrl,
          browser_user_data_dir: this.config.browser_user_data_dir ?? null
        };
      }
    }
    this.#emitStatus({
      connected: this.snapshot().connected,
      reason_code: "tiktok_browser_observer_manual_recovery_opened",
      target_url: targetUrl,
      browser_user_data_dir: this.config.browser_user_data_dir ?? null,
      browser_can_minimize: true,
      browser_can_close: false
    });
    return {
      ok: true,
      reason_code: "tiktok_browser_observer_manual_recovery_opened",
      target_url: targetUrl,
      browser_user_data_dir: this.config.browser_user_data_dir ?? null,
      browser_can_minimize: true,
      browser_can_close: false
    };
  }

  #targetUrl() {
    if (this.config.live_room_url && isTiktokUrl(this.config.live_room_url)) {
      return this.config.live_room_url;
    }
    return this.config.creator_handle ? `https://www.tiktok.com/@${this.config.creator_handle}/live` : null;
  }

  #createCdpClient(webSocketDebuggerUrl) {
    if (this.CdpClientImpl) {
      return new this.CdpClientImpl({
        webSocketDebuggerUrl,
        WebSocketImpl: this.WebSocketImpl,
        commandTimeoutMs: this.config.cdp_command_timeout_ms,
        setTimeoutImpl: this.setTimeoutImpl,
        clearTimeoutImpl: this.clearTimeoutImpl,
        now: this.now
      });
    }
    return new CdpWebSocketClient({
      webSocketDebuggerUrl,
      WebSocketImpl: this.WebSocketImpl,
      commandTimeoutMs: this.config.cdp_command_timeout_ms,
      setTimeoutImpl: this.setTimeoutImpl,
      clearTimeoutImpl: this.clearTimeoutImpl
    });
  }

  #registerCdpClient(key, client, metadata = {}) {
    if (!key || this.cdpClients.has(key)) {
      return false;
    }
    this.cdpClients.set(key, { client, metadata });
    this.stats.browser_target_attach_count += 1;
    this.#refreshCdpTargetStats();
    return true;
  }

  #refreshCdpTargetStats() {
    this.stats.browser_cdp_target_count = this.cdpClients.size;
    this.stats.browser_auxiliary_target_count = [...this.cdpClients.values()]
      .filter(({ metadata }) => !metadata?.primary)
      .length;
    this.stats.browser_auxiliary_session_count = this.cdpSessionMetadata.size;
  }

  #bindCdpClient(client, metadata = {}) {
    const add = (eventName, handler) => {
      if (typeof client.on === "function") {
        client.on(eventName, handler);
      }
    };
    const metadataFor = (event) => this.#metadataForCdpEvent(event, metadata);
    add("Target.attachedToTarget", (event) => {
      void this.#handleCdpTargetAttached(event, client, metadata);
    });
    add("Target.detachedFromTarget", (event) => this.#handleCdpTargetDetached(event, metadata));
    add("Network.webSocketCreated", (event) => this.#handleBrowserWebSocketCreated(event, metadataFor(event)));
    add("Network.webSocketFrameReceived", (event) => {
      void this.#handleBrowserWebSocketFrame(event, metadataFor(event));
    });
    add("Network.webSocketClosed", (event) => this.#handleBrowserWebSocketClosed(event, metadataFor(event)));
    add("Network.requestWillBeSent", (event) => this.#handleBrowserRequestWillBeSent(event, metadataFor(event)));
    add("Network.responseReceived", (event) => this.#handleBrowserResponseReceived(event, metadataFor(event)));
    add("Network.loadingFinished", (event) => {
      void this.#handleBrowserLoadingFinished(event, metadataFor(event));
    });
    add("Network.loadingFailed", (event) => this.#handleBrowserLoadingFailed(event, metadataFor(event)));
    add("Runtime.bindingCalled", (event) => {
      void this.#handleRuntimeBindingCalled(event, metadataFor(event));
    });
    add("Inspector.detached", (event) => this.#handleBrowserDetached(event, metadataFor(event)));
  }

  #metadataForCdpEvent(event = {}, fallbackMetadata = {}) {
    const sessionId = stringOrNull(event.sessionId ?? event.params?.sessionId);
    if (sessionId && this.cdpSessionMetadata.has(sessionId)) {
      return this.cdpSessionMetadata.get(sessionId);
    }
    return fallbackMetadata;
  }

  async #enableTargetAutoAttach(client, metadata = {}) {
    if (this.config.browser_auto_attach_child_targets !== true || !client) {
      return;
    }
    try {
      await client.send("Target.setAutoAttach", {
        autoAttach: true,
        waitForDebuggerOnStart: false,
        flatten: true
      });
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_auto_attach_enabled",
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null
      });
    } catch (error) {
      this.#emitIssue("tiktok_browser_observer_auto_attach_failed", {
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null,
        message: error?.message ?? null
      });
    }
  }

  async #handleCdpTargetAttached(event = {}, client, parentMetadata = {}) {
    const params = event.params ?? event;
    const sessionId = stringOrNull(params.sessionId);
    const targetInfo = params.targetInfo ?? {};
    if (!sessionId) {
      return;
    }
    const target = {
      ...targetInfo,
      webSocketDebuggerUrl: `cdp-session:${sessionId}`
    };
    if (!isBrowserObserverAuxiliaryTarget(target, {
      targetUrl: this.#targetUrl(),
      targetTypes: this.config.browser_auxiliary_target_types
    })) {
      return;
    }
    const metadata = {
      targetKey: `${parentMetadata.targetKey ?? "page"}:session:${sessionId}`,
      targetType: stringOrNull(targetInfo.type),
      targetUrl: stringOrNull(targetInfo.url),
      primary: false,
      sessionId,
      parentTargetKey: parentMetadata.targetKey ?? null
    };
    if (!this.cdpSessionMetadata.has(sessionId)) {
      this.cdpSessionMetadata.set(sessionId, metadata);
      this.stats.browser_target_attach_count += 1;
      this.#refreshCdpTargetStats();
    }
    await this.#enableAuxiliaryNetwork(client, metadata);
    await this.#installRuntimeCapture(client, metadata);
    this.#emitStatus({
      connected: this.snapshot().connected,
      reason_code: "tiktok_browser_observer_auxiliary_session_attached",
      cdp_target_type: metadata.targetType,
      cdp_target_url: metadata.targetUrl
    });
  }

  #handleCdpTargetDetached(event = {}) {
    const sessionId = stringOrNull(event.params?.sessionId ?? event.sessionId);
    if (!sessionId || !this.cdpSessionMetadata.has(sessionId)) {
      return;
    }
    const metadata = this.cdpSessionMetadata.get(sessionId);
    this.cdpSessionMetadata.delete(sessionId);
    this.#deleteRequestStateForTargetKey(metadata.targetKey);
    this.#refreshCdpTargetStats();
    this.#emitStatus({
      connected: this.snapshot().connected,
      reason_code: "tiktok_browser_observer_auxiliary_session_detached",
      cdp_target_type: metadata.targetType ?? null,
      cdp_target_url: metadata.targetUrl ?? null
    });
  }

  #deleteRequestStateForTargetKey(targetKey) {
    if (!targetKey) {
      return;
    }
    const prefix = `${targetKey}:`;
    for (const key of this.requestUrls.keys()) {
      if (key.startsWith(prefix)) {
        this.requestUrls.delete(key);
      }
    }
    for (const key of this.pollingResponseRequests.keys()) {
      if (key.startsWith(prefix)) {
        this.pollingResponseRequests.delete(key);
      }
    }
  }

  #requestKey(targetKey, requestId) {
    return `${targetKey ?? "page"}:${requestId}`;
  }

  #handleBrowserWebSocketCreated(event = {}, metadata = {}) {
    const params = event.params ?? event;
    const requestId = stringOrNull(params.requestId);
    const url = stringOrNull(params.url);
    if (requestId && url) {
      this.requestUrls.set(this.#requestKey(metadata.targetKey, requestId), url);
    }
    const isRelevant = (url && this.#isRelevantWebSocketUrl(url)) || (!url && !this.config.require_relevant_websocket_url);
    const isLiveEventTransport = url ? this.#isLiveEventObservedUrl(url, "tiktok_browser_observer_websocket_seen") : !this.config.require_relevant_websocket_url;
    if (isRelevant && isLiveEventTransport) {
      this.stats.browser_websocket_count += 1;
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_websocket_seen",
        websocket_url: url,
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null
      });
      if (this.config.browser_auto_minimize_after_live !== false) {
        void this.#minimizeBrowserWindow("tiktok_browser_observer_websocket_seen");
      }
    } else if (isRelevant && url) {
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_non_event_websocket_seen",
        websocket_url: url,
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null
      });
    }
  }

  async #handleBrowserWebSocketFrame(event = {}, metadata = {}) {
    const params = event.params ?? event;
    const requestId = stringOrNull(params.requestId);
    const requestUrl = requestId ? this.requestUrls.get(this.#requestKey(metadata.targetKey, requestId)) : null;
    if (!requestUrl && this.config.require_relevant_websocket_url) {
      const acceptsUnmappedAuxiliaryFrame = metadata?.primary === false
        && this.config.browser_accept_unmapped_auxiliary_frames === true;
      if (!acceptsUnmappedAuxiliaryFrame) {
        this.stats.filtered_frame_count += 1;
        return;
      }
    }
    if (requestUrl && !this.#isRelevantWebSocketUrl(requestUrl)) {
      this.stats.filtered_frame_count += 1;
      return;
    }
    const response = params.response ?? params.frame ?? {};
    const frame = browserObserverFramePayload(response);
    if (frame === null) {
      this.stats.filtered_frame_count += 1;
      return;
    }
    await this.#processBrowserObservedFrame(frame, {
      metadata,
      reasonCode: "tiktok_browser_observer_frame_received",
      observedUrl: requestUrl
    });
  }

  #handleBrowserRequestWillBeSent(event = {}, metadata = {}) {
    const params = event.params ?? event;
    const requestId = stringOrNull(params.requestId);
    const url = stringOrNull(params.request?.url ?? params.url);
    if (!requestId || !url) {
      return;
    }
    this.requestUrls.set(this.#requestKey(metadata.targetKey, requestId), url);
  }

  #handleBrowserResponseReceived(event = {}, metadata = {}) {
    const params = event.params ?? event;
    const requestId = stringOrNull(params.requestId);
    const url = stringOrNull(params.response?.url)
      ?? (requestId ? this.requestUrls.get(this.#requestKey(metadata.targetKey, requestId)) : null);
    if (!requestId || !url || !this.#isRelevantBrowserPollingResponseUrl(url)) {
      return;
    }
    this.pollingResponseRequests.set(this.#requestKey(metadata.targetKey, requestId), {
      requestId,
      url,
      status: numberOrNull(params.response?.status),
      mimeType: stringOrNull(params.response?.mimeType),
      metadata
    });
  }

  async #handleBrowserLoadingFinished(event = {}, metadata = {}) {
    const params = event.params ?? event;
    const requestId = stringOrNull(params.requestId);
    const requestKey = requestId ? this.#requestKey(metadata.targetKey, requestId) : null;
    if (!requestKey) {
      return;
    }
    const tracked = this.pollingResponseRequests.get(requestKey);
    this.requestUrls.delete(requestKey);
    this.pollingResponseRequests.delete(requestKey);
    if (!tracked) {
      return;
    }
    let body;
    try {
      body = await this.#cdpClientForMetadata(metadata)?.send(
        "Network.getResponseBody",
        { requestId },
        this.#cdpCommandOptions(metadata)
      );
    } catch (error) {
      if (isCdpResponseBodyUnavailableError(error) || isTransientCdpTargetGoneError(error)) {
        this.stats.browser_polling_response_body_unavailable_count += 1;
        if (isTransientCdpTargetGoneError(error)) {
          this.stats.browser_auxiliary_target_gone_count += 1;
        }
        this.#emitStatus({
          connected: this.snapshot().connected,
          reason_code: isTransientCdpTargetGoneError(error)
            ? "tiktok_browser_observer_polling_response_target_gone"
            : "tiktok_browser_observer_polling_response_body_unavailable",
          cdp_target_type: metadata.targetType ?? null,
          cdp_target_url: metadata.targetUrl ?? null,
          url: tracked.url,
          message: error?.message ?? null
        });
        return;
      }
      this.stats.browser_runtime_capture_failed_count += 1;
      this.#emitIssue("tiktok_browser_observer_polling_response_body_failed", {
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null,
        url: tracked.url,
        message: error?.message ?? null
      });
      return;
    }
    const frame = browserObserverResponseBodyPayload(body);
    if (frame === null) {
      this.stats.filtered_frame_count += 1;
      return;
    }
    this.stats.browser_polling_response_count += 1;
    await this.#processBrowserObservedFrame(frame, {
      metadata,
      reasonCode: "tiktok_browser_observer_polling_response_received",
      observedUrl: tracked.url
    });
  }

  #handleBrowserLoadingFailed(event = {}, metadata = {}) {
    const requestId = stringOrNull(event.params?.requestId ?? event.requestId);
    if (!requestId) {
      return;
    }
    const requestKey = this.#requestKey(metadata.targetKey, requestId);
    this.requestUrls.delete(requestKey);
    this.pollingResponseRequests.delete(requestKey);
  }

  async #handleRuntimeBindingCalled(event = {}, metadata = {}) {
    const params = event.params ?? event;
    if (params.name !== this.config.browser_runtime_binding_name) {
      return;
    }
    let payload;
    try {
      payload = JSON.parse(String(params.payload ?? ""));
    } catch {
      this.stats.browser_runtime_capture_failed_count += 1;
      return;
    }
    const url = stringOrNull(payload.url);
    if (payload.kind === "websocket_frame" && url && !this.#isRelevantWebSocketUrl(url)) {
      this.stats.filtered_frame_count += 1;
      return;
    }
    if ((payload.kind === "fetch_response" || payload.kind === "xhr_response")
      && url
      && !this.#isRelevantBrowserPollingResponseUrl(url)) {
      this.stats.filtered_frame_count += 1;
      return;
    }
    const frame = browserObserverRuntimePayload(payload);
    if (frame === null) {
      this.stats.filtered_frame_count += 1;
      return;
    }
    this.stats.browser_runtime_capture_count += 1;
    await this.#processBrowserObservedFrame(frame, {
      metadata,
      reasonCode: `tiktok_browser_observer_runtime_${payload.kind ?? "payload"}_received`,
      observedUrl: url
    });
  }

  async #processBrowserObservedFrame(frame, { metadata = {}, reasonCode = "tiktok_browser_observer_frame_received", observedUrl = null } = {}) {
    const receivedAt = this.now();
    this.stats.frame_count += 1;
    this.stats.raw_frame_count += 1;
    this.stats.first_frame_at = this.stats.first_frame_at ?? receivedAt;
    this.stats.last_frame_at = receivedAt;
    this.#observeBrowserLoginSignal(observedUrl, {
      receivedAt,
      metadata,
      reasonCode
    });
    let decoded;
    try {
      decoded = this.messageDecoder(frame, {
        sourceDescriptor: this.sourceDescriptor,
        tenantId: this.config.tenant_id ?? null,
        sessionId: this.config.session_id ?? null,
        liveRoomId: this.config.live_room_id,
        provider: this.config.provider,
        receivedAt,
        includeP1RawItems: Boolean(this.p1RawStore)
      });
    } catch (error) {
      this.stats.rejected_count += 1;
      this.#emitIssue("tiktok_browser_observer_decoder_failed", {
        message: error?.message ?? null
      });
      return;
    }
    const nonEventTransportFrame = isTiktokNonEventTransportFrame(decoded);
    const isLiveEventTransport = !nonEventTransportFrame && this.#isLiveEventObservedUrl(observedUrl, reasonCode);
    const hasDecodedSignal = decoded?.ok === true
      && (Number(decoded.event_count ?? 0) > 0
        || Number(decoded.p1_event_count ?? 0) > 0);
    const semanticFrame = hasDecodedSignal || isLiveEventTransport;
    if (isLiveEventTransport) {
      this.stats.browser_event_transport_frame_count += 1;
      this.stats.browser_last_event_transport_frame_at = receivedAt;
    }
    if (semanticFrame) {
      this.stats.live_detected_at = this.stats.live_detected_at ?? receivedAt;
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED, {
        connected: true,
        observed_at: receivedAt,
        transport: "local_browser_cdp",
        target_source: "tiktok_browser_observer",
        reason_code: reasonCode,
        observed_url: observedUrl,
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null
      });
    } else {
      this.#emitStatus({
        connected: this.snapshot().connected,
        observed_at: receivedAt,
        reason_code: nonEventTransportFrame
          ? "tiktok_browser_observer_non_event_transport_frame_observed"
          : observedUrl
          ? "tiktok_browser_observer_non_event_response_observed"
          : "tiktok_browser_observer_raw_frame_without_live_signal",
        observed_url: observedUrl,
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null
      });
    }
    await this.#emitDecodedFrame(decoded, { frame, receivedAt, semanticFrame });
  }

  #observeBrowserLoginSignal(observedUrl, { receivedAt = this.now(), metadata = {}, reasonCode = null } = {}) {
    if (!browserObserverUrlHasNotLoggedInSignal(observedUrl)) {
      return;
    }
    this.stats.browser_not_logged_in_count += 1;
    this.stats.browser_last_not_logged_in_at = receivedAt;
    if (this.stats.browser_not_logged_in_count === 1) {
      this.#emitIssue("tiktok_browser_observer_not_logged_in", {
        observed_url: observedUrl,
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null,
        reason_code: reasonCode
      });
    }
  }

  #cdpClientForMetadata(metadata = {}) {
    if (metadata?.targetKey && this.cdpClients.has(metadata.targetKey)) {
      return this.cdpClients.get(metadata.targetKey)?.client ?? null;
    }
    return this.cdpClient;
  }

  #handleBrowserWebSocketClosed(event = {}, metadata = {}) {
    const requestId = event.params?.requestId ?? event.requestId ?? null;
    if (requestId) {
      this.requestUrls.delete(this.#requestKey(metadata.targetKey, requestId));
    }
    this.#emitStatus({
      connected: this.snapshot().connected,
      reason_code: "tiktok_browser_observer_websocket_closed",
      request_id: requestId,
      cdp_target_type: metadata.targetType ?? null,
      cdp_target_url: metadata.targetUrl ?? null
    });
  }

  #handleBrowserDetached(event = {}, metadata = {}) {
    if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED) {
      return;
    }
    if (metadata?.primary === false) {
      const key = metadata.targetKey ?? null;
      if (key && this.cdpClients.has(key)) {
        this.cdpClients.delete(key);
        this.#refreshCdpTargetStats();
      }
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_auxiliary_target_detached",
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null
      });
      return;
    }
    this.stats.browser_detached_count += 1;
    this.stats.browser_last_detached_at = this.now();
    this.#emitIssue("tiktok_browser_observer_detached", event);
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
      connected: false,
      reason_code: "tiktok_browser_observer_detached"
    });
    this.#scheduleReconnect();
  }

  async #emitDecodedFrame(decoded, { frame = null, receivedAt = this.now(), semanticFrame = true } = {}) {
    if (this.onDecodedFrame) {
      try {
        await this.onDecodedFrame(decoded, { collector: this, receivedAt, frame, semanticFrame });
      } catch (error) {
        this.#emitIssue("tiktok_browser_observer_decoded_frame_handler_failed", {
          message: error?.message ?? null
        });
      }
    }
    if (!decoded?.ok && decoded?.issues?.length) {
      this.stats.rejected_count += decoded.rejected?.length ?? decoded.p1_rejected?.length ?? 0;
      if (semanticFrame) {
        this.#emitIssue(decoded.issues[0].reason_code, decoded.issues[0]);
      }
    }
    this.stats.decoded_frame_count += 1;
    for (const gift of decoded?.events ?? []) {
      this.stats.event_count += 1;
      this.stats.accepted_count += 1;
      this.stats.last_event_at = gift.event_time;
      if (this.onGift) {
        try {
          await this.onGift({
            event: gift,
            decoded,
            collector: this,
            receivedAt
          });
        } catch (error) {
          this.#emitIssue("tiktok_browser_observer_gift_handler_failed", {
            source_event_id: gift.source_event_id,
            message: error?.message ?? null
          });
        }
      }
    }
    for (const p1Event of decoded?.p1_events ?? []) {
      let eventToEmit = p1Event;
      if (this.p1RawStore) {
        const rawItem = rawP1ItemForEvent(p1Event, decoded.p1_raw_items ?? []);
        try {
          const rawRecord = this.p1RawStore.append(rawItem ?? frame ?? createMinimalP1Raw(p1Event), {
            normalizedEvent: p1Event,
            receivedAt
          });
          eventToEmit = withP1RawReference(p1Event, { rawRecord });
        } catch (error) {
          this.stats.rejected_count += 1;
          this.#emitIssue("tiktok_browser_observer_p1_raw_backup_failed", {
            event_type: p1Event.event_type,
            source_event_id: p1Event.payload?.source_event_id ?? null,
            message: error?.message ?? null
          });
          await this.#emitP1CaptureState({
            eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
            reasonCode: "p1_raw_backup_failed",
            receivedAt
          });
          continue;
        }
      }
      this.stats.p1_event_count += 1;
      if (this.onP1Evidence) {
        try {
          await this.onP1Evidence({
            event: eventToEmit,
            decoded,
            collector: this,
            receivedAt
          });
        } catch (error) {
          this.#emitIssue("tiktok_browser_observer_p1_handler_failed", {
            event_type: eventToEmit.event_type,
            message: error?.message ?? null
          });
        }
      }
    }
    if ((decoded?.p1_rejected ?? []).length > 0) {
      this.stats.rejected_count += decoded.p1_rejected.length;
      if (semanticFrame) {
        this.#emitIssue("tiktok_browser_observer_p1_frame_rejected", decoded.p1_rejected[0]);
        await this.#emitP1CaptureState({
          eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
          reasonCode: "p1_schema_drift_or_invalid_payload",
          receivedAt
        });
      }
    }
    if ((decoded?.rejected ?? []).length > 0) {
      this.stats.rejected_count += decoded.rejected.length;
      if (semanticFrame) {
        this.#emitIssue(decoded.rejected[0].reason_codes?.[0] ?? "tiktok_browser_observer_frame_rejected", decoded.rejected[0]);
      }
    }
    this.#emitStatus({
      connected: semanticFrame ? true : this.snapshot().connected,
      observed_at: receivedAt,
      reason_code: semanticFrame ? null : "tiktok_browser_observer_non_event_frame_archived"
    });
  }

  async #emitP1CaptureState({ eventType, reasonCode, receivedAt }) {
    if (!this.onP1Evidence) {
      return;
    }
    const event = createP1CaptureStateEvent({
      tenantId: this.config.tenant_id ?? null,
      liveRoomId: this.config.live_room_id ?? null,
      sessionId: this.config.session_id ?? null,
      eventType,
      sourceRole: this.sourceDescriptor.role,
      sourceId: this.sourceDescriptor.source_id,
      reasonCode,
      observedAt: receivedAt,
      metrics: {
        frame_count: this.stats.frame_count,
        p1_event_count: this.stats.p1_event_count,
        rejected_count: this.stats.rejected_count
      }
    });
    await this.onP1Evidence({
      event,
      decoded: null,
      collector: this,
      receivedAt
    });
  }

  #startTargetPolling(targetUrl) {
    if (this.targetPollTimer || this.config.browser_attach_auxiliary_targets !== true || !this.setTimeoutImpl) {
      return;
    }
    const delay = Math.max(500, Number(this.config.browser_target_poll_interval_ms ?? 2000));
    const tick = async () => {
      this.targetPollTimer = null;
      if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED
        || this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW
        || !this.cdpClient) {
        return;
      }
      await this.#attachDiscoveredCdpTargets(targetUrl);
      if (this.state !== TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED && this.cdpClient) {
        this.targetPollTimer = this.setTimeoutImpl(tick, delay);
      }
    };
    this.targetPollTimer = this.setTimeoutImpl(tick, delay);
    if (typeof this.targetPollTimer?.unref === "function") {
      this.targetPollTimer.unref();
    }
  }

  #stopTargetPolling() {
    if (this.targetPollTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.targetPollTimer);
    }
    this.targetPollTimer = null;
  }

  async #attachDiscoveredCdpTargets(targetUrl) {
    if (this.config.browser_attach_auxiliary_targets !== true || !this.fetchImpl || !this.config.cdp_http_url) {
      return;
    }
    this.stats.browser_target_poll_count += 1;
    let targets = [];
    try {
      targets = await resolveTiktokBrowserObserverCdpTargets({
        config: this.config,
        fetchImpl: this.fetchImpl,
        targetUrl
      });
    } catch (error) {
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_target_poll_failed",
        error: {
          message: error?.message ?? null
        }
      });
      return;
    }
    for (const target of targets) {
      const webSocketDebuggerUrl = stringOrNull(target.webSocketDebuggerUrl);
      if (!webSocketDebuggerUrl || this.cdpClients.has(webSocketDebuggerUrl)) {
        continue;
      }
      const client = this.#createCdpClient(webSocketDebuggerUrl);
      const metadata = {
        targetKey: webSocketDebuggerUrl,
        targetType: stringOrNull(target.type),
        targetUrl: stringOrNull(target.url),
        primary: false
      };
      try {
        await client.connect();
        this.#registerCdpClient(webSocketDebuggerUrl, client, metadata);
        this.#bindCdpClient(client, metadata);
        await this.#enableTargetAutoAttach(client, metadata);
        await this.#enableAuxiliaryNetwork(client, metadata);
        await this.#installRuntimeCapture(client, metadata);
        this.#emitStatus({
          connected: this.snapshot().connected,
          reason_code: "tiktok_browser_observer_auxiliary_target_attached",
          cdp_target_type: metadata.targetType,
          cdp_target_url: metadata.targetUrl
        });
      } catch (error) {
        this.cdpClients.delete(webSocketDebuggerUrl);
        this.#refreshCdpTargetStats();
        try {
          client.close();
        } catch {
          // Auxiliary CDP target cleanup is best effort.
        }
        if (isTransientCdpTargetGoneError(error)) {
          this.stats.browser_auxiliary_target_gone_count += 1;
          this.#emitStatus({
            connected: this.snapshot().connected,
            reason_code: "tiktok_browser_observer_auxiliary_target_gone",
            cdp_target_type: metadata.targetType,
            cdp_target_url: metadata.targetUrl,
            message: error?.message ?? null
          });
          continue;
        }
        this.stats.browser_target_attach_failed_count += 1;
        this.#emitIssue("tiktok_browser_observer_auxiliary_target_attach_failed", {
          cdp_target_type: metadata.targetType,
          cdp_target_url: metadata.targetUrl,
          message: error?.message ?? null
        });
      }
    }
  }

  async #enableAuxiliaryNetwork(client, metadata = {}) {
    try {
      await client.send("Network.enable", {}, this.#cdpCommandOptions(metadata));
    } catch (error) {
      if (isTransientCdpTargetGoneError(error)) {
        this.stats.browser_auxiliary_target_gone_count += 1;
        this.#emitStatus({
          connected: this.snapshot().connected,
          reason_code: "tiktok_browser_observer_auxiliary_target_gone",
          cdp_target_type: metadata.targetType ?? null,
          cdp_target_url: metadata.targetUrl ?? null,
          message: error?.message ?? null
        });
        return;
      }
      this.stats.browser_target_attach_failed_count += 1;
      this.#emitIssue("tiktok_browser_observer_auxiliary_network_enable_failed", {
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null,
        message: error?.message ?? null
      });
    }
  }

  async #installRuntimeCapture(client, metadata = {}) {
    if (this.config.browser_runtime_capture_enabled !== true || !client) {
      return;
    }
    const bindingName = stringOrNull(this.config.browser_runtime_binding_name) ?? "__liveRushTiktokCapture";
    const commandOptions = this.#cdpCommandOptions(metadata);
    const source = browserRuntimeCaptureScript({
      bindingName,
      pollingPatterns: this.config.browser_polling_response_url_patterns ?? [],
      websocketPatterns: this.config.relevant_websocket_url_patterns ?? []
    });
    try {
      await client.send("Runtime.enable", {}, commandOptions);
      await client.send("Runtime.addBinding", { name: bindingName }, commandOptions);
      let pageScriptInstalled = false;
      try {
        await client.send("Page.addScriptToEvaluateOnNewDocument", { source }, commandOptions);
        pageScriptInstalled = true;
      } catch {
        // Workers do not expose Page.*. Runtime.evaluate below still patches their global scope.
      }
      await client.send("Runtime.evaluate", {
        expression: source,
        awaitPromise: false,
        returnByValue: false
      }, commandOptions);
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_runtime_capture_installed",
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null,
        page_script_installed: pageScriptInstalled
      });
    } catch (error) {
      if (isTransientRuntimeCaptureTargetGoneError(error, metadata)) {
        this.stats.browser_runtime_capture_target_gone_count += 1;
        this.#emitStatus({
          connected: this.snapshot().connected,
          reason_code: "tiktok_browser_observer_runtime_capture_target_gone",
          cdp_target_type: metadata.targetType ?? null,
          cdp_target_url: metadata.targetUrl ?? null,
          message: error?.message ?? null
        });
        return;
      }
      this.stats.browser_runtime_capture_failed_count += 1;
      this.#emitIssue("tiktok_browser_observer_runtime_capture_install_failed", {
        cdp_target_type: metadata.targetType ?? null,
        cdp_target_url: metadata.targetUrl ?? null,
        message: error?.message ?? null
      });
    }
  }

  #cdpCommandOptions(metadata = {}) {
    const sessionId = stringOrNull(metadata.sessionId);
    return sessionId ? { sessionId } : undefined;
  }

  #isRelevantWebSocketUrl(url) {
    const patterns = this.config.relevant_websocket_url_patterns ?? [];
    if (!patterns.length) {
      return true;
    }
    const text = String(url ?? "").toLowerCase();
    return patterns.some((pattern) => text.includes(String(pattern).toLowerCase()));
  }

  #isRelevantBrowserPollingResponseUrl(url) {
    const patterns = this.config.browser_polling_response_url_patterns ?? [];
    if (!patterns.length) {
      return false;
    }
    const text = String(url ?? "").toLowerCase();
    return patterns.some((pattern) => text.includes(String(pattern).toLowerCase()));
  }

  #isLiveEventObservedUrl(url, reasonCode = "") {
    const text = String(url ?? "").toLowerCase();
    if (!text) {
      return false;
    }
    const nonEventPatterns = this.config.browser_non_event_url_patterns ?? [];
    if (nonEventPatterns.some((pattern) => text.includes(String(pattern).toLowerCase()))) {
      return false;
    }
    const liveEventPatterns = this.config.browser_live_event_url_patterns ?? [];
    if (liveEventPatterns.some((pattern) => text.includes(String(pattern).toLowerCase()))) {
      return true;
    }
    return String(reasonCode ?? "").includes("websocket") && this.#isRelevantWebSocketUrl(url);
  }

  #startPageHealthPolling() {
    if (this.pageHealthTimer || this.config.browser_page_health_check_enabled !== true || !this.setTimeoutImpl) {
      return;
    }
    const delay = Math.max(1000, Number(this.config.browser_page_health_check_interval_ms ?? 15000));
    const tick = async () => {
      this.pageHealthTimer = null;
      if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED || !this.cdpClient) {
        return;
      }
      await this.#checkBrowserPageHealth();
      if (this.state !== TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED && this.cdpClient) {
        this.pageHealthTimer = this.setTimeoutImpl(tick, delay);
      }
    };
    this.pageHealthTimer = this.setTimeoutImpl(tick, delay);
    if (typeof this.pageHealthTimer?.unref === "function") {
      this.pageHealthTimer.unref();
    }
  }

  #stopPageHealthPolling() {
    if (this.pageHealthTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.pageHealthTimer);
    }
    this.pageHealthTimer = null;
  }

  async #checkBrowserPageHealth() {
    if (this.config.browser_page_health_check_enabled !== true || !this.cdpClient) {
      return {
        ok: true,
        skipped: true
      };
    }
    let value = null;
    try {
      const result = await this.cdpClient.send("Runtime.evaluate", {
        expression: `(() => {
          const bodyText = String(document.body?.innerText || document.documentElement?.innerText || "");
          return {
            title: String(document.title || ""),
            url: String(location.href || ""),
            readyState: String(document.readyState || ""),
            bodyText: bodyText.slice(0, 12000)
          };
        })()`,
        awaitPromise: false,
        returnByValue: true
      });
      value = result?.result?.value ?? null;
    } catch (error) {
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_page_health_check_failed",
        error: {
          message: error?.message ?? null
        }
      });
      return {
        ok: false,
        reason_code: "tiktok_browser_observer_page_health_check_failed"
      };
    }
    this.stats.browser_page_health_check_count += 1;
    const classification = this.#classifyBrowserPageHealth(value);
    if (classification.blocked) {
      const firstBlockedObservation = !this.pageBlockedActive;
      if (firstBlockedObservation) {
        this.stats.browser_page_blocked_count += 1;
      }
      this.pageBlockedActive = true;
      this.stats.browser_last_page_blocked_at = this.now();
      if (firstBlockedObservation) {
        this.#emitIssue("tiktok_browser_observer_page_verification_blocked", {
          target_url: this.#targetUrl(),
          page_url: stringOrNull(value?.url),
          page_title: stringOrNull(value?.title),
          matched_text: classification.matchedText,
          needs_manual_browser_verification: true
        });
        this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
          connected: false,
          target_url: this.#targetUrl(),
          transport: "local_browser_cdp",
          reason_code: "tiktok_browser_observer_page_verification_blocked",
          page_url: stringOrNull(value?.url),
          page_title: stringOrNull(value?.title),
          matched_text: classification.matchedText
        });
      } else {
        this.#emitStatus({
          connected: false,
          reason_code: "tiktok_browser_observer_page_verification_still_blocked",
          page_url: stringOrNull(value?.url),
          page_title: stringOrNull(value?.title),
          matched_text: classification.matchedText
        });
      }
      return {
        ok: false,
        reason_code: "tiktok_browser_observer_page_verification_blocked"
      };
    }
    if (this.pageBlockedActive) {
      this.pageBlockedActive = false;
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_page_health_recovered",
        page_url: stringOrNull(value?.url),
        page_title: stringOrNull(value?.title)
      });
      if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW) {
        this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY, {
          connected: true,
          target_url: this.#targetUrl(),
          transport: "local_browser_cdp",
          reason_code: "tiktok_browser_observer_attached_waiting_for_live"
        });
        this.#startTargetPolling(this.#targetUrl());
      }
    }
    return {
      ok: true
    };
  }

  #classifyBrowserPageHealth(value = {}) {
    const text = [
      value?.title,
      value?.url,
      value?.bodyText
    ].map((item) => String(item ?? "").toLowerCase()).join("\n");
    const patterns = this.config.browser_page_blocked_text_patterns ?? [];
    const matchedPattern = patterns
      .map((pattern) => String(pattern).toLowerCase())
      .find((pattern) => pattern && text.includes(pattern));
    if (!matchedPattern) {
      return {
        blocked: false,
        matchedText: null
      };
    }
    const index = text.indexOf(matchedPattern);
    const matchedText = index >= 0
      ? text.slice(Math.max(0, index - 80), Math.min(text.length, index + matchedPattern.length + 120)).replace(/\s+/gu, " ").trim()
      : matchedPattern;
    return {
      blocked: true,
      matchedText
    };
  }

  #scheduleReconnect() {
    if (this.config.reconnect.enabled !== true
      || this.reconnectAttempt >= this.config.reconnect.max_attempts
      || this.stats.reconnect_count >= this.config.reconnect.max_total_attempts) {
      this.#emitIssue("tiktok_browser_observer_reconnect_exhausted");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_browser_observer_reconnect_exhausted"
      });
      return;
    }
    this.#closeBrowserProcessIfRequested();
    this.reconnectAttempt += 1;
    this.stats.reconnect_count += 1;
    const delay = Math.min(
      this.config.reconnect.max_delay_ms,
      this.config.reconnect.base_delay_ms * (2 ** Math.max(0, this.reconnectAttempt - 1))
    );
    if (!this.setTimeoutImpl) {
      return;
    }
    this.reconnectTimer = this.setTimeoutImpl(async () => {
      this.reconnectTimer = null;
      await this.#connectOnce({ fromReconnect: true });
    }, delay);
  }

  #retryTransientBrowserFailure(reasonCode, { targetUrl = null } = {}) {
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
      connected: false,
      reason_code: reasonCode,
      target_url: targetUrl,
      transport: "local_browser_cdp"
    });
    this.#scheduleReconnect();
    return {
      ok: false,
      reason_code: reasonCode
    };
  }

  #dropCdpClient() {
    this.#stopTargetPolling();
    this.#stopPageHealthPolling();
    const clients = new Set([
      ...[...this.cdpClients.values()].map(({ client }) => client),
      this.cdpClient
    ].filter(Boolean));
    this.cdpClient = null;
    this.cdpClients.clear();
    this.cdpSessionMetadata.clear();
    this.requestUrls.clear();
    this.pollingResponseRequests.clear();
    this.#refreshCdpTargetStats();
    for (const client of clients) {
      if (!client || typeof client.close !== "function") {
        continue;
      }
      try {
        client.close();
      } catch {
        // Closing CDP is best effort.
      }
    }
  }

  #closeBrowserProcessIfRequested() {
    if (!this.config.browser_close_on_stop || !this.browserProcess) {
      return;
    }
    const browserProcess = this.browserProcess;
    this.browserProcess = null;
    try {
      if (typeof browserProcess.kill === "function") {
        browserProcess.kill();
      }
    } catch (error) {
      this.#emitStatus({
        connected: false,
        reason_code: "tiktok_browser_observer_browser_close_failed",
        error: {
          message: error?.message ?? null
        }
      });
    }
  }

  async #minimizeBrowserWindow(reasonCode) {
    if (!this.cdpClient || this.browserMinimizedForClient) {
      return false;
    }
    this.browserMinimizedForClient = true;
    try {
      const windowInfo = await this.cdpClient.send("Browser.getWindowForTarget");
      const windowId = Number(windowInfo?.windowId);
      if (!Number.isFinite(windowId)) {
        this.#emitStatus({
          connected: this.snapshot().connected,
          reason_code: "tiktok_browser_observer_window_minimize_unavailable"
        });
        return false;
      }
      await this.cdpClient.send("Browser.setWindowBounds", {
        windowId,
        bounds: {
          windowState: "minimized"
        }
      });
      this.stats.browser_auto_minimized_count += 1;
      this.stats.browser_last_minimized_at = this.now();
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: reasonCode === "tiktok_browser_observer_websocket_seen"
          ? "tiktok_browser_observer_window_minimized_after_live"
          : "tiktok_browser_observer_window_minimized"
      });
      return true;
    } catch (error) {
      this.#emitStatus({
        connected: this.snapshot().connected,
        reason_code: "tiktok_browser_observer_window_minimize_failed",
        error: {
          message: error?.message ?? null
        }
      });
      return false;
    }
  }

  #setState(state, patch = {}) {
    this.state = state;
    this.#emitStatus({
      connected: patch.connected ?? this.snapshot().connected,
      ...patch
    });
  }

  #emitIssue(reasonCode, detail = {}) {
    this.stats.last_issue_code = reasonCode;
    if (this.onIssue) {
      this.onIssue({
        reason_code: reasonCode,
        detail,
        observed_at: this.now(),
        collector_id: this.config.collector_id
      });
    }
  }

  #emitStatus(patch = {}) {
    if (!this.onStatus) {
      return;
    }
    this.onStatus({
      collector_id: this.config.collector_id,
      source_role: this.config.role,
      live_room_id: this.config.live_room_id ?? null,
      creator_handle: this.config.creator_handle ?? null,
      state: this.state,
      connected: patch.connected ?? this.snapshot().connected,
      event_count: this.stats.event_count,
      accepted_count: this.stats.accepted_count,
      p1_event_count: this.stats.p1_event_count,
      rejected_count: this.stats.rejected_count,
      reconnect_count: this.stats.reconnect_count,
      frame_count: this.stats.frame_count,
      raw_frame_count: this.stats.raw_frame_count,
      decoded_frame_count: this.stats.decoded_frame_count,
      first_frame_at: this.stats.first_frame_at,
      last_frame_at: this.stats.last_frame_at,
      last_event_at: this.stats.last_event_at,
      observed_at: patch.observed_at ?? this.now(),
      reason_code: patch.reason_code ?? null,
      transport: "local_browser_cdp",
      target_source: "tiktok_browser_observer",
      ...patch
    });
  }
}

export class TiktokLiveConnectorGiftCollector {
  constructor({
    config = {},
    sourceDescriptor = null,
    ConnectorImpl = null,
    WebcastEvent = null,
    ControlEvent = null,
    moduleLoader = defaultTiktokLiveConnectorModuleLoader,
    messageDecoder = decodeTiktokWebcastFrame,
    onGift = null,
    onP1Evidence = null,
    p1RawStore = null,
    onDecodedFrame = null,
    onStatus = null,
    onIssue = null,
    setTimeoutImpl = globalThis.setTimeout,
    clearTimeoutImpl = globalThis.clearTimeout,
    now = nowIso
  } = {}) {
    this.config = createTiktokWebcastCollectorConfig({
      provider: "tiktok_live_connector",
      ...config
    });
    this.sourceDescriptor = sourceDescriptor ?? createGiftSourceDescriptor({
      role: this.config.role,
      source_id: sourceIdForProductionRole(this.config.role)
    });
    this.ConnectorImpl = ConnectorImpl;
    this.WebcastEvent = WebcastEvent;
    this.ControlEvent = ControlEvent;
    this.moduleLoader = moduleLoader;
    this.messageDecoder = messageDecoder;
    this.onGift = onGift;
    this.onP1Evidence = onP1Evidence;
    this.p1RawStore = p1RawStore;
    this.onDecodedFrame = onDecodedFrame;
    this.onStatus = onStatus;
    this.onIssue = onIssue;
    this.setTimeoutImpl = setTimeoutImpl;
    this.clearTimeoutImpl = clearTimeoutImpl;
    this.now = now;
    this.state = TIKTOK_WEBCAST_COLLECTOR_STATES.IDLE;
    this.connection = null;
    this.connectorModule = null;
    this.reconnectTimer = null;
    this.standbyTimer = null;
    this.requestPollTimer = null;
    this.reconnectAttempt = 0;
    this.connectionGeneration = 0;
    this.roomIdWakeupInFlight = false;
    this.processInitialDataAllowed = this.config.connector_process_initial_data === true;
    this.target = null;
    this.transport = null;
    this.seenGiftKeys = new Set();
    this.seenP1Keys = new Set();
    this.pendingComboGifts = new Map();
    this.recentGiftEvents = [];
    this.stats = {
      event_count: 0,
      accepted_count: 0,
      p1_event_count: 0,
      rejected_count: 0,
      frame_count: 0,
      raw_frame_count: 0,
      request_poll_count: 0,
      duplicate_count: 0,
      reconnect_count: 0,
      standby_poll_count: 0,
      pending_combo_count: 0,
      standby_started_at: null,
      live_detected_at: null,
      standby_last_reason_code: null,
      standby_last_http_status: null,
      last_event_at: null,
      first_frame_at: null,
      last_frame_at: null,
      last_issue_code: null
    };
  }

  async start() {
    this.#clearStandbyTimer();
    return this.#connectOnce();
  }

  updateLiveRoomId(liveRoomId, { reconnectIfStandby = true } = {}) {
    const normalizedLiveRoomId = stringOrNull(liveRoomId);
    if (!normalizedLiveRoomId) {
      return {
        ok: false,
        reason_code: "tiktok_live_connector_live_room_id_required"
      };
    }
    const changed = this.config.live_room_id !== normalizedLiveRoomId;
    this.config.live_room_id = normalizedLiveRoomId;
    if (changed) {
      this.#emitStatus({
        connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
        live_room_id: normalizedLiveRoomId,
        reason_code: "tiktok_live_connector_live_room_id_updated"
      });
    }
    if (reconnectIfStandby && this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY) {
      if (this.roomIdWakeupInFlight) {
        return {
          ok: true,
          changed,
          reconnecting: false,
          wakeup_in_flight: true,
          live_room_id: normalizedLiveRoomId
        };
      }
      this.roomIdWakeupInFlight = true;
      this.#clearStandbyTimer();
      void this.#connectOnce({ fromStandby: true })
        .catch((error) => {
          this.#emitIssue("tiktok_live_connector_room_id_wakeup_failed", {
            live_room_id: normalizedLiveRoomId,
            message: error?.message ?? null
          });
        })
        .finally(() => {
          this.roomIdWakeupInFlight = false;
        });
      return {
        ok: true,
        changed,
        reconnecting: true,
        live_room_id: normalizedLiveRoomId
      };
    }
    return {
      ok: true,
      changed,
      reconnecting: false,
      live_room_id: normalizedLiveRoomId
    };
  }

  async #connectOnce({ fromStandby = false } = {}) {
    const uniqueId = deriveTiktokConnectorUniqueId(this.config);
    if (!uniqueId) {
      this.#emitIssue("tiktok_live_connector_unique_id_required");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_live_connector_unique_id_required"
      });
      return {
        ok: false,
        reason_code: "tiktok_live_connector_unique_id_required"
      };
    }
    let Connector = this.ConnectorImpl;
    let webcastEvents = this.WebcastEvent;
    let controlEvents = this.ControlEvent;
    if (!Connector) {
      try {
        this.connectorModule = this.connectorModule ?? await this.moduleLoader();
        Connector = this.connectorModule?.TikTokLiveConnection
          ?? this.connectorModule?.default?.TikTokLiveConnection
          ?? this.connectorModule?.default
          ?? null;
        webcastEvents = webcastEvents ?? this.connectorModule?.WebcastEvent ?? this.connectorModule?.default?.WebcastEvent ?? null;
        controlEvents = controlEvents ?? this.connectorModule?.ControlEvent ?? this.connectorModule?.default?.ControlEvent ?? null;
        configureTiktokLiveConnectorSignConfig(this.connectorModule, this.config);
      } catch (error) {
        this.#emitIssue("tiktok_live_connector_module_unavailable", {
          message: error?.message ?? null
        });
        this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
          connected: false,
          reason_code: "tiktok_live_connector_module_unavailable"
        });
        return {
          ok: false,
          reason_code: "tiktok_live_connector_module_unavailable"
        };
      }
    }
    if (!Connector) {
      this.#emitIssue("tiktok_live_connector_module_unavailable");
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_live_connector_module_unavailable"
      });
      return {
        ok: false,
        reason_code: "tiktok_live_connector_module_unavailable"
      };
    }

    this.#dropConnection();
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTING, {
      connected: false,
      unique_id: uniqueId,
      connector_source: "tiktok_live_connector",
      ...(fromStandby ? { reason_code: "standby_retry_connecting" } : {})
    });
    let connection = null;
    try {
      connection = new Connector(uniqueId, this.#createConnectorOptions());
      this.connection = connection;
      this.target = {
        ok: true,
        source: "tiktok_live_connector",
        unique_id: uniqueId,
        live_room_id: this.config.live_room_id ?? null
      };
      const generation = ++this.connectionGeneration;
      this.#bindConnection(connection, {
        WebcastEvent: webcastEvents,
        ControlEvent: controlEvents,
        generation
      });
      const state = await connection.connect(this.config.live_room_id ?? undefined);
      this.#handleConnected(state);
      return {
        ok: true,
        collector_id: this.config.collector_id,
        state: this.state,
        live_room_id: this.config.live_room_id ?? state?.roomId ?? null,
        unique_id: uniqueId,
        connector_source: "tiktok_live_connector"
      };
    } catch (error) {
      const reasonCode = reasonCodeForTiktokLiveConnectorError(error);
      const pollingFallback = await this.#tryStartRequestPollingFallback(connection, {
        uniqueId,
        reasonCode,
        error
      });
      if (pollingFallback.ok) {
        return pollingFallback;
      }
      const target = {
        ok: false,
        reason_code: reasonCode,
        live_room_id: this.config.live_room_id ?? null,
        unique_id: uniqueId,
        error: serializeConnectorError(error)
      };
      this.#dropConnection();
      if (this.#canEnterStandby(target)) {
        return this.#enterStandby(target);
      }
      this.#emitIssue(reasonCode, target);
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: reasonCode,
        unique_id: uniqueId
      });
      return target;
    }
  }

  stop({ reason = "collector_stop" } = {}) {
    this.#clearStandbyTimer();
    this.#clearRequestPollTimer();
    if (this.reconnectTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    this.#reportPendingCombos("tiktok_live_connector_combo_unfinalized_on_stop");
    this.#dropConnection();
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED, {
      connected: false,
      reason_code: reason
    });
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      stats: this.snapshot().stats
    };
  }

  snapshot() {
    const runtimeFlags = createTiktokLiveConnectorRuntimeFlags(this.config);
    return {
      ok: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      collector_id: this.config.collector_id,
      provider: this.config.provider,
      role: this.config.role,
      state: this.state,
      live_room_id: this.config.live_room_id ?? null,
      unique_id: deriveTiktokConnectorUniqueId(this.config),
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      transport: this.transport,
      stats: { ...this.stats },
      ...runtimeFlags,
      target_source: this.target?.source ?? "tiktok_live_connector"
    };
  }

  #createConnectorOptions() {
    const authenticateWs = Boolean(this.config.authenticate_ws && this.config.session_cookie);
    const options = {
      connectWithUniqueId: this.config.connector_connect_with_unique_id,
      processInitialData: this.config.connector_process_initial_data === true && this.processInitialDataAllowed === true,
      fetchRoomInfoOnConnect: this.config.connector_fetch_room_info_on_connect,
      enableExtendedGiftInfo: this.config.connector_enable_extended_gift_info,
      enableRequestPolling: this.config.connector_enable_request_polling,
      requestPollingIntervalMs: this.config.connector_request_polling_interval_ms,
      signApiKey: this.config.connector_sign_api_key ?? null,
      disableEulerFallbacks: this.config.connector_disable_euler_fallbacks,
      logFetchFallbackErrors: this.config.connector_log_fetch_fallback_errors,
      webClientParams: this.config.connector_web_client_params,
      webClientHeaders: {
        "User-Agent": this.config.user_agent,
        ...this.config.headers,
        ...this.config.connector_web_client_headers
      },
      webClientOptions: {
        timeout: this.config.connect_timeout_ms,
        ...this.config.connector_web_client_options
      },
      wsClientParams: this.config.connector_ws_client_params,
      wsClientHeaders: {
        "User-Agent": this.config.user_agent,
        ...this.config.connector_ws_client_headers
      },
      wsClientOptions: this.config.connector_ws_client_options,
      authenticateWs,
      sessionId: authenticateWs ? this.config.session_cookie : null,
      ttTargetIdc: authenticateWs ? this.config.tt_target_idc : null
    };
    if (typeof this.config.connector_signed_websocket_provider === "function") {
      options.signedWebSocketProvider = this.config.connector_signed_websocket_provider;
    }
    return options;
  }

  #bindConnection(connection, { WebcastEvent: webcastEvents = null, ControlEvent: controlEvents = null, generation = this.connectionGeneration } = {}) {
    const active = () => this.connection === connection && this.connectionGeneration === generation;
    const guard = (handler) => (...args) => {
      if (!active()) {
        return undefined;
      }
      return handler(...args);
    };
    const add = (name, handler) => {
      if (!name) {
        return;
      }
      const guardedHandler = guard(handler);
      if (typeof connection.on === "function") {
        connection.on(name, guardedHandler);
        return;
      }
      if (typeof connection.addListener === "function") {
        connection.addListener(name, guardedHandler);
        return;
      }
      if (typeof connection.addEventListener === "function") {
        connection.addEventListener(name, guardedHandler);
      }
    };
    add(controlEvents?.CONNECTED ?? "connected", (state) => this.#handleConnected(state));
    add(controlEvents?.DISCONNECTED ?? "disconnected", (event) => this.#handleDisconnected(event));
    add(controlEvents?.ERROR ?? "error", (error) => this.#handleConnectorError(error));
    add(controlEvents?.RAW_DATA ?? "rawData", (raw) => this.#handleRawData(raw));
    add(controlEvents?.WEBSOCKET_CONNECTED ?? "websocketConnected", (event) => this.#emitStatus({
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      reason_code: "tiktok_live_connector_websocket_connected",
      connector_event: event ?? null
    }));
    add(controlEvents?.ENTER_ROOM ?? "enterRoom", (event) => this.#emitStatus({
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      reason_code: "tiktok_live_connector_enter_room",
      connector_event: event ?? null
    }));

    add(webcastEvents?.GIFT ?? "gift", (event) => {
      void this.#handleGiftEvent(event);
    });
    for (const eventType of [
      webcastEvents?.CHAT ?? "chat",
      webcastEvents?.MEMBER ?? "member",
      webcastEvents?.LIKE ?? "like",
      webcastEvents?.ROOM_USER ?? "roomUser",
      webcastEvents?.FOLLOW ?? "follow",
      webcastEvents?.SHARE ?? "share",
      webcastEvents?.SOCIAL ?? "social"
    ]) {
      add(eventType, (event) => {
        void this.#handleP1Event(eventType, event);
      });
    }
    add(webcastEvents?.STREAM_END ?? "streamEnd", (event) => this.#handleStreamEnd(event));
  }

  #handleConnected(state = {}) {
    this.reconnectAttempt = 0;
    this.transport = this.transport ?? "websocket";
    const roomId = stringOrNull(firstDefined(state?.roomId, state?.room_id, state?.roomInfo?.roomId, state?.roomInfo?.room_id));
    if (roomId) {
      this.config.live_room_id = roomId;
      this.target = {
        ...(this.target ?? {}),
        live_room_id: roomId
      };
    }
    if (this.stats.standby_started_at) {
      this.stats.live_detected_at = this.stats.live_detected_at ?? this.now();
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED, {
      connected: true,
      live_room_id: this.config.live_room_id ?? null,
      transport: this.transport,
      reason_code: "tiktok_live_connector_connected"
    });
    this.processInitialDataAllowed = false;
  }

  #handleDisconnected(event = {}) {
    if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED
      || this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY) {
      return;
    }
    this.#clearRequestPollTimer();
    this.#reportPendingCombos("tiktok_live_connector_combo_unfinalized_on_disconnect");
    this.#emitIssue("tiktok_live_connector_disconnected", event);
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
      connected: false,
      reason_code: "tiktok_live_connector_disconnected"
    });
    this.#scheduleReconnect();
  }

  #handleConnectorError(error = {}) {
    const reasonCode = reasonCodeForTiktokLiveConnectorError(error);
    this.#emitIssue(reasonCode, serializeConnectorError(error));
    this.#emitStatus({
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      reason_code: reasonCode
    });
  }

  #handleRawData() {
    const receivedAt = this.now();
    this.stats.raw_frame_count += 1;
    this.stats.first_frame_at = this.stats.first_frame_at ?? receivedAt;
    this.stats.last_frame_at = receivedAt;
    this.#emitStatus({
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      observed_at: receivedAt,
      reason_code: "tiktok_live_connector_raw_frame"
    });
  }

  #handleStreamEnd(event = {}) {
    this.#reportPendingCombos("tiktok_live_connector_combo_unfinalized_on_stream_end");
    this.#emitIssue("tiktok_live_connector_stream_end", event);
    this.#dropConnection();
    this.#enterStandby({
      ok: false,
      reason_code: "tiktok_live_connector_stream_end",
      live_room_id: this.config.live_room_id ?? null,
      unique_id: deriveTiktokConnectorUniqueId(this.config)
    });
  }

  async #handleGiftEvent(raw) {
    const receivedAt = this.now();
    const payload = createTiktokLiveConnectorPayload("gift", raw, {
      liveRoomId: this.config.live_room_id
    });
    this.stats.frame_count += 1;
    this.stats.first_frame_at = this.stats.first_frame_at ?? receivedAt;
    this.stats.last_frame_at = receivedAt;
    if (isTiktokLiveConnectorPendingCombo(payload)) {
      const comboKey = tiktokLiveConnectorComboKey(payload);
      this.pendingComboGifts.set(comboKey, {
        payload,
        received_at: receivedAt
      });
      this.stats.pending_combo_count = this.pendingComboGifts.size;
      this.#emitStatus({
        connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
        observed_at: receivedAt,
        reason_code: "tiktok_live_connector_combo_pending",
        pending_combo_count: this.stats.pending_combo_count
      });
      return;
    }
    if (isTiktokLiveConnectorFinalCombo(payload)) {
      this.pendingComboGifts.delete(tiktokLiveConnectorComboKey(payload));
      this.stats.pending_combo_count = this.pendingComboGifts.size;
    }
    await this.#decodeAndEmitConnectorPayload(payload, {
      receivedAt
    });
  }

  async #handleP1Event(eventType, raw) {
    const receivedAt = this.now();
    const payload = createTiktokLiveConnectorPayload(eventType, raw, {
      liveRoomId: this.config.live_room_id
    });
    this.stats.frame_count += 1;
    this.stats.first_frame_at = this.stats.first_frame_at ?? receivedAt;
    this.stats.last_frame_at = receivedAt;
    await this.#decodeAndEmitConnectorPayload(payload, {
      receivedAt
    });
  }

  async #decodeAndEmitConnectorPayload(payload, { receivedAt = this.now() } = {}) {
    let decoded;
    try {
      decoded = this.messageDecoder({
        events: [payload]
      }, {
        sourceDescriptor: this.sourceDescriptor,
        tenantId: this.config.tenant_id ?? null,
        sessionId: this.config.session_id ?? null,
        liveRoomId: this.config.live_room_id,
        provider: this.config.provider,
        receivedAt,
        includeP1RawItems: Boolean(this.p1RawStore)
      });
    } catch (error) {
      this.stats.rejected_count += 1;
      this.#emitIssue("tiktok_live_connector_decoder_failed", {
        message: error?.message ?? null
      });
      this.#emitStatus({
        connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
        observed_at: receivedAt,
        reason_code: "tiktok_live_connector_decoder_failed"
      });
      return;
    }
    if (this.onDecodedFrame) {
      try {
        await this.onDecodedFrame(decoded, { collector: this, receivedAt, frame: payload });
      } catch (error) {
        this.#emitIssue("tiktok_live_connector_decoded_frame_handler_failed", {
          message: error?.message ?? null
        });
      }
    }
    if (!decoded.ok && decoded.issues?.length) {
      this.stats.rejected_count += decoded.rejected?.length ?? decoded.p1_rejected?.length ?? 0;
      this.#emitIssue(decoded.issues[0].reason_code, decoded.issues[0]);
    }
    for (const gift of decoded.events ?? []) {
      const giftKey = gift.idempotency_key ?? gift.source_event_id ?? null;
      if (giftKey && this.seenGiftKeys.has(giftKey)) {
        this.stats.duplicate_count += 1;
        continue;
      }
      if (giftKey) {
        this.seenGiftKeys.add(giftKey);
      }
      const eventToEmit = this.#annotateGiftRisk(gift);
      this.stats.event_count += 1;
      this.stats.accepted_count += 1;
      this.stats.last_event_at = eventToEmit.event_time;
      if (this.onGift) {
        try {
          await this.onGift({
            event: eventToEmit,
            decoded,
            collector: this,
            receivedAt
          });
        } catch (error) {
          this.#emitIssue("tiktok_live_connector_gift_handler_failed", {
            source_event_id: gift.source_event_id,
            message: error?.message ?? null
          });
        }
      }
    }
    for (const p1Event of decoded.p1_events ?? []) {
      const p1Key = p1Event.idempotency_key ?? p1Event.payload?.source_event_id ?? null;
      if (p1Key && this.seenP1Keys.has(p1Key)) {
        this.stats.duplicate_count += 1;
        continue;
      }
      if (p1Key) {
        this.seenP1Keys.add(p1Key);
      }
      let eventToEmit = p1Event;
      if (this.p1RawStore) {
        const rawItem = rawP1ItemForEvent(p1Event, decoded.p1_raw_items ?? []);
        try {
          const rawRecord = this.p1RawStore.append(rawItem ?? payload, {
            normalizedEvent: p1Event,
            receivedAt
          });
          eventToEmit = withP1RawReference(p1Event, { rawRecord });
        } catch (error) {
          this.stats.rejected_count += 1;
          this.#emitIssue("tiktok_live_connector_p1_raw_backup_failed", {
            event_type: p1Event.event_type,
            source_event_id: p1Event.payload?.source_event_id ?? null,
            message: error?.message ?? null
          });
          await this.#emitP1CaptureState({
            eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
            reasonCode: "p1_raw_backup_failed",
            receivedAt
          });
          continue;
        }
      }
      this.stats.p1_event_count += 1;
      if (this.onP1Evidence) {
        try {
          await this.onP1Evidence({
            event: eventToEmit,
            decoded,
            collector: this,
            receivedAt
          });
        } catch (error) {
          this.#emitIssue("tiktok_live_connector_p1_handler_failed", {
            event_type: eventToEmit.event_type,
            message: error?.message ?? null
          });
        }
      }
    }
    if ((decoded.p1_rejected ?? []).length > 0) {
      this.stats.rejected_count += decoded.p1_rejected.length;
      this.#emitIssue("tiktok_live_connector_p1_frame_rejected", decoded.p1_rejected[0]);
      await this.#emitP1CaptureState({
        eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
        reasonCode: "p1_schema_drift_or_invalid_payload",
        receivedAt
      });
    }
    if ((decoded.rejected ?? []).length > 0) {
      this.stats.rejected_count += decoded.rejected.length;
      this.#emitIssue(decoded.rejected[0].reason_codes?.[0] ?? "tiktok_live_connector_frame_rejected", decoded.rejected[0]);
    }
    this.#emitStatus({
      connected: this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      observed_at: receivedAt
    });
  }

  async #emitP1CaptureState({ eventType, reasonCode, receivedAt }) {
    if (!this.onP1Evidence) {
      return;
    }
    const event = createP1CaptureStateEvent({
      tenantId: this.config.tenant_id ?? null,
      liveRoomId: this.config.live_room_id ?? null,
      sessionId: this.config.session_id ?? null,
      eventType,
      sourceRole: this.sourceDescriptor.role,
      sourceId: this.sourceDescriptor.source_id,
      reasonCode,
      observedAt: receivedAt,
      metrics: {
        frame_count: this.stats.frame_count,
        p1_event_count: this.stats.p1_event_count,
        rejected_count: this.stats.rejected_count
      }
    });
    await this.onP1Evidence({
      event,
      decoded: null,
      collector: this,
      receivedAt
    });
  }

  #annotateGiftRisk(gift) {
    const risk = classifyTiktokGiftRisk(gift, {
      recentEvents: this.recentGiftEvents,
      bigGiftValueThreshold: this.config.big_gift_value_threshold ?? 100000,
      highFrequencyEventsPerSecondThreshold: this.config.high_frequency_events_per_second_threshold ?? 20
    });
    const needsReview = risk.risk_reasons.length > 0;
    const event = {
      ...gift,
      tiktok_resilience: {
        risk_level: risk.risk_level,
        risk_reasons: risk.risk_reasons,
        requires_backup_confirmation: risk.risk_reasons.includes("big_gift"),
        needs_after_live_review: needsReview,
        can_drive_live_score: !needsReview,
        decision: needsReview
          ? (risk.risk_reasons.includes("big_gift") ? TIKTOK_GIFT_RESILIENCE_DECISIONS.HOLD_FOR_CONFIRMATION : TIKTOK_GIFT_RESILIENCE_DECISIONS.MANUAL_REVIEW_REQUIRED)
          : TIKTOK_GIFT_RESILIENCE_DECISIONS.PRIMARY_ACTIVE
      }
    };
    this.recentGiftEvents.push(event);
    if (this.recentGiftEvents.length > 500) {
      this.recentGiftEvents.splice(0, this.recentGiftEvents.length - 500);
    }
    if (needsReview) {
      this.#emitIssue("tiktok_live_connector_gift_needs_review", {
        source_event_id: event.source_event_id,
        live_room_id: event.live_room_id,
        gift_id: event.gift_id,
        gift_total_value: event.gift_total_value,
        risk_level: risk.risk_level,
        risk_reasons: risk.risk_reasons
      });
    }
    return event;
  }

  async #tryStartRequestPollingFallback(connection, { uniqueId = null, reasonCode = null, error = null } = {}) {
    if (!connection || this.config.connector_enable_request_polling !== true) {
      return { ok: false };
    }
    if (!this.#isWebSocketUpgradeFailure(reasonCode, error)) {
      return { ok: false };
    }
    const roomId = stringOrNull(firstDefined(
      this.config.live_room_id,
      connection.roomId,
      connection.webClient?.roomId,
      connection.clientParams?.room_id,
      connection.webClient?.clientParams?.room_id
    ));
    if (!roomId || !this.#hasConnectorPollFetcher(connection)) {
      return { ok: false };
    }
    this.connection = connection;
    this.config.live_room_id = roomId;
    this.transport = "request_polling";
    this.reconnectAttempt = 0;
    this.target = {
      ...(this.target ?? {}),
      ok: true,
      source: "tiktok_live_connector",
      unique_id: uniqueId,
      live_room_id: roomId,
      transport: this.transport
    };
    this.#emitIssue("tiktok_live_connector_websocket_fallback_to_polling", serializeConnectorError(error));
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED, {
      connected: true,
      live_room_id: roomId,
      unique_id: uniqueId,
      transport: this.transport,
      reason_code: "tiktok_live_connector_request_polling_connected"
    });
    this.processInitialDataAllowed = false;
    this.#scheduleRequestPoll();
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      live_room_id: roomId,
      unique_id: uniqueId,
      connector_source: "tiktok_live_connector",
      transport: this.transport,
      reason_code: "tiktok_live_connector_request_polling_connected"
    };
  }

  #isWebSocketUpgradeFailure(reasonCode, error) {
    if (reasonCode !== "tiktok_live_connector_connect_failed") {
      return false;
    }
    const message = String(error?.message ?? error ?? "").toLowerCase();
    return message.includes("websocket")
      || message.includes("web socket")
      || message.includes("unexpected server response")
      || message.includes("not responding");
  }

  #hasConnectorPollFetcher(connection) {
    return typeof connection?.options?.signedWebSocketProvider === "function"
      || typeof connection?.webClient?.fetchSignedWebSocketFromEuler === "function"
      || typeof connection?.webClient?.fetchSignedWebSocketFromEuler?.call === "function";
  }

  #scheduleRequestPoll() {
    if (!this.setTimeoutImpl || this.requestPollTimer || this.transport !== "request_polling") {
      return;
    }
    const delay = Math.max(1000, Number(this.config.connector_request_polling_interval_ms ?? 1000));
    this.requestPollTimer = this.setTimeoutImpl(async () => {
      this.requestPollTimer = null;
      await this.#runRequestPoll();
    }, delay);
  }

  async #runRequestPoll() {
    if (this.state !== TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED
      || this.transport !== "request_polling"
      || !this.connection) {
      return;
    }
    const observedAt = this.now();
    try {
      this.stats.request_poll_count += 1;
      const result = await this.#fetchConnectorPollResult(this.connection);
      this.#applyConnectorPollCursor(this.connection, result);
      if (typeof this.connection.processProtoMessageFetchResult === "function") {
        await this.connection.processProtoMessageFetchResult(result);
      }
      this.stats.raw_frame_count += 1;
      this.stats.first_frame_at = this.stats.first_frame_at ?? observedAt;
      this.stats.last_frame_at = observedAt;
      this.#emitStatus({
        connected: true,
        observed_at: observedAt,
        transport: this.transport,
        reason_code: "tiktok_live_connector_request_poll"
      });
      this.#scheduleRequestPoll();
    } catch (error) {
      const reasonCode = reasonCodeForTiktokLiveConnectorError(error);
      this.#emitIssue(reasonCode, serializeConnectorError(error));
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
        connected: false,
        transport: this.transport,
        reason_code: reasonCode
      });
      this.#scheduleReconnect();
    }
  }

  async #fetchConnectorPollResult(connection) {
    const authenticateWs = Boolean(connection.options?.authenticateWs && connection.options?.sessionId);
    const request = {
      roomId: this.config.live_room_id ?? connection.roomId ?? connection.webClient?.roomId,
      uniqueId: undefined,
      sessionId: authenticateWs ? connection.options.sessionId : undefined,
      ttTargetIdc: authenticateWs ? connection.options.ttTargetIdc : undefined,
      useMobile: connection.options?.useMobile
    };
    if (typeof connection.options?.signedWebSocketProvider === "function") {
      return connection.options.signedWebSocketProvider(request);
    }
    const route = connection.webClient?.fetchSignedWebSocketFromEuler;
    if (typeof route === "function") {
      return route(request);
    }
    if (typeof route?.call === "function") {
      return route.call(request);
    }
    throw new Error("TikTok connector request polling route is unavailable.");
  }

  #applyConnectorPollCursor(connection, result = {}) {
    const cursor = result?.cursor ?? null;
    const internalExt = result?.internalExt ?? result?.internal_ext ?? null;
    if (cursor) {
      if (connection.clientParams) {
        connection.clientParams.cursor = cursor;
      }
      if (connection.webClient?.clientParams) {
        connection.webClient.clientParams.cursor = cursor;
      }
    }
    if (internalExt) {
      if (connection.clientParams) {
        connection.clientParams.internal_ext = internalExt;
      }
      if (connection.webClient?.clientParams) {
        connection.webClient.clientParams.internal_ext = internalExt;
      }
    }
  }

  #clearRequestPollTimer() {
    if (this.requestPollTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.requestPollTimer);
    }
    this.requestPollTimer = null;
  }

  #canEnterStandby(target) {
    if (this.config.standby.enabled !== true) {
      return false;
    }
    const retryReasonCodes = new Set(this.config.standby.retry_reason_codes ?? []);
    if (!retryReasonCodes.has(target.reason_code)) {
      return false;
    }
    return Boolean(deriveTiktokConnectorUniqueId(this.config));
  }

  #enterStandby(target) {
    const observedAt = this.now();
    this.stats.standby_started_at = this.stats.standby_started_at ?? observedAt;
    this.stats.standby_last_reason_code = target.reason_code ?? null;
    this.stats.standby_last_http_status = target.http_status ?? null;
    if (this.#standbyTimedOut(observedAt)) {
      this.#emitIssue("tiktok_live_connector_standby_timeout", {
        last_reason_code: target.reason_code,
        http_status: target.http_status ?? null,
        live_room_id: target.live_room_id ?? this.config.live_room_id ?? null
      });
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_live_connector_standby_timeout",
        last_reason_code: target.reason_code,
        http_status: target.http_status ?? null,
        live_room_id: target.live_room_id ?? this.config.live_room_id ?? null
      });
      return {
        ok: false,
        reason_code: "tiktok_live_connector_standby_timeout",
        last_reason_code: target.reason_code,
        state: this.state
      };
    }
    this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.STANDBY, {
      connected: false,
      reason_code: "standby_waiting_for_live",
      last_reason_code: target.reason_code,
      http_status: target.http_status ?? null,
      live_room_id: target.live_room_id ?? this.config.live_room_id ?? null,
      unique_id: deriveTiktokConnectorUniqueId(this.config)
    });
    this.#scheduleStandbyPoll();
    return {
      ok: true,
      collector_id: this.config.collector_id,
      state: this.state,
      reason_code: "standby_waiting_for_live",
      last_reason_code: target.reason_code,
      live_room_id: target.live_room_id ?? this.config.live_room_id ?? null,
      unique_id: deriveTiktokConnectorUniqueId(this.config)
    };
  }

  #standbyTimedOut(observedAt) {
    const maxWaitMs = Number(this.config.standby.max_wait_ms ?? 0);
    if (!maxWaitMs || maxWaitMs < 0 || !this.stats.standby_started_at) {
      return false;
    }
    return Date.parse(observedAt) - Date.parse(this.stats.standby_started_at) >= maxWaitMs;
  }

  #scheduleStandbyPoll() {
    if (!this.setTimeoutImpl || this.standbyTimer) {
      return;
    }
    const delay = standbyRetryDelayMs(this.config.standby, this.stats.standby_last_reason_code);
    this.standbyTimer = this.setTimeoutImpl(async () => {
      this.standbyTimer = null;
      if (this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED) {
        return;
      }
      this.stats.standby_poll_count += 1;
      await this.#connectOnce({ fromStandby: true });
    }, delay);
  }

  #clearStandbyTimer() {
    if (this.standbyTimer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(this.standbyTimer);
    }
    this.standbyTimer = null;
  }

  #scheduleReconnect() {
    if (this.config.reconnect.enabled !== true
      || this.reconnectAttempt >= this.config.reconnect.max_attempts
      || this.stats.reconnect_count >= this.config.reconnect.max_total_attempts) {
      this.#emitIssue("tiktok_live_connector_reconnect_exhausted");
      if (this.#canEnterStandby({ reason_code: "tiktok_live_connector_disconnected" })) {
        this.#enterStandby({
          ok: false,
          reason_code: "tiktok_live_connector_disconnected",
          live_room_id: this.config.live_room_id ?? null
        });
        return;
      }
      this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.NEEDS_REVIEW, {
        connected: false,
        reason_code: "tiktok_live_connector_reconnect_exhausted"
      });
      return;
    }
    this.reconnectAttempt += 1;
    this.stats.reconnect_count += 1;
    const delay = Math.min(
      this.config.reconnect.max_delay_ms,
      this.config.reconnect.base_delay_ms * (2 ** Math.max(0, this.reconnectAttempt - 1))
    );
    if (!this.setTimeoutImpl) {
      return;
    }
    this.reconnectTimer = this.setTimeoutImpl(async () => {
      this.reconnectTimer = null;
      const result = await this.#connectOnce();
      if (result?.ok === false && this.state !== TIKTOK_WEBCAST_COLLECTOR_STATES.STOPPED && !this.reconnectTimer) {
        this.#setState(TIKTOK_WEBCAST_COLLECTOR_STATES.RECONNECTING, {
          connected: false,
          reason_code: result.reason_code
        });
        this.#scheduleReconnect();
      }
    }, delay);
  }

  #dropConnection() {
    this.#clearRequestPollTimer();
    const connection = this.connection;
    this.connection = null;
    this.transport = null;
    this.connectionGeneration += 1;
    if (connection && typeof connection.disconnect === "function") {
      try {
        connection.disconnect();
      } catch {
        // Disconnect is best effort because connector implementations vary.
      }
    }
    if (connection && typeof connection.removeAllListeners === "function") {
      try {
        connection.removeAllListeners();
      } catch {
        // Listener cleanup is also best effort.
      }
    }
  }

  #reportPendingCombos(reasonCode) {
    if (this.pendingComboGifts.size === 0) {
      return;
    }
    for (const [comboKey, item] of this.pendingComboGifts.entries()) {
      const detail = {
        combo_key: comboKey,
        source_event_id: item.payload?.source_event_id ?? item.payload?.msgId ?? null,
        live_room_id: item.payload?.roomId ?? item.payload?.room_id ?? this.config.live_room_id ?? null,
        gift_id: item.payload?.giftId ?? item.payload?.gift_id ?? null,
        repeat_count: item.payload?.repeatCount ?? item.payload?.repeat_count ?? null,
        raw_payload_hash: hashRawGiftPayload(item.payload),
        requires_after_live_backfill: true
      };
      this.#emitIssue(reasonCode, detail);
      this.#emitPendingComboEvidence(reasonCode, item, detail);
    }
    this.pendingComboGifts.clear();
    this.stats.pending_combo_count = 0;
  }

  #emitPendingComboEvidence(reasonCode, item, detail) {
    if (!this.onP1Evidence) {
      return;
    }
    const observedAt = this.now();
    let event = createP1CaptureStateEvent({
      tenantId: this.config.tenant_id ?? null,
      liveRoomId: detail.live_room_id,
      sessionId: this.config.session_id ?? null,
      eventType: EVENT_TYPES.P1_CAPTURE_DEGRADED,
      sourceRole: this.sourceDescriptor.role,
      sourceId: this.sourceDescriptor.source_id,
      reasonCode,
      observedAt,
      data: {
        pending_combo: detail
      }
    });
    if (this.p1RawStore) {
      try {
        const rawRecord = this.p1RawStore.append(item.payload, {
          normalizedEvent: event,
          receivedAt: observedAt
        });
        event = withP1RawReference(event, { rawRecord });
      } catch (error) {
        this.#emitIssue("tiktok_live_connector_pending_combo_raw_backup_failed", {
          combo_key: detail.combo_key,
          message: error?.message ?? null
        });
      }
    }
    try {
      const maybePromise = this.onP1Evidence({
        event,
        decoded: null,
        collector: this,
        receivedAt: observedAt
      });
      if (maybePromise && typeof maybePromise.catch === "function") {
        maybePromise.catch((error) => this.#emitIssue("tiktok_live_connector_pending_combo_evidence_failed", {
          combo_key: detail.combo_key,
          message: error?.message ?? null
        }));
      }
    } catch (error) {
      this.#emitIssue("tiktok_live_connector_pending_combo_evidence_failed", {
        combo_key: detail.combo_key,
        message: error?.message ?? null
      });
    }
  }

  #setState(state, patch = {}) {
    this.state = state;
    this.#emitStatus({
      connected: state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      ...patch
    });
  }

  #emitIssue(reasonCode, detail = {}) {
    this.stats.last_issue_code = reasonCode;
    if (this.onIssue) {
      this.onIssue({
        reason_code: reasonCode,
        detail,
        observed_at: this.now(),
        collector_id: this.config.collector_id
      });
    }
  }

  #emitStatus(patch = {}) {
    if (!this.onStatus) {
      return;
    }
    const runtimeFlags = createTiktokLiveConnectorRuntimeFlags(this.config);
    this.onStatus({
      collector_id: this.config.collector_id,
      source_role: this.config.role,
      live_room_id: this.config.live_room_id ?? null,
      unique_id: deriveTiktokConnectorUniqueId(this.config),
      state: this.state,
      connected: patch.connected ?? this.state === TIKTOK_WEBCAST_COLLECTOR_STATES.CONNECTED,
      event_count: this.stats.event_count,
      accepted_count: this.stats.accepted_count,
      p1_event_count: this.stats.p1_event_count,
      rejected_count: this.stats.rejected_count,
      reconnect_count: this.stats.reconnect_count,
      pending_combo_count: this.stats.pending_combo_count,
      request_poll_count: this.stats.request_poll_count,
      frame_count: this.stats.frame_count,
      raw_frame_count: this.stats.raw_frame_count,
      first_frame_at: this.stats.first_frame_at,
      last_frame_at: this.stats.last_frame_at,
      last_event_at: this.stats.last_event_at,
      observed_at: patch.observed_at ?? this.now(),
      reason_code: patch.reason_code ?? null,
      transport: this.transport,
      target_source: "tiktok_live_connector",
      ...runtimeFlags,
      ...patch
    });
  }
}

class CdpWebSocketClient {
  constructor({
    webSocketDebuggerUrl,
    WebSocketImpl = null,
    commandTimeoutMs = 10000,
    setTimeoutImpl = globalThis.setTimeout,
    clearTimeoutImpl = globalThis.clearTimeout
  } = {}) {
    this.webSocketDebuggerUrl = webSocketDebuggerUrl;
    this.WebSocketImpl = resolveWebSocketConstructor(WebSocketImpl);
    this.commandTimeoutMs = commandTimeoutMs;
    this.setTimeoutImpl = setTimeoutImpl;
    this.clearTimeoutImpl = clearTimeoutImpl;
    this.socket = null;
    this.nextId = 1;
    this.pending = new Map();
    this.listeners = new Map();
  }

  connect() {
    if (!this.WebSocketImpl) {
      return Promise.reject(new Error("WebSocket is not available for CDP."));
    }
    return new Promise((resolve, reject) => {
      let settled = false;
      const socket = new this.WebSocketImpl(this.webSocketDebuggerUrl);
      this.socket = socket;
      const finish = (callback, value) => {
        if (settled) {
          return;
        }
        settled = true;
        callback(value);
      };
      const timer = this.setTimeoutImpl
        ? this.setTimeoutImpl(() => finish(reject, new Error("CDP connect timeout.")), this.commandTimeoutMs)
        : null;
      const clearTimer = () => {
        if (timer && this.clearTimeoutImpl) {
          this.clearTimeoutImpl(timer);
        }
      };
      this.#addSocketListener(socket, "open", () => {
        clearTimer();
        finish(resolve);
      });
      this.#addSocketListener(socket, "message", (event) => this.#handleMessage(event));
      this.#addSocketListener(socket, "error", (event) => {
        clearTimer();
        finish(reject, event instanceof Error ? event : new Error("CDP socket error."));
      });
      this.#addSocketListener(socket, "close", () => {
        clearTimer();
        this.#rejectPending(new Error("CDP socket closed."));
      });
    });
  }

  send(method, params = {}, options = {}) {
    if (!this.socket || typeof this.socket.send !== "function") {
      return Promise.reject(new Error("CDP socket is not connected."));
    }
    const id = this.nextId++;
    const sessionId = stringOrNull(options?.sessionId);
    const payload = JSON.stringify({
      id,
      method,
      params,
      ...(sessionId ? { sessionId } : {})
    });
    return new Promise((resolve, reject) => {
      const timer = this.setTimeoutImpl
        ? this.setTimeoutImpl(() => {
            this.pending.delete(id);
            reject(new Error(`CDP command timeout: ${method}`));
          }, this.commandTimeoutMs)
        : null;
      this.pending.set(id, {
        resolve,
        reject,
        timer
      });
      try {
        this.socket.send(payload);
      } catch (error) {
        this.#clearPendingTimer(id);
        this.pending.delete(id);
        reject(error);
      }
    });
  }

  on(eventName, handler) {
    this.listeners.set(eventName, [...(this.listeners.get(eventName) ?? []), handler]);
  }

  close() {
    this.#rejectPending(new Error("CDP client closed."));
    if (this.socket && typeof this.socket.close === "function") {
      this.socket.close();
    }
    this.socket = null;
  }

  #addSocketListener(socket, name, handler) {
    if (typeof socket.addEventListener === "function") {
      socket.addEventListener(name, handler);
      return;
    }
    if (typeof socket.on === "function") {
      socket.on(name, handler);
      return;
    }
    socket[`on${name}`] = handler;
  }

  #handleMessage(event) {
    const raw = event?.data ?? event;
    const text = typeof raw === "string" ? raw : raw?.toString?.() ?? "";
    let message;
    try {
      message = JSON.parse(text);
    } catch {
      return;
    }
    if (message.id && this.pending.has(message.id)) {
      const pending = this.pending.get(message.id);
      this.#clearPendingTimer(message.id);
      this.pending.delete(message.id);
      if (message.error) {
        pending.reject(new Error(message.error.message ?? "CDP command failed."));
      } else {
        pending.resolve(message.result ?? {});
      }
      return;
    }
    if (message.method) {
      for (const handler of this.listeners.get(message.method) ?? []) {
        handler(message);
      }
    }
  }

  #clearPendingTimer(id) {
    const pending = this.pending.get(id);
    if (pending?.timer && this.clearTimeoutImpl) {
      this.clearTimeoutImpl(pending.timer);
    }
  }

  #rejectPending(error) {
    for (const [id, pending] of this.pending.entries()) {
      this.#clearPendingTimer(id);
      pending.reject(error);
    }
    this.pending.clear();
  }
}

async function defaultTiktokBrowserObserverLauncher({
  config = createTiktokBrowserObserverCollectorConfig(),
  targetUrl = null
} = {}) {
  if (config.cdp_websocket_url) {
    return {
      ok: true,
      web_socket_debugger_url: config.cdp_websocket_url,
      launched: false,
      navigated: false
    };
  }
  const executablePath = config.browser_executable_path ?? findLocalChromiumExecutable();
  if (!executablePath) {
    return {
      ok: false,
      reason_code: "tiktok_browser_observer_browser_not_found"
    };
  }
  mkdirSync(config.browser_user_data_dir, { recursive: true });
  const args = [
    `--remote-debugging-port=${config.remote_debugging_port}`,
    `--user-data-dir=${config.browser_user_data_dir}`,
    "--no-first-run",
    "--disable-default-apps",
    "--disable-background-timer-throttling",
    "--disable-backgrounding-occluded-windows",
    "--disable-renderer-backgrounding",
    "--disable-features=CalculateNativeWinOcclusion",
    ...config.browser_launch_args
  ];
  const shouldPreloadRuntimeCapture = config.browser_preload_runtime_capture === true
    && config.browser_runtime_capture_enabled === true;
  if (targetUrl && !shouldPreloadRuntimeCapture) {
    args.push(config.browser_open_as_app ? `--app=${targetUrl}` : targetUrl);
  }
  const browserProcess = spawn(executablePath, args, {
    detached: true,
    stdio: "ignore"
  });
  browserProcess.unref();
  return {
    ok: true,
    process: browserProcess,
    debugging_url: config.cdp_http_url,
    launched: true,
    navigated: Boolean(targetUrl && !shouldPreloadRuntimeCapture)
  };
}

async function resolveTiktokBrowserObserverCdpWebSocketUrl({
  config = createTiktokBrowserObserverCollectorConfig(),
  fetchImpl = globalThis.fetch,
  targetUrl = null
} = {}) {
  if (config.cdp_websocket_url) {
    return config.cdp_websocket_url;
  }
  if (!fetchImpl || !config.cdp_http_url) {
    return null;
  }
  const startedAt = Date.now();
  const timeoutMs = Math.max(1000, Number(config.cdp_attach_timeout_ms ?? 15000));
  let targetCreationAttempted = false;
  while (Date.now() - startedAt <= timeoutMs) {
    try {
      const targets = await fetchCdpJson(fetchImpl, `${config.cdp_http_url.replace(/\/+$/u, "")}/json/list`);
      const pageTargets = Array.isArray(targets) ? targets.filter((target) => target.type === "page" || target.type === "webview") : [];
      const target = selectBrowserObserverPageTarget(pageTargets, targetUrl)
        ?? (targetUrl ? null : pageTargets.find((item) => item.webSocketDebuggerUrl))
        ?? null;
      if (target?.webSocketDebuggerUrl) {
        return target.webSocketDebuggerUrl;
      }
      if (targetUrl && !targetCreationAttempted) {
        targetCreationAttempted = true;
        const createdTarget = await createBrowserObserverPageTarget(fetchImpl, config.cdp_http_url, targetUrl);
        if (createdTarget?.webSocketDebuggerUrl) {
          return createdTarget.webSocketDebuggerUrl;
        }
      }
    } catch {
      // The browser may need a moment to open its DevTools endpoint.
    }
    await sleep(250);
  }
  return null;
}

async function resolveTiktokBrowserObserverCdpTargets({
  config = createTiktokBrowserObserverCollectorConfig(),
  fetchImpl = globalThis.fetch,
  targetUrl = null
} = {}) {
  if (!fetchImpl || !config.cdp_http_url) {
    return [];
  }
  const targets = await fetchCdpJson(fetchImpl, `${config.cdp_http_url.replace(/\/+$/u, "")}/json/list`);
  if (!Array.isArray(targets)) {
    return [];
  }
  return targets.filter((target) => isBrowserObserverAuxiliaryTarget(target, {
    targetUrl,
    targetTypes: config.browser_auxiliary_target_types
  }));
}

function isBrowserObserverAuxiliaryTarget(target = {}, {
  targetUrl = null,
  targetTypes = ["service_worker", "worker", "shared_worker"]
} = {}) {
  if (!target?.webSocketDebuggerUrl) {
    return false;
  }
  const type = String(target.type ?? "").toLowerCase();
  if (!new Set((targetTypes ?? []).map((item) => String(item).toLowerCase())).has(type)) {
    return false;
  }
  const title = String(target.title ?? "").toLowerCase();
  const url = String(target.url ?? "").toLowerCase();
  const targetHost = safeHostname(targetUrl);
  if (!title && !url) {
    return true;
  }
  if (targetHost && (url.includes(targetHost) || title.includes(targetHost))) {
    return true;
  }
  return url.includes("tiktok.com")
    || title.includes("tiktok.com")
    || url.includes("webcast")
    || title.includes("webcast")
    || url.includes("im-ws")
    || title.includes("im-ws");
}

async function fetchCdpJson(fetchImpl, url, options = undefined) {
  const response = await fetchImpl(url, options);
  if (typeof response?.json === "function") {
    return response.json();
  }
  const text = typeof response?.text === "function" ? await response.text() : "";
  return JSON.parse(text);
}

function selectBrowserObserverPageTarget(targets = [], targetUrl = null) {
  const normalizedTarget = normalizeBrowserObserverTargetUrl(targetUrl);
  if (!normalizedTarget) {
    return targets.find((target) => target.webSocketDebuggerUrl) ?? null;
  }
  return targets.find((target) => {
    const url = normalizeBrowserObserverTargetUrl(target.url);
    return target.webSocketDebuggerUrl && browserObserverTargetUrlsMatch(url, normalizedTarget);
  }) ?? null;
}

async function createBrowserObserverPageTarget(fetchImpl, cdpHttpUrl, targetUrl) {
  if (!fetchImpl || !cdpHttpUrl || !targetUrl) {
    return null;
  }
  const endpoint = `${cdpHttpUrl.replace(/\/+$/u, "")}/json/new?${encodeURIComponent(targetUrl)}`;
  for (const method of ["PUT", "GET"]) {
    try {
      const created = await fetchCdpJson(fetchImpl, endpoint, { method });
      if (created?.webSocketDebuggerUrl) {
        return created;
      }
    } catch {
      // Chromium variants differ on whether /json/new accepts PUT or GET.
    }
  }
  return null;
}

function normalizeBrowserObserverTargetUrl(value) {
  const text = String(value ?? "").trim().toLowerCase();
  if (!text) {
    return "";
  }
  try {
    const url = new URL(text);
    url.hash = "";
    if (url.pathname.endsWith("/") && url.pathname !== "/") {
      url.pathname = url.pathname.replace(/\/+$/u, "");
    }
    return url.toString().replace(/\/+$/u, "");
  } catch {
    return text.replace(/\/+$/u, "");
  }
}

function browserObserverTargetUrlsMatch(currentUrl, targetUrl) {
  if (!currentUrl || !targetUrl) {
    return false;
  }
  if (currentUrl === targetUrl || currentUrl.includes(targetUrl) || targetUrl.includes(currentUrl)) {
    return true;
  }
  try {
    const current = new URL(currentUrl);
    const target = new URL(targetUrl);
    return isTiktokHostname(current.hostname)
      && isTiktokHostname(target.hostname)
      && current.pathname.replace(/\/+$/u, "") === target.pathname.replace(/\/+$/u, "");
  } catch {
    return false;
  }
}

function safeHostname(value) {
  try {
    return new URL(String(value ?? "")).hostname.toLowerCase();
  } catch {
    return "";
  }
}

function browserObserverUrlHasNotLoggedInSignal(value) {
  const text = stringOrNull(value);
  if (!text) {
    return false;
  }
  try {
    const parsed = new URL(text);
    return parsed.searchParams.get("user_is_login") === "false";
  } catch {
    return /(?:^|[?&])user_is_login=false(?:&|$)/iu.test(text);
  }
}

function browserObserverFramePayload(response = {}) {
  const payloadData = response.payloadData ?? response.data ?? response.payload ?? null;
  if (payloadData === null || payloadData === undefined || payloadData === "") {
    return null;
  }
  const opcode = Number(response.opcode ?? response.opCode ?? 1);
  if (opcode === 2) {
    return Buffer.from(String(payloadData), "base64");
  }
  return payloadData;
}

function browserObserverResponseBodyPayload(body = {}) {
  const payload = body?.body;
  if (payload === null || payload === undefined || payload === "") {
    return null;
  }
  if (body.base64Encoded === true) {
    return Buffer.from(String(payload), "base64");
  }
  return String(payload);
}

function isCdpResponseBodyUnavailableError(error) {
  const message = String(error?.message ?? error ?? "").toLowerCase();
  return message.includes("no resource with given identifier")
    || message.includes("no data found for resource with given identifier");
}

function isTransientCdpTargetGoneError(error) {
  const message = String(error?.message ?? error ?? "").toLowerCase();
  return message.includes("session with given id not found")
    || message.includes("cdp socket closed")
    || message.includes("target closed")
    || message.includes("target crashed")
    || message.includes("target detached")
    || message.includes("cannot find context with specified id");
}

function isTransientRuntimeCaptureTargetGoneError(error, metadata = {}) {
  if (isTransientCdpTargetGoneError(error)) {
    return true;
  }
  const targetType = String(metadata.targetType ?? "").toLowerCase();
  const message = String(error?.message ?? error ?? "").toLowerCase();
  const auxiliaryTargetTimedOut = metadata.primary !== true
    && targetType !== "page"
    && targetType !== "webview"
    && message.includes("cdp command timeout");
  return auxiliaryTargetTimedOut;
}

function browserObserverRuntimePayload(payload = {}) {
  const data = payload.data ?? payload.body ?? payload.payload ?? null;
  if (data === null || data === undefined || data === "") {
    return null;
  }
  if (payload.encoding === "base64") {
    return Buffer.from(String(data), "base64");
  }
  return String(data);
}

function browserRuntimeCaptureScript({ bindingName, pollingPatterns = [], websocketPatterns = [] } = {}) {
  const safeBindingName = JSON.stringify(String(bindingName || "__liveRushTiktokCapture"));
  const safePollingPatterns = JSON.stringify((pollingPatterns ?? []).map((item) => String(item).toLowerCase()));
  const safeWebsocketPatterns = JSON.stringify((websocketPatterns ?? []).map((item) => String(item).toLowerCase()));
  return `(() => {
  const bindingName = ${safeBindingName};
  const pollingPatterns = ${safePollingPatterns};
  const websocketPatterns = ${safeWebsocketPatterns};
  if (globalThis.__liveRushRuntimeCaptureInstalled) return;
  globalThis.__liveRushRuntimeCaptureInstalled = true;
  const emit = (payload) => {
    try {
      const binding = globalThis[bindingName];
      if (typeof binding === "function") binding(JSON.stringify(payload));
    } catch {}
  };
  const includesAny = (value, patterns) => {
    const text = String(value || "").toLowerCase();
    return patterns.length === 0 || patterns.some((pattern) => text.includes(pattern));
  };
  const bytesToBase64 = (bytes) => {
    let binary = "";
    const chunkSize = 0x8000;
    for (let index = 0; index < bytes.length; index += chunkSize) {
      binary += String.fromCharCode(...bytes.subarray(index, index + chunkSize));
    }
    return btoa(binary);
  };
  const captureBody = async (kind, url, response) => {
    if (!includesAny(url, pollingPatterns)) return;
    try {
      const buffer = await response.arrayBuffer();
      emit({ kind, url: String(url || ""), encoding: "base64", data: bytesToBase64(new Uint8Array(buffer)), status: response.status || null });
    } catch {}
  };
  try {
    const OriginalWebSocket = globalThis.WebSocket;
    if (typeof OriginalWebSocket === "function") {
      const WrappedWebSocket = function(...args) {
        const socket = new OriginalWebSocket(...args);
        const url = String(args[0] || "");
        if (includesAny(url, websocketPatterns)) {
          socket.addEventListener("message", async (event) => {
            try {
              const data = event.data;
              if (typeof data === "string") {
                emit({ kind: "websocket_frame", url, encoding: "text", data });
              } else if (data instanceof Blob) {
                const buffer = await data.arrayBuffer();
                emit({ kind: "websocket_frame", url, encoding: "base64", data: bytesToBase64(new Uint8Array(buffer)) });
              } else if (data instanceof ArrayBuffer) {
                emit({ kind: "websocket_frame", url, encoding: "base64", data: bytesToBase64(new Uint8Array(data)) });
              } else if (ArrayBuffer.isView(data)) {
                emit({ kind: "websocket_frame", url, encoding: "base64", data: bytesToBase64(new Uint8Array(data.buffer, data.byteOffset, data.byteLength)) });
              }
            } catch {}
          });
        }
        return socket;
      };
      Object.setPrototypeOf(WrappedWebSocket, OriginalWebSocket);
      WrappedWebSocket.prototype = OriginalWebSocket.prototype;
      globalThis.WebSocket = WrappedWebSocket;
    }
  } catch {}
  try {
    const originalFetch = globalThis.fetch;
    if (typeof originalFetch === "function") {
      globalThis.fetch = async (...args) => {
        const response = await originalFetch(...args);
        const url = String(args[0]?.url || args[0] || response.url || "");
        if (includesAny(url, pollingPatterns)) {
          void captureBody("fetch_response", url, response.clone());
        }
        return response;
      };
    }
  } catch {}
  try {
    const OriginalXHR = globalThis.XMLHttpRequest;
    if (typeof OriginalXHR === "function") {
      const originalOpen = OriginalXHR.prototype.open;
      const originalSend = OriginalXHR.prototype.send;
      OriginalXHR.prototype.open = function(method, url, ...rest) {
        this.__liveRushUrl = String(url || "");
        return originalOpen.call(this, method, url, ...rest);
      };
      OriginalXHR.prototype.send = function(...args) {
        this.addEventListener("loadend", () => {
          const url = this.__liveRushUrl || this.responseURL || "";
          if (!includesAny(url, pollingPatterns)) return;
          try {
            if (this.responseType === "arraybuffer" && this.response) {
              emit({ kind: "xhr_response", url, encoding: "base64", data: bytesToBase64(new Uint8Array(this.response)), status: this.status || null });
            } else if (!this.responseType || this.responseType === "text" || typeof this.responseText === "string") {
              emit({ kind: "xhr_response", url, encoding: "text", data: this.responseText || "", status: this.status || null });
            }
          } catch {}
        });
        return originalSend.apply(this, args);
      };
    }
  } catch {}
})();`;
}

function findLocalChromiumExecutable() {
  const candidates = localChromiumExecutableCandidates();
  return candidates.find((candidate) => candidate && existsSync(candidate)) ?? null;
}

function localChromiumExecutableCandidates() {
  const platform = typeof process !== "undefined" ? process.platform : "";
  const env = typeof process !== "undefined" ? process.env ?? {} : {};
  if (platform === "win32") {
    return [
      env.LIVE_RUSH_BROWSER_PATH,
      env.MSEDGE_PATH,
      env.CHROME_PATH,
      env.LOCALAPPDATA ? join(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : null,
      env.PROGRAMFILES ? join(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : null,
      env["PROGRAMFILES(X86)"] ? join(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : null,
      env.LOCALAPPDATA ? join(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : null,
      env.PROGRAMFILES ? join(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : null,
      env["PROGRAMFILES(X86)"] ? join(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : null
    ];
  }
  if (platform === "darwin") {
    return [
      env.LIVE_RUSH_BROWSER_PATH,
      "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
      "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
    ];
  }
  return [
    env.LIVE_RUSH_BROWSER_PATH,
    "/usr/bin/microsoft-edge",
    "/usr/bin/microsoft-edge-stable",
    "/usr/bin/google-chrome",
    "/usr/bin/google-chrome-stable",
    "/snap/bin/chromium",
    "/usr/bin/chromium",
    "/usr/bin/chromium-browser",
    "/snap/chromium/current/usr/lib/chromium-browser/chrome"
  ];
}

function resolveWebSocketConstructor(candidate = null) {
  if (typeof candidate === "function") {
    return candidate;
  }
  if (typeof globalThis.WebSocket === "function") {
    return globalThis.WebSocket;
  }
  return resolveNodeWebSocketConstructor();
}

function resolveNodeWebSocketConstructor() {
  if (nodeWebSocketConstructorCache !== undefined) {
    return nodeWebSocketConstructorCache;
  }
  try {
    const loaded = nodeRequire("ws");
    const constructor = loaded?.WebSocket ?? loaded?.default ?? loaded;
    nodeWebSocketConstructorCache = typeof constructor === "function" ? constructor : null;
  } catch {
    nodeWebSocketConstructorCache = null;
  }
  return nodeWebSocketConstructorCache;
}

function defaultTiktokBrowserObserverUserDataDir() {
  const env = typeof process !== "undefined" ? process.env ?? {} : {};
  if (env.LOCALAPPDATA) {
    return join(env.LOCALAPPDATA, "LiveRushDataCollector", "browser-profile");
  }
  if (typeof process !== "undefined" && process.platform === "darwin") {
    return join(homedir(), "Library", "Application Support", "LiveRushDataCollector", "browser-profile");
  }
  return join(homedir(), ".cache", "LiveRushDataCollector", "browser-profile");
}

function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

function sanitizeTiktokHandle(value) {
  const text = stringOrNull(value);
  if (!text) {
    return null;
  }
  return text.replace(/^@+/, "").replace(/^https?:\/\/[^/]+\/@?/, "").split(/[/?#]/)[0] || null;
}

function deriveTiktokConnectorUniqueId(config = {}) {
  const directHandle = sanitizeTiktokHandle(firstDefined(
    config.creator_handle,
    config.unique_id,
    config.username
  ));
  if (directHandle) {
    return directHandle;
  }
  return sanitizeTiktokHandleFromTiktokUrl(firstDefined(config.live_room_url, config.url));
}

function sanitizeTiktokHandleFromTiktokUrl(value) {
  const text = stringOrNull(value);
  if (!text || !isTiktokUrl(text)) {
    return null;
  }
  try {
    const parsed = new URL(text);
    const match = parsed.pathname.match(/\/@([^/?#]+)/u);
    return sanitizeTiktokHandle(match?.[1]);
  } catch {
    return null;
  }
}

function isTiktokUrl(value) {
  const text = stringOrNull(value);
  if (!text) {
    return false;
  }
  try {
    return isTiktokHostname(new URL(text).hostname);
  } catch {
    return false;
  }
}

function isTiktokHostname(hostname) {
  const normalized = String(hostname ?? "").trim().toLowerCase();
  return normalized === "tiktok.com" || normalized.endsWith(".tiktok.com");
}

async function defaultTiktokLiveConnectorModuleLoader() {
  return import("tiktok-live-connector");
}

function configureTiktokLiveConnectorSignConfig(moduleValue, config = {}) {
  const signConfig = moduleValue?.SignConfig ?? moduleValue?.default?.SignConfig ?? null;
  if (!signConfig) {
    return;
  }
  if (config.connector_sign_api_url) {
    signConfig.basePath = config.connector_sign_api_url;
  }
  if (config.connector_sign_api_key) {
    signConfig.apiKey = config.connector_sign_api_key;
  }
}

function createTiktokLiveConnectorRuntimeFlags(config = {}) {
  const signApiKeyConfigured = Boolean(stringOrNull(config.connector_sign_api_key ?? config.sign_api_key));
  const signApiUrlConfigured = Boolean(stringOrNull(config.connector_sign_api_url ?? config.sign_api_url));
  const signedWebsocketProviderConfigured = typeof config.connector_signed_websocket_provider === "function";
  const signingConfigured = signApiKeyConfigured || signApiUrlConfigured || signedWebsocketProviderConfigured;
  const authenticateWs = Boolean(config.authenticate_ws && config.session_cookie);
  return {
    connector_signing_configured: signingConfigured,
    connector_uses_default_signing: !signingConfigured,
    connector_sign_api_key_configured: signApiKeyConfigured,
    connector_sign_api_url_configured: signApiUrlConfigured,
    connector_signed_websocket_provider_configured: signedWebsocketProviderConfigured,
    connector_request_polling_enabled: config.connector_enable_request_polling === true,
    connector_connect_with_unique_id: config.connector_connect_with_unique_id === true,
    connector_fetch_room_info_on_connect: config.connector_fetch_room_info_on_connect === true,
    connector_authenticate_ws: authenticateWs,
    connector_session_cookie_configured: Boolean(stringOrNull(config.session_cookie))
  };
}

function createTiktokLiveConnectorPayload(eventType, raw, { liveRoomId = null } = {}) {
  const shape = raw?.payload && typeof raw.payload === "object" ? raw.payload : raw ?? {};
  const common = shape.common ?? {};
  const user = shape.user ?? shape.fromUser ?? shape.sender ?? shape.member ?? {};
  const gift = shape.giftDetails ?? shape.gift ?? shape.extendedGiftInfo ?? {};
  const normalizedType = normalizeTiktokConnectorEventType(eventType, shape);
  const eventTime = firstDefined(
    shape.event_time,
    shape.timestamp,
    shape.create_time,
    shape.createTime,
    common.createTime,
    common.create_time
  );
  const roomId = stringOrNull(firstDefined(
    shape.live_room_id,
    shape.room_id,
    shape.roomId,
    common.room_id,
    common.roomId,
    liveRoomId
  ));
  const userId = stringOrNull(firstDefined(
    shape.gifter_user_id,
    shape.userId,
    shape.user_id,
    user.userId,
    user.user_id,
    user.userIdStr,
    user.user_id_str,
    user.id,
    user.uniqueId,
    user.unique_id
  ));
  const giftId = stringOrNull(firstDefined(
    shape.gift_id,
    shape.giftId,
    gift.gift_id,
    gift.giftId,
    gift.id
  ));
  const groupId = stringOrNull(firstDefined(
    shape.group_id,
    shape.groupId,
    shape.combo_id,
    shape.comboId,
    shape.streak_id,
    shape.streakId
  ));
  const sourceEventId = stringOrNull(firstDefined(
    shape.source_event_id,
    shape.msg_id,
    shape.msgId,
    shape.message_id,
    shape.messageId,
    common.msgId,
    common.msg_id,
    common.messageId,
    common.message_id,
    shape.logId,
    shape.log_id,
    shape.id
  )) ?? createTiktokLiveConnectorSyntheticEventId({
    eventType: normalizedType,
    roomId,
    userId,
    giftId,
    groupId,
    eventTime,
    repeatCount: firstDefined(shape.repeatCount, shape.repeat_count, shape.comboCount, shape.groupCount, shape.count),
    repeatEnd: firstDefined(shape.repeatEnd, shape.repeat_end)
  });
  const payload = {
    ...shape,
    type: normalizedType,
    event_type: normalizedType,
    method: shape.method ?? methodForTiktokConnectorType(normalizedType),
    msgId: sourceEventId,
    msg_id: sourceEventId,
    source_event_id: sourceEventId,
    roomId,
    room_id: roomId,
    createTime: eventTime,
    create_time: eventTime,
    comment: firstDefined(shape.comment, shape.text, shape.content, shape.message, shape.msg),
    action: firstDefined(shape.action, shape.social_action, shape.socialAction, normalizedType === "follow" ? "follow" : normalizedType === "share" ? "share" : null),
    user: {
      ...user,
      userId: firstDefined(user.userId, user.user_id, user.id, user.uniqueId, user.unique_id, userId),
      user_id: firstDefined(user.user_id, user.userId, user.id, userId),
      uniqueId: firstDefined(user.uniqueId, user.unique_id),
      unique_id: firstDefined(user.unique_id, user.uniqueId),
      nickname: firstDefined(user.nickname, user.nickName, user.displayName, user.uniqueId, user.unique_id, userId)
    },
    gift: {
      ...gift,
      id: giftId,
      giftId,
      gift_id: giftId,
      name: firstDefined(gift.name, gift.giftName, gift.gift_name, shape.giftName, shape.gift_name, giftId),
      giftName: firstDefined(gift.giftName, gift.gift_name, gift.name, shape.giftName, shape.gift_name),
      gift_name: firstDefined(gift.gift_name, gift.giftName, gift.name, shape.giftName, shape.gift_name),
      diamondCount: firstDefined(gift.diamondCount, gift.diamond_count, gift.diamond, shape.diamondCount, shape.diamond_count),
      diamond_count: firstDefined(gift.diamond_count, gift.diamondCount, gift.diamond, shape.diamondCount, shape.diamond_count),
      type: firstDefined(gift.type, gift.giftType, gift.gift_type, shape.giftType, shape.gift_type)
    },
    giftId,
    gift_id: giftId,
    giftName: firstDefined(shape.giftName, shape.gift_name, gift.giftName, gift.gift_name, gift.name, giftId),
    gift_name: firstDefined(shape.gift_name, shape.giftName, gift.gift_name, gift.giftName, gift.name, giftId),
    diamondCount: firstDefined(shape.diamondCount, shape.diamond_count, gift.diamondCount, gift.diamond_count, gift.diamond),
    diamond_count: firstDefined(shape.diamond_count, shape.diamondCount, gift.diamond_count, gift.diamondCount, gift.diamond),
    repeatCount: firstDefined(shape.repeatCount, shape.repeat_count, shape.comboCount, shape.combo_count, shape.groupCount, shape.count),
    repeat_count: firstDefined(shape.repeat_count, shape.repeatCount, shape.combo_count, shape.comboCount, shape.groupCount, shape.count),
    comboCount: firstDefined(shape.comboCount, shape.combo_count, shape.groupCount, shape.repeatCount),
    combo_count: firstDefined(shape.combo_count, shape.comboCount, shape.groupCount, shape.repeat_count),
    repeatEnd: firstDefined(shape.repeatEnd, shape.repeat_end),
    repeat_end: firstDefined(shape.repeat_end, shape.repeatEnd),
    giftType: firstDefined(shape.giftType, shape.gift_type, gift.type, gift.giftType, gift.gift_type),
    gift_type: firstDefined(shape.gift_type, shape.giftType, gift.type, gift.giftType, gift.gift_type),
    groupId,
    group_id: groupId,
    viewer_count: firstDefined(shape.viewer_count, shape.viewerCount, shape.userCount, shape.user_count, shape.totalUser, shape.total_user),
    total_user: firstDefined(shape.total_user, shape.totalUser, shape.total, shape.viewerCount, shape.viewer_count)
  };
  if (normalizedType !== "gift") {
    for (const key of [
      "gift",
      "giftId",
      "gift_id",
      "giftName",
      "gift_name",
      "diamondCount",
      "diamond_count",
      "repeatCount",
      "repeat_count",
      "comboCount",
      "combo_count",
      "repeatEnd",
      "repeat_end",
      "giftType",
      "gift_type",
      "groupId",
      "group_id"
    ]) {
      delete payload[key];
    }
  }
  return payload;
}

function normalizeTiktokConnectorEventType(eventType, shape = {}) {
  const text = String(eventType ?? shape.type ?? shape.event_type ?? shape.method ?? "").trim();
  const lower = text.toLowerCase();
  if (["roomuser", "room_user", "room_user_seq", "roomuserseq"].includes(lower)) {
    return "room_user";
  }
  if (lower.includes("gift")) {
    return "gift";
  }
  if (lower.includes("chat") || lower.includes("comment")) {
    return "chat";
  }
  if (lower.includes("member") || lower.includes("join")) {
    return "member";
  }
  if (lower.includes("like")) {
    return "like";
  }
  if (lower.includes("follow")) {
    return "follow";
  }
  if (lower.includes("share")) {
    return "share";
  }
  if (lower.includes("social")) {
    return "social";
  }
  return lower || "system";
}

function methodForTiktokConnectorType(type) {
  const map = {
    gift: "WebcastGiftMessage",
    chat: "WebcastChatMessage",
    member: "WebcastMemberMessage",
    like: "WebcastLikeMessage",
    room_user: "WebcastRoomUserSeqMessage",
    follow: "WebcastSocialMessage",
    share: "WebcastSocialMessage",
    social: "WebcastSocialMessage"
  };
  return map[type] ?? type;
}

function createTiktokLiveConnectorSyntheticEventId(input = {}) {
  return `tiktok_live_connector_${createHash("sha256")
    .update(stableStringify(input))
    .digest("hex")
    .slice(0, 16)}`;
}

function isTiktokLiveConnectorPendingCombo(payload = {}) {
  const giftType = numberOrNull(firstDefined(payload.gift_type, payload.giftType, payload.gift?.type));
  const repeatEnd = booleanOrNull(firstDefined(payload.repeat_end, payload.repeatEnd));
  return giftType === 1 && repeatEnd !== true;
}

function isTiktokLiveConnectorFinalCombo(payload = {}) {
  const giftType = numberOrNull(firstDefined(payload.gift_type, payload.giftType, payload.gift?.type));
  const repeatEnd = booleanOrNull(firstDefined(payload.repeat_end, payload.repeatEnd));
  return giftType === 1 && repeatEnd === true;
}

function tiktokLiveConnectorComboKey(payload = {}) {
  return [
    payload.room_id ?? payload.roomId ?? "room",
    payload.user?.userId ?? payload.user?.user_id ?? payload.userId ?? "user",
    payload.gift_id ?? payload.giftId ?? payload.gift?.id ?? "gift",
    payload.group_id ?? payload.groupId ?? payload.source_event_id ?? payload.msgId ?? "combo"
  ].map((item) => String(item ?? "")).join(":");
}

function reasonCodeForTiktokLiveConnectorError(error) {
  const text = connectorErrorText(error).toLowerCase();
  if (text.includes("signatureratelimiterror")
    || text.includes("rate_limit_account")
    || text.includes("sign server message")
    || text.includes("eulerstream.com/pricing")) {
    return "tiktok_live_connector_signer_rate_limited";
  }
  if (text.includes("not live") || text.includes("offline") || text.includes("stream end")) {
    return "tiktok_live_connector_room_not_live";
  }
  if (text.includes("403") || text.includes("429") || text.includes("forbidden") || text.includes("rate limit") || text.includes("blocked by tiktok")) {
    return "tiktok_live_connector_fetch_blocked";
  }
  if (text.includes("sign") || text.includes("signature") || text.includes("euler")) {
    return "tiktok_live_connector_signer_failed";
  }
  if (text.includes("timeout") || text.includes("timed out")) {
    return "tiktok_live_connector_timeout";
  }
  if (text.includes("not found") || text.includes("user does not exist") || text.includes("uniqueid")) {
    return "tiktok_live_connector_unique_id_not_found";
  }
  return "tiktok_live_connector_connect_failed";
}

function serializeConnectorError(error) {
  if (!error) {
    return {};
  }
  const details = connectorErrorDetails(error);
  const primary = details[0] ?? {};
  return {
    name: primary.name ?? null,
    message: primary.message ?? null,
    code: primary.code ?? null,
    status: primary.status ?? null,
    errors: details.slice(1, 6)
  };
}

function standbyRetryDelayMs(standby = {}, reasonCode = null) {
  const baseDelay = Math.max(1000, Number(standby.poll_interval_ms ?? 5000));
  const configured = standby.retry_delay_ms_by_reason_code?.[reasonCode];
  if (Number.isFinite(Number(configured)) && Number(configured) > 0) {
    return Math.max(1000, Number(configured));
  }
  if (reasonCode === "tiktok_live_connector_signer_rate_limited") {
    return Math.max(baseDelay, 60_000);
  }
  if (reasonCode === "tiktok_live_connector_fetch_blocked") {
    return Math.max(baseDelay, 30_000);
  }
  return baseDelay;
}

function connectorErrorText(error) {
  return connectorErrorDetails(error)
    .flatMap((detail) => [detail.name, detail.message, detail.code, detail.status])
    .filter((value) => value !== null && value !== undefined && value !== "")
    .map((value) => String(value))
    .join(" ");
}

function connectorErrorDetails(error, depth = 0) {
  if (!error || depth > 3) {
    return [];
  }
  const details = [connectorErrorDetail(error)];
  for (const nested of connectorNestedErrors(error)) {
    if (Array.isArray(nested)) {
      for (const item of nested) {
        details.push(...connectorErrorDetails(item, depth + 1));
      }
    } else {
      details.push(...connectorErrorDetails(nested, depth + 1));
    }
  }
  return details.filter((detail) => Object.values(detail).some((value) => value !== null && value !== undefined && value !== ""));
}

function connectorErrorDetail(error) {
  if (typeof error === "string") {
    return {
      name: null,
      message: error,
      code: null,
      status: null
    };
  }
  const message = connectorErrorMessage(error);
  return {
    name: connectorErrorTextValue(error?.name),
    message,
    code: connectorErrorTextValue(error?.code ?? error?.error_code),
    status: error?.status ?? error?.statusCode ?? error?.response?.status ?? null
  };
}

function connectorNestedErrors(error) {
  if (!error || typeof error !== "object") {
    return [];
  }
  return [
    error.errors,
    error.cause,
    error.exception,
    error.error,
    error.response?.data,
    error.data
  ].filter(Boolean);
}

function connectorErrorMessage(error) {
  const explicit = firstDefined(
    error?.message,
    error?.error_message,
    error?.reason,
    error?.info,
    error?.exception?.message,
    error?.response?.data?.message,
    error?.response?.data?.error?.message,
    error?.data?.message
  );
  if (explicit !== undefined && explicit !== null && explicit !== "") {
    return connectorErrorTextValue(explicit);
  }
  if (error && typeof error === "object" && !(error instanceof Error)) {
    return connectorErrorTextValue(error);
  }
  return null;
}

function connectorErrorTextValue(value) {
  if (value === undefined || value === null) {
    return null;
  }
  if (typeof value === "string") {
    const trimmed = value.trim();
    return trimmed ? trimmed.slice(0, 1000) : null;
  }
  if (typeof value === "number" || typeof value === "boolean") {
    return String(value);
  }
  try {
    const serialized = JSON.stringify(value);
    return serialized ? serialized.slice(0, 1000) : null;
  } catch {
    return String(value).slice(0, 1000);
  }
}

function findNestedRoomId(value, depth = 0) {
  if (!value || depth > 8) {
    return null;
  }
  if (Array.isArray(value)) {
    for (const item of value) {
      const found = findNestedRoomId(item, depth + 1);
      if (found) {
        return found;
      }
    }
    return null;
  }
  if (typeof value !== "object") {
    return null;
  }
  for (const key of ["roomId", "room_id", "roomID"]) {
    const candidate = stringOrNull(value[key]);
    if (candidate && /^\d+$/.test(candidate)) {
      return candidate;
    }
  }
  for (const nested of Object.values(value)) {
    const found = findNestedRoomId(nested, depth + 1);
    if (found) {
      return found;
    }
  }
  return null;
}

function fillTiktokWebcastUrlTemplate(template, { roomId, cursor = null } = {}) {
  return String(template)
    .replaceAll("{room_id}", encodeURIComponent(roomId ?? ""))
    .replaceAll("{roomId}", encodeURIComponent(roomId ?? ""))
    .replaceAll("{cursor}", encodeURIComponent(cursor ?? ""));
}

function validateTiktokWebcastTargetUrl({ websocketUrl, liveRoomId = null, allowedHosts = [] } = {}) {
  let parsed;
  try {
    parsed = new URL(websocketUrl);
  } catch {
    return {
      ok: false,
      reason_code: "tiktok_webcast_url_invalid"
    };
  }
  if (!["ws:", "wss:"].includes(parsed.protocol)) {
    return {
      ok: false,
      reason_code: "tiktok_webcast_url_scheme_invalid"
    };
  }
  if (Array.isArray(allowedHosts) && allowedHosts.length > 0 && !allowedHosts.includes(parsed.hostname)) {
    return {
      ok: false,
      reason_code: "tiktok_webcast_url_host_not_allowed"
    };
  }
  const urlRoomId = parsed.searchParams.get("room_id") ?? parsed.searchParams.get("roomId") ?? parsed.searchParams.get("room");
  if (liveRoomId && urlRoomId && urlRoomId !== String(liveRoomId)) {
    return {
      ok: false,
      reason_code: "tiktok_webcast_url_room_mismatch"
    };
  }
  return {
    ok: true,
    reason_code: "tiktok_webcast_url_ready"
  };
}

async function fetchWithOptionalTimeout(fetchImpl, url, options = {}, timeoutMs = 0) {
  if (!timeoutMs || typeof AbortController === "undefined") {
    return fetchImpl(url, options);
  }
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), Number(timeoutMs));
  try {
    return await fetchImpl(url, {
      ...options,
      signal: controller.signal
    });
  } finally {
    clearTimeout(timer);
  }
}

function decodeWebcastFramePayload(frame) {
  if (frame === undefined || frame === null) {
    return {
      ok: false,
      reason_code: "tiktok_webcast_frame_empty",
      hash_input: null
    };
  }
  if (typeof frame === "object" && !isBinaryLike(frame)) {
    return {
      ok: true,
      payload: frame
    };
  }
  const protoDecoded = decodeTiktokConnectorProtoWebcastFrame(frame);
  if (protoDecoded) {
    return protoDecoded;
  }
  const protoFetchDecoded = decodeTiktokConnectorProtoFetchResultFrame(frame);
  if (protoFetchDecoded) {
    return protoFetchDecoded;
  }
  const text = frameToUtf8Text(frame);
  if (text === null) {
    return {
      ok: false,
      reason_code: "tiktok_webcast_binary_frame_requires_decoder",
      hash_input: frameByteLength(frame)
    };
  }
  const parsed = parseJsonMaybe(text);
  if (parsed.error) {
    return {
      ok: false,
      reason_code: "tiktok_webcast_frame_parse_failed",
      hash_input: text
    };
  }
  const connectorFetchPayload = normalizeTiktokConnectorFetchResultPayload(parsed.value);
  if (connectorFetchPayload) {
    return {
      ok: true,
      payload: connectorFetchPayload
    };
  }
  return {
    ok: true,
    payload: parsed.value
  };
}

function decodeTiktokConnectorProtoWebcastFrame(frame) {
  const schema = loadTiktokConnectorSchema();
  const bytes = frameToBuffer(frame);
  if (!schema || !bytes || bytes.byteLength === 0) {
    return null;
  }
  let pushFrame;
  try {
    pushFrame = schema.WebcastPushFrame.decode(bytes);
  } catch {
    return null;
  }
  const payloadEncoding = String(pushFrame?.payloadEncoding ?? "").toLowerCase();
  if (!["pb", "gzip"].includes(payloadEncoding) || !pushFrame.payload || pushFrame.payload.length === 0) {
    return null;
  }
  let payloadBytes = Buffer.from(pushFrame.payload);
  if (isGzipPayload(payloadBytes)) {
    try {
      payloadBytes = unzipSync(payloadBytes);
    } catch {
      return {
        ok: false,
        reason_code: "tiktok_webcast_proto_gzip_decode_failed",
        hash_input: frameByteLength(frame)
      };
    }
  }
  const transportMetadata = tiktokPushFrameTransportMetadata(pushFrame);
  if (isTiktokNonEventPushFrame(transportMetadata)) {
    return {
      ok: true,
      payload: {
        type: "tiktok_browser_transport_frame",
        transport_frame_type: "non_event_control",
        transport_metadata: transportMetadata,
        messages: []
      }
    };
  }
  let fetchResult;
  try {
    fetchResult = schema.ProtoMessageFetchResult.decode(payloadBytes);
  } catch {
    return null;
  }
  const messages = (fetchResult.messages ?? []).map((message) => decodeTiktokConnectorProtoMessage(message, schema));
  return {
    ok: true,
    payload: {
      type: "tiktok_connector_proto_fetch",
      transport_frame_type: "live_event",
      transport_metadata: transportMetadata,
      cursor: fetchResult.cursor ?? null,
      internalExt: fetchResult.internalExt ?? null,
      payloadEncoding: pushFrame.payloadEncoding,
      payloadType: pushFrame.payloadType,
      messages
    }
  };
}

function decodeTiktokConnectorProtoFetchResultFrame(frame) {
  const schema = loadTiktokConnectorSchema();
  const bytes = frameToBuffer(frame);
  if (!schema || !bytes || bytes.byteLength === 0 || !schema.ProtoMessageFetchResult?.decode) {
    return null;
  }
  let fetchResult;
  try {
    fetchResult = schema.ProtoMessageFetchResult.decode(bytes);
  } catch {
    return null;
  }
  const payload = normalizeTiktokConnectorFetchResultPayload(fetchResult, { requireEvidence: true });
  return payload ? { ok: true, payload } : null;
}

function normalizeTiktokConnectorFetchResultPayload(fetchResult, { requireEvidence = false } = {}) {
  if (!fetchResult || typeof fetchResult !== "object" || !Array.isArray(fetchResult.messages)) {
    return null;
  }
  const schema = loadTiktokConnectorSchema();
  const messages = fetchResult.messages.map((message) => {
    const decoded = decodeTiktokConnectorProtoMessageFromFetchItem(message, schema);
    return decoded ?? message;
  });
  if (requireEvidence
    && messages.length === 0
    && !fetchResult.cursor
    && !fetchResult.internalExt
    && !fetchResult.internal_ext) {
    return null;
  }
  return {
    ...fetchResult,
    type: "tiktok_connector_proto_fetch",
    cursor: fetchResult.cursor ?? null,
    internalExt: fetchResult.internalExt ?? fetchResult.internal_ext ?? null,
    messages
  };
}

function loadTiktokConnectorSchema() {
  if (tiktokConnectorSchemaCache !== undefined) {
    return tiktokConnectorSchemaCache;
  }
  try {
    tiktokConnectorSchemaCache = nodeRequire("tiktok-live-connector/dist/types/tiktok-schema");
  } catch {
    tiktokConnectorSchemaCache = null;
  }
  return tiktokConnectorSchemaCache;
}

function decodeTiktokConnectorProtoMessage(message = {}, schema = {}) {
  const type = String(message.type ?? "");
  const decoder = schema[type];
  let data = null;
  let decodeError = null;
  if (decoder?.decode && message.payload) {
    try {
      data = decoder.decode(Buffer.from(message.payload));
    } catch (error) {
      decodeError = error?.message ?? "decode_failed";
    }
  }
  if (!data) {
    return {
      type,
      message_type: type,
      msgId: stringOrNull(message.msgId),
      decode_error: decodeError
    };
  }
  return tiktokConnectorProtoMessageToWebcastItem({
    type,
    msgId: stringOrNull(message.msgId),
    data
  });
}

function decodeTiktokConnectorProtoMessageFromFetchItem(message = {}, schema = {}) {
  const type = String(firstDefined(message.type, message.method, message.message_type, message.messageType) ?? "");
  if (!type || !schema?.[type]?.decode) {
    return null;
  }
  const payload = firstDefined(message.payload, message.payloadData, message.data);
  const payloadBytes = bufferFromMaybeEncodedPayload(payload);
  if (!payloadBytes) {
    return null;
  }
  return decodeTiktokConnectorProtoMessage({
    type,
    payload: payloadBytes,
    msgId: stringOrNull(firstDefined(message.msgId, message.msg_id, message.id)),
    msgType: message.msgType ?? message.msg_type ?? 0,
    offset: message.offset ?? "0",
    isHistory: message.isHistory ?? message.is_history ?? false
  }, schema);
}

function tiktokConnectorProtoMessageToWebcastItem({ type, msgId = null, data = {} } = {}) {
  const common = data.common ?? {};
  const base = {
    type: tiktokConnectorProtoEventType(type),
    message_type: type,
    raw_message_type: type,
    msgId: stringOrNull(firstDefined(common.msgId, msgId)),
    roomId: stringOrNull(common.roomId),
    createTime: firstDefined(common.createTime, common.clientSendTime),
    timestamp: firstDefined(common.createTime, common.clientSendTime),
    data
  };
  if (type === "WebcastGiftMessage") {
    const gift = data.giftDetails ?? {};
    return {
      ...base,
      type: "gift",
      giftId: firstDefined(data.giftId, gift.id),
      gift_id: stringOrNull(firstDefined(gift.id, data.giftId)),
      gift_name: stringOrNull(firstDefined(gift.giftName, gift.name, gift.describe)),
      gift_unit_value: numberOrNull(firstDefined(gift.diamondCount, gift.diamond_count)),
      giftType: firstDefined(gift.giftType, data.giftType),
      gift,
      giftDetails: gift,
      user: data.user ?? {},
      recipient: data.toUser ?? {},
      repeatCount: numberOrNull(data.repeatCount),
      repeatEnd: protoBoolean(data.repeatEnd),
      comboCount: numberOrNull(data.comboCount),
      groupId: stringOrNull(data.groupId),
      comboId: stringOrNull(data.groupId),
      orderId: stringOrNull(data.orderId),
      fanTicketCount: stringOrNull(data.fanTicketCount)
    };
  }
  if (type === "WebcastChatMessage") {
    return {
      ...base,
      type: "chat",
      comment: data.content ?? data.comment ?? "",
      user: data.user ?? {}
    };
  }
  if (type === "WebcastMemberMessage") {
    return {
      ...base,
      type: "member",
      user: data.user ?? {},
      action: data.action ?? null
    };
  }
  if (type === "WebcastLikeMessage") {
    return {
      ...base,
      type: "like",
      user: data.user ?? {},
      likeCount: numberOrNull(firstDefined(data.count, data.likeCount)),
      totalLikeCount: numberOrNull(data.total)
    };
  }
  if (type === "WebcastRoomUserSeqMessage") {
    return {
      ...base,
      type: "roomUser",
      totalUserCount: numberOrNull(firstDefined(data.total, data.totalUser, data.totalUserCount)),
      viewerCount: numberOrNull(firstDefined(data.total, data.viewerCount))
    };
  }
  return {
    ...base,
    type
  };
}

function tiktokConnectorProtoEventType(type) {
  const map = {
    WebcastGiftMessage: "gift",
    WebcastChatMessage: "chat",
    WebcastMemberMessage: "member",
    WebcastLikeMessage: "like",
    WebcastRoomUserSeqMessage: "roomUser",
    WebcastSocialMessage: "social",
    WebcastControlMessage: "control"
  };
  return map[type] ?? type;
}

function tiktokPushFrameTransportMetadata(pushFrame = {}) {
  const headers = {};
  for (const [key, value] of Object.entries(pushFrame.headers ?? {})) {
    headers[String(key).toLowerCase()] = String(value ?? "");
  }
  return {
    service: stringOrNull(pushFrame.service),
    method: stringOrNull(pushFrame.method),
    payload_encoding: stringOrNull(pushFrame.payloadEncoding),
    payload_type: stringOrNull(pushFrame.payloadType),
    psm: stringOrNull(headers["x-psm"]),
    frontier_method: stringOrNull(headers["x-method"]),
    frontier_message_id: stringOrNull(headers[":x_frontier_msg_id"]),
    frontier_trace_id: stringOrNull(headers["x_frontier_traceid"])
  };
}

function isTiktokNonEventPushFrame(metadata = {}) {
  const psm = String(metadata.psm ?? "").toLowerCase();
  if (psm.includes("bsync.cache_svr")) {
    return true;
  }
  return false;
}

function isTiktokNonEventTransportFrame(decoded = {}) {
  const frameType = String(decoded?.transport_frame_type ?? "").toLowerCase();
  if (frameType.includes("non_event")) {
    return true;
  }
  const psm = String(decoded?.transport_metadata?.psm ?? "").toLowerCase();
  return psm.includes("bsync.cache_svr");
}

function protoBoolean(value) {
  if (value === true || value === false) {
    return value;
  }
  const numeric = numberOrNull(value);
  if (numeric !== null) {
    return numeric !== 0;
  }
  return Boolean(value);
}

function isGzipPayload(bytes) {
  return bytes && bytes.length > 2 && bytes[0] === 0x1f && bytes[1] === 0x8b && bytes[2] === 0x08;
}

function isBinaryLike(value) {
  return typeof Buffer !== "undefined" && Buffer.isBuffer(value)
    || value instanceof ArrayBuffer
    || ArrayBuffer.isView(value);
}

function frameToBuffer(frame) {
  if (typeof Buffer !== "undefined" && Buffer.isBuffer(frame)) {
    return frame;
  }
  if (frame instanceof ArrayBuffer) {
    return Buffer.from(frame);
  }
  if (ArrayBuffer.isView(frame)) {
    return Buffer.from(frame.buffer, frame.byteOffset, frame.byteLength);
  }
  return null;
}

function bufferFromMaybeEncodedPayload(payload) {
  if (payload === undefined || payload === null) {
    return null;
  }
  if (typeof Buffer !== "undefined" && Buffer.isBuffer(payload)) {
    return payload;
  }
  if (payload instanceof ArrayBuffer) {
    return Buffer.from(payload);
  }
  if (ArrayBuffer.isView(payload)) {
    return Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength);
  }
  if (Array.isArray(payload)) {
    return Buffer.from(payload);
  }
  if (payload && typeof payload === "object" && Array.isArray(payload.data)) {
    return Buffer.from(payload.data);
  }
  if (typeof payload === "string") {
    const text = payload.trim();
    if (!text) {
      return null;
    }
    try {
      return Buffer.from(text, "base64");
    } catch {
      return Buffer.from(text);
    }
  }
  return null;
}

function frameToUtf8Text(frame) {
  if (typeof frame === "string") {
    return frame;
  }
  if (typeof Buffer !== "undefined" && Buffer.isBuffer(frame)) {
    return frame.toString("utf8");
  }
  if (frame instanceof ArrayBuffer) {
    return Buffer.from(frame).toString("utf8");
  }
  if (ArrayBuffer.isView(frame)) {
    return Buffer.from(frame.buffer, frame.byteOffset, frame.byteLength).toString("utf8");
  }
  return null;
}

function frameByteLength(frame) {
  if (typeof frame === "string") {
    return frame.length;
  }
  if (typeof Buffer !== "undefined" && Buffer.isBuffer(frame)) {
    return frame.byteLength;
  }
  if (frame instanceof ArrayBuffer) {
    return frame.byteLength;
  }
  if (ArrayBuffer.isView(frame)) {
    return frame.byteLength;
  }
  return null;
}

function extractTiktokWebcastMessageItems(payload) {
  if (!payload) {
    return [];
  }
  if (Array.isArray(payload)) {
    return payload;
  }
  for (const key of ["events", "messages", "gift_events", "gifts", "payload", "data"]) {
    const value = payload[key];
    if (Array.isArray(value)) {
      return value;
    }
    if (value && typeof value === "object") {
      const nested = extractTiktokWebcastMessageItems(value);
      if (nested.length > 0) {
        return nested;
      }
    }
  }
  return [payload];
}

function isTiktokGiftMessage(item = {}) {
  const type = String(item.type ?? item.event_type ?? item.event ?? item.method ?? item.message_type ?? item.messageType ?? "").toLowerCase();
  if (["gift", "gift_event", "webcastgiftmessage", "webcast_gift_message"].includes(type)) {
    return true;
  }
  if (item.gift || item.giftId || item.gift_id || item.giftName || item.gift_name) {
    return true;
  }
  return false;
}

function extractTiktokWebcastGiftShape(payload = {}) {
  const parsed = typeof payload === "string" ? parseJsonMaybe(payload).value : payload;
  if (!parsed || typeof parsed !== "object") {
    return {};
  }
  if (parsed.payload && typeof parsed.payload === "object" && !Array.isArray(parsed.payload)) {
    return {
      ...parsed,
      ...extractTiktokWebcastGiftShape(parsed.payload)
    };
  }
  if (parsed.data && typeof parsed.data === "object" && !Array.isArray(parsed.data)) {
    return {
      ...parsed,
      ...extractTiktokWebcastGiftShape(parsed.data)
    };
  }
  if (parsed.event && typeof parsed.event === "object" && !Array.isArray(parsed.event)) {
    return {
      ...parsed,
      ...parsed.event
    };
  }
  if (parsed.message && typeof parsed.message === "object" && !Array.isArray(parsed.message)) {
    return {
      ...parsed,
      ...parsed.message
    };
  }
  return parsed;
}

function normalizeTiktokEventTime(value) {
  if (value instanceof Date) {
    return value.toISOString();
  }
  if (typeof value === "string") {
    const asNumber = Number(value);
    if (!Number.isFinite(asNumber)) {
      const parsed = Date.parse(value);
      return Number.isNaN(parsed) ? nowIso() : new Date(parsed).toISOString();
    }
    return timestampToIso(asNumber) ?? nowIso();
  }
  if (typeof value === "number") {
    return timestampToIso(value) ?? nowIso();
  }
  return nowIso();
}

function createTiktokSyntheticEventId({
  liveRoomId,
  userId,
  giftId,
  eventTime,
  giftCount,
  repeatEnd
} = {}) {
  const hash = createHash("sha256")
    .update(stableStringify({
      liveRoomId,
      userId,
      giftId,
      eventTime,
      giftCount,
      repeatEnd
    }))
    .digest("hex")
    .slice(0, 16);
  return `tiktok_synthetic_${hash}`;
}

function classifyTiktokGiftRisk(gift = null, {
  recentEvents = [],
  bigGiftValueThreshold = 100000,
  highFrequencyEventsPerSecondThreshold = 20,
  mismatches = []
} = {}) {
  if (!gift) {
    return {
      risk_level: TIKTOK_GIFT_RISK_LEVELS.NORMAL,
      risk_reasons: []
    };
  }
  const reasons = [];
  const giftMatchKey = gift.match_key ?? makeGiftMatchKey(gift);
  if (mismatches.some((mismatch) => mismatch.match_key === giftMatchKey || mismatch.idempotency_key === gift.idempotency_key)) {
    reasons.push("mismatch");
  }
  if (Number(gift.gift_total_value ?? 0) >= Number(bigGiftValueThreshold)) {
    reasons.push("big_gift");
  }
  if (gift.gift_combo_id && gift.is_combo_final !== true) {
    reasons.push("combo_in_progress");
  }
  const eventBucket = Math.floor(toEpochMs(gift.event_time) / 1000);
  const sameBucketCount = recentEvents.filter((event) => {
    return event.live_room_id === gift.live_room_id
      && event.gifter_user_id === gift.gifter_user_id
      && event.gift_id === gift.gift_id
      && Math.floor(toEpochMs(event.event_time) / 1000) === eventBucket;
  }).length;
  if (sameBucketCount >= Number(highFrequencyEventsPerSecondThreshold)) {
    reasons.push("high_frequency");
  }
  let riskLevel = TIKTOK_GIFT_RISK_LEVELS.NORMAL;
  if (reasons.includes("mismatch")) {
    riskLevel = TIKTOK_GIFT_RISK_LEVELS.MISMATCH;
  } else if (reasons.includes("big_gift")) {
    riskLevel = TIKTOK_GIFT_RISK_LEVELS.BIG_GIFT;
  } else if (reasons.includes("combo_in_progress")) {
    riskLevel = TIKTOK_GIFT_RISK_LEVELS.COMBO_IN_PROGRESS;
  } else if (reasons.includes("high_frequency")) {
    riskLevel = TIKTOK_GIFT_RISK_LEVELS.HIGH_FREQUENCY;
  }
  return {
    risk_level: riskLevel,
    risk_reasons: reasons
  };
}

function tiktokResilienceActions(decision, issues = []) {
  if (issues.length === 0) {
    return ["继续主备双线路监控"];
  }
  const reasonCodes = new Set(issues.map((issue) => issue.reason_code));
  const actions = [];
  if (reasonCodes.has("tiktok_big_gift_backup_confirmation_required")) {
    actions.push("大礼物等待备用线路或下播复核确认后结算");
  }
  if (reasonCodes.has("tiktok_big_gift_backup_takeover_requires_review")) {
    actions.push("备线接管的大礼物先标记为待下播复核");
  }
  if (reasonCodes.has("tiktok_combo_wait_repeat_end")) {
    actions.push("连击礼物等待 repeatEnd/final 事件后入账");
  }
  if (reasonCodes.has("tiktok_high_frequency_review_required")) {
    actions.push("高频礼物窗口进入下播复核抽检");
  }
  if (reasonCodes.has("tiktok_primary_backup_mismatch")) {
    actions.push("冻结该礼物的自动结算并进入人工复核");
  }
  if (reasonCodes.has("tiktok_no_realtime_route_available")) {
    actions.push("切换到下播补数据流程并保留本地 raw backup");
  }
  if (reasonCodes.has("tiktok_route_confirmation_required")) {
    actions.push("扩大确认窗口并等待任一实时线路恢复");
  }
  if (decision === TIKTOK_GIFT_RESILIENCE_DECISIONS.BACKUP_TAKEOVER) {
    actions.push("记录备线接管并下播复核");
  }
  return [...new Set(actions)];
}

function createTiktokBackfillCorrection({
  action,
  reason_code,
  event,
  realtimeEvent = null,
  matchKey,
  generatedAt
} = {}) {
  return {
    correction_id: createId("tiktok_gift_correction"),
    action,
    reason_code,
    match_key: matchKey,
    source_event_id: event?.source_event_id ?? null,
    live_room_id: event?.live_room_id ?? null,
    gifter_user_id: event?.gifter_user_id ?? null,
    gift_id: event?.gift_id ?? null,
    gift_count: Number(event?.gift_count ?? 0),
    gift_total_value: Number(event?.gift_total_value ?? 0),
    realtime_gift_count: realtimeEvent ? Number(realtimeEvent.gift_count ?? 0) : null,
    realtime_gift_total_value: realtimeEvent ? Number(realtimeEvent.gift_total_value ?? 0) : null,
    generated_at: generatedAt
  };
}

function tiktokBackfillActions({
  officialCount,
  backupOnlyCount,
  officialOnlyCount,
  adjustmentCount,
  realtimeOnlyCount,
  mismatchCount,
  roomMismatchCount
} = {}) {
  const actions = [];
  if (officialCount === 0) {
    actions.push("下播后导入 TikTok/MCA/运营复核礼物明细");
  }
  if (backupOnlyCount > 0) {
    actions.push("回放备用线路补入主线路漏收礼物");
  }
  if (officialOnlyCount > 0) {
    actions.push("从下播复核明细补入直播期间漏收礼物");
  }
  if (adjustmentCount > 0) {
    actions.push("生成礼物数量或价值修正单");
  }
  if (realtimeOnlyCount > 0) {
    actions.push("复核直播中记录是否为误收或复核明细缺失");
  }
  if (mismatchCount > 0) {
    actions.push("复核主备冲突并冻结相关结算");
  }
  if (roomMismatchCount > 0) {
    actions.push("确认补数文件属于当前 TikTok 直播间");
  }
  if (actions.length === 0) {
    actions.push("补数据复核完成，保留审计记录");
  }
  return [...new Set(actions)];
}

function createBacktestGate(gateId, ok, action) {
  return {
    gate_id: gateId,
    ok: ok === true,
    action
  };
}

export class DouyinLiveDataOpenApiClient {
  constructor({
    appId,
    accessToken,
    baseUrl = DOUYIN_LIVE_DATA_ENDPOINTS.PRODUCTION_BASE_URL,
    fetchImpl = globalThis.fetch
  } = {}) {
    this.appId = appId;
    this.accessToken = accessToken;
    this.baseUrl = baseUrl;
    this.fetchImpl = fetchImpl;
  }

  startGiftTask({ roomId } = {}) {
    return this.#request(DOUYIN_LIVE_DATA_ENDPOINTS.START_TASK, {
      method: "POST",
      body: {
        appid: this.appId,
        roomid: roomId,
        msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT
      }
    });
  }

  stopGiftTask({ roomId } = {}) {
    return this.#request(DOUYIN_LIVE_DATA_ENDPOINTS.STOP_TASK, {
      method: "POST",
      body: {
        appid: this.appId,
        roomid: roomId,
        msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT
      }
    });
  }

  queryGiftTaskStatus({ roomId } = {}) {
    return this.#request(DOUYIN_LIVE_DATA_ENDPOINTS.QUERY_TASK, {
      method: "GET",
      query: {
        appid: this.appId,
        roomid: roomId,
        msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT
      }
    });
  }

  queryFailedGiftDataPage({ roomId, pageNum = 1, pageSize = 100 } = {}) {
    const safePageNum = Math.max(1, Number(pageNum) || 1);
    const safePageSize = Math.min(100, Math.max(1, Number(pageSize) || 100));
    return this.#request(DOUYIN_LIVE_DATA_ENDPOINTS.QUERY_FAILED_DATA, {
      method: "GET",
      query: {
        appid: this.appId,
        roomid: roomId,
        msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT,
        page_num: safePageNum,
        page_size: safePageSize
      }
    });
  }

  reportGiftAck({ roomId, ackType, items = [] } = {}) {
    return this.#request(DOUYIN_LIVE_DATA_ENDPOINTS.REPORT_ACK, {
      method: "POST",
      body: {
        app_id: this.appId,
        room_id: roomId,
        ack_type: Number(ackType),
        data: JSON.stringify(items)
      }
    });
  }

  async #request(path, { method = "GET", query = {}, body = null } = {}) {
    if (!this.fetchImpl) {
      return {
        ok: false,
        reason_code: "fetch_not_available"
      };
    }
    if (!this.appId) {
      return {
        ok: false,
        reason_code: "douyin_app_id_required"
      };
    }
    if (!this.accessToken) {
      return {
        ok: false,
        reason_code: "douyin_access_token_required"
      };
    }
    const url = new URL(path, this.baseUrl);
    for (const [key, value] of Object.entries(query)) {
      if (value !== undefined && value !== null) {
        url.searchParams.set(key, String(value));
      }
    }
    let response;
    try {
      response = await this.fetchImpl(String(url), {
        method,
        headers: {
          "access-token": this.accessToken,
          "content-type": "application/json"
        },
        body: body ? JSON.stringify(body) : undefined
      });
    } catch (error) {
      return {
        ok: false,
        reason_code: "douyin_openapi_request_failed",
        error_name: error?.name ?? "Error",
        request: {
          method,
          path,
          query,
          msg_type: body?.msg_type ?? query.msg_type ?? null
        }
      };
    }
    const payload = await parseFetchJson(response);
    const errNo = Number(payload?.err_no ?? -1);
    return {
      ok: response?.ok === true && errNo === 0,
      http_status: response?.status ?? null,
      err_no: payload?.err_no ?? null,
      err_msg: payload?.err_msg ?? null,
      logid: payload?.logid ?? null,
      data: payload?.data ?? null,
      request: {
        method,
        path,
        query,
        msg_type: body?.msg_type ?? query.msg_type ?? null
      },
      raw_response_hash: hashRawGiftPayload(payload ?? {})
    };
  }
}

export function createLocalGiftBridgeHeartbeat(input = {}) {
  return {
    heartbeat_id: input.heartbeat_id ?? createId("gift_bridge_heartbeat"),
    collector_id: input.collector_id ?? "local_collector",
    tenant_id: input.tenant_id ?? null,
    live_room_id: input.live_room_id ?? null,
    session_id: input.session_id ?? null,
    source_role: input.source_role ?? GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    connected: input.connected ?? true,
    observed_at: input.observed_at ?? nowIso(),
    event_count: Number(input.event_count ?? 0),
    accepted_count: Number(input.accepted_count ?? 0),
    rejected_count: Number(input.rejected_count ?? 0),
    last_event_at: input.last_event_at ?? null,
    app_version: input.app_version ?? null
  };
}

export function evaluateLocalGiftBridgeHealth({
  heartbeat = null,
  expectedLiveRoomId = null,
  now = nowIso(),
  timeoutMs = 10000,
  rejectedCount = 0
} = {}) {
  if (!heartbeat) {
    return {
      ok: false,
      connected: false,
      health: GIFT_SOURCE_HEALTH.DISCONNECTED,
      stale_ms: null,
      issue_count: 1,
      issues: [createContractIssue("heartbeat", "gift_bridge_heartbeat_missing")],
      customer_message: "礼物记录连接中，请稍等。"
    };
  }

  const issues = [];
  const staleMs = Math.max(0, toEpochMs(now) - toEpochMs(heartbeat.observed_at));
  if (heartbeat.connected !== true) {
    issues.push(createContractIssue("connected", "gift_bridge_disconnected"));
  }
  if (staleMs > Number(timeoutMs)) {
    issues.push(createContractIssue("observed_at", "gift_bridge_heartbeat_stale"));
  }
  if (expectedLiveRoomId && heartbeat.live_room_id && heartbeat.live_room_id !== expectedLiveRoomId) {
    issues.push(createContractIssue("live_room_id", "gift_bridge_room_mismatch"));
  }
  if (Number(rejectedCount) > 0 || Number(heartbeat.rejected_count ?? 0) > 0) {
    issues.push(createContractIssue("rejected_count", "gift_bridge_rejected_records"));
  }

  const disconnected = issues.some((issue) => [
    "gift_bridge_disconnected",
    "gift_bridge_heartbeat_stale",
    "gift_bridge_room_mismatch"
  ].includes(issue.reason_code));
  const attention = issues.length > 0;
  const health = disconnected
    ? GIFT_SOURCE_HEALTH.DISCONNECTED
    : attention
      ? GIFT_SOURCE_HEALTH.NEEDS_REVIEW
      : GIFT_SOURCE_HEALTH.HEALTHY;

  return {
    ok: issues.length === 0,
    connected: !disconnected,
    health,
    stale_ms: staleMs,
    issue_count: issues.length,
    issues,
    customer_message: issues.length === 0 ? "礼物记录连接正常。" : "礼物记录需要检查。"
  };
}

export function createLocalGiftSourceOpsMetrics({
  bridgeHealthStatus = {},
  giftSourceStatus = {},
  autoReaderStatus = {},
  dataConfirmationP95Ms = 0,
  officialAdjustmentRatio = 0
} = {}) {
  const health = bridgeHealthStatus.health ?? {};
  const acceptedCount = Number(bridgeHealthStatus.accepted_count ?? 0);
  const rejectedCount = Number(bridgeHealthStatus.rejected_count ?? 0);
  const totalBridgeCount = acceptedCount + rejectedCount;
  const sourceCount = Number(giftSourceStatus.source_count ?? 0);
  const mismatchCount = Number(giftSourceStatus.mismatch_count ?? 0);
  const reviewCount = Number(giftSourceStatus.review?.mismatch_count ?? mismatchCount);
  const tickCount = Number(autoReaderStatus.tick_count ?? 0);
  const issueTickCount = Number(autoReaderStatus.last_issue_count ?? 0) > 0 ? 1 : 0;

  return {
    data_confirmation_p95_ms: Number(dataConfirmationP95Ms),
    official_adjustment_ratio: Number(officialAdjustmentRatio),
    source_disconnect_ratio: health.connected === false ? 1 : 0,
    gift_record_rejected_ratio: totalBridgeCount > 0 ? rejectedCount / totalBridgeCount : 0,
    gift_record_mismatch_ratio: sourceCount > 0 ? mismatchCount / sourceCount : 0,
    gift_review_ratio: sourceCount > 0 ? reviewCount / sourceCount : 0,
    gift_bridge_stale_ms: Number(health.stale_ms ?? 0),
    local_reader_issue_ratio: tickCount > 0 ? issueTickCount / tickCount : 0,
    route_switch_count: Number(giftSourceStatus.route_switch_count ?? 0),
    review_count: reviewCount
  };
}

export function createLocalGiftSourceDiagnosticSummary({
  bridgeHealthStatus = {},
  giftSourceStatus = {},
  autoReaderStatus = {},
  inboxCursor = {},
  generatedAt = nowIso()
} = {}) {
  const health = bridgeHealthStatus.health ?? {};
  const cursorFiles = Object.values(inboxCursor.files ?? {});
  const processedLineCount = cursorFiles.reduce((sum, file) => sum + Number(file.processed_line_count ?? 0), 0);
  const missingFileCount = cursorFiles.filter((file) => file.missing === true).length;
  const readyFileCount = Number(giftSourceStatus.inbox?.ready_file_count ?? 0);
  const acceptedCount = Number(bridgeHealthStatus.accepted_count ?? 0);
  const rejectedCount = Number(bridgeHealthStatus.rejected_count ?? 0);
  const totalBridgeCount = acceptedCount + rejectedCount;
  const mismatchCount = Number(giftSourceStatus.mismatch_count ?? 0);
  const disconnected = health.connected === false;
  const needsReview = rejectedCount > 0 || mismatchCount > 0 || Number(health.issue_count ?? 0) > 0;
  const level = disconnected
    ? GIFT_SOURCE_HEALTH.DISCONNECTED
    : needsReview
      ? GIFT_SOURCE_HEALTH.NEEDS_REVIEW
      : GIFT_SOURCE_HEALTH.HEALTHY;

  return {
    ok: level === GIFT_SOURCE_HEALTH.HEALTHY,
    generated_at: generatedAt,
    tenant_id: bridgeHealthStatus.tenant_id ?? null,
    live_room_id: bridgeHealthStatus.live_room_id ?? giftSourceStatus.live_room_id ?? null,
    level,
    customer_message: level === GIFT_SOURCE_HEALTH.HEALTHY ? "礼物记录连接正常。" : "礼物记录需要检查。",
    source_health: {
      disconnected_count: disconnected ? 1 : 0,
      slow_source_count: Number(health.stale_ms ?? 0) > 3000 ? 1 : 0,
      route_switch_count: Number(giftSourceStatus.route_switch_count ?? 0),
      review_count: rejectedCount + mismatchCount,
      rejected_record_ratio: totalBridgeCount > 0 ? rejectedCount / totalBridgeCount : 0,
      mismatch_count: mismatchCount
    },
    bridge: {
      connected: health.connected === true,
      health: health.health ?? GIFT_SOURCE_HEALTH.DISCONNECTED,
      stale_ms: health.stale_ms ?? null,
      issue_count: Number(health.issue_count ?? 0),
      issue_codes: (health.issues ?? []).map((issue) => issue.reason_code),
      accepted_count: acceptedCount,
      rejected_count: rejectedCount,
      rejected_record_ratio: totalBridgeCount > 0 ? rejectedCount / totalBridgeCount : 0,
      last_append_at: bridgeHealthStatus.last_append_at ?? null,
      last_heartbeat_at: bridgeHealthStatus.last_heartbeat?.observed_at ?? null
    },
    reader: {
      enabled: autoReaderStatus.enabled === true,
      timer_active: autoReaderStatus.timer_active === true,
      tick_count: Number(autoReaderStatus.tick_count ?? 0),
      imported_count: Number(autoReaderStatus.imported_count ?? 0),
      last_tick_at: autoReaderStatus.last_tick_at ?? null,
      last_imported_count: Number(autoReaderStatus.last_imported_count ?? 0),
      last_issue_count: Number(autoReaderStatus.last_issue_count ?? 0)
    },
    inbox: {
      ready_file_count: readyFileCount,
      file_count: cursorFiles.length,
      missing_file_count: missingFileCount,
      processed_line_count: processedLineCount,
      last_event_time: latestCursorEventTime(cursorFiles)
    },
    counts: {
      primary_count: Number(giftSourceStatus.primary_count ?? 0),
      backup_count: Number(giftSourceStatus.backup_count ?? 0),
      guardian_count: Number(giftSourceStatus.guardian_count ?? 0),
      official_review_count: Number(giftSourceStatus.official_review_count ?? 0),
      confirmed_by_backup_count: Number(giftSourceStatus.confirmed_by_backup_count ?? 0),
      mismatch_count: mismatchCount
    },
    next_actions: localGiftSourceDiagnosticActions({ disconnected, rejectedCount, mismatchCount, readyFileCount })
  };
}

export function createGiftCollectorFieldTrialPlan(input = {}) {
  return {
    plan_id: input.plan_id ?? createId("gift_collector_trial"),
    created_at: input.created_at ?? nowIso(),
    collector_id: input.collector_id ?? "local_collector",
    expected_live_room_id: input.expected_live_room_id ?? null,
    required_roles: input.required_roles ?? [
      GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
      GIFT_SOURCE_ROLES.BACKUP_REALTIME
    ],
    min_events_per_role: Number(input.min_events_per_role ?? 2),
    heartbeat_timeout_ms: Number(input.heartbeat_timeout_ms ?? 10000),
    max_live_lag_ms_p95: Number(input.max_live_lag_ms_p95 ?? 3000),
    max_rejected_ratio: Number(input.max_rejected_ratio ?? 0),
    max_mismatch_ratio: Number(input.max_mismatch_ratio ?? 0.001),
    require_raw_backup: input.require_raw_backup ?? true,
    require_replay: input.require_replay ?? true,
    require_after_live_review: input.require_after_live_review ?? true,
    allow_mock_data: input.allow_mock_data ?? false,
    customer_message: "礼物记录实测方案已准备。"
  };
}

export function evaluateGiftCollectorFieldTrial({
  plan = createGiftCollectorFieldTrialPlan(),
  contracts = [],
  bridgeHealthStatus = {},
  giftSourceStatus = {},
  autoReaderStatus = {},
  sampleEventsByRole = {},
  replaySummary = null,
  officialReviewStatus = {},
  observedMetrics = {},
  now = nowIso()
} = {}) {
  const trialPlan = {
    ...createGiftCollectorFieldTrialPlan(plan),
    ...plan
  };
  const issues = [];
  const roleSummaries = [];

  for (const role of trialPlan.required_roles) {
    const contract = contracts.find((item) => item.role === role);
    if (!contract) {
      issues.push(createFieldTrialIssue(role, "collector_contract_missing", "blocked"));
    } else {
      const validation = validateGiftSourceAdapterContract(contract);
      for (const issue of validation.issues) {
        issues.push(createFieldTrialIssue(issue.field, issue.reason_code, "blocked", { role }));
      }
      if (trialPlan.require_raw_backup && contract.local_raw_backup_required !== true) {
        issues.push(createFieldTrialIssue("local_raw_backup_required", "collector_raw_backup_required", "blocked", { role }));
      }
      if (trialPlan.require_raw_backup && !contract.local_raw_backup_path) {
        issues.push(createFieldTrialIssue("local_raw_backup_path", "collector_raw_backup_path_required", "blocked", { role }));
      }
    }

    const events = normalizeSampleEventsForRole(sampleEventsByRole, role);
    const eventIssues = validateFieldTrialSamples({
      role,
      events,
      expectedLiveRoomId: trialPlan.expected_live_room_id,
      allowMockData: trialPlan.allow_mock_data
    });
    issues.push(...eventIssues);
    if (events.length < trialPlan.min_events_per_role) {
      issues.push(createFieldTrialIssue("sample_events", "collector_sample_count_too_low", "blocked", {
        role,
        count: events.length,
        required_count: trialPlan.min_events_per_role
      }));
    }
    roleSummaries.push({
      role,
      sample_count: events.length,
      valid_sample_count: Math.max(0, events.length - eventIssues.length)
    });
  }

  const health = bridgeHealthStatus.health ?? evaluateLocalGiftBridgeHealth({
    heartbeat: bridgeHealthStatus.last_heartbeat ?? null,
    expectedLiveRoomId: trialPlan.expected_live_room_id,
    now,
    timeoutMs: trialPlan.heartbeat_timeout_ms,
    rejectedCount: bridgeHealthStatus.rejected_count ?? 0
  });
  if (health.connected !== true) {
    issues.push(createFieldTrialIssue("bridge_health", "collector_connection_not_ready", "blocked"));
  }
  if (trialPlan.expected_live_room_id && bridgeHealthStatus.live_room_id && bridgeHealthStatus.live_room_id !== trialPlan.expected_live_room_id) {
    issues.push(createFieldTrialIssue("live_room_id", "collector_room_mismatch", "blocked"));
  }

  const metrics = {
    ...createLocalGiftSourceOpsMetrics({
      bridgeHealthStatus: {
        ...bridgeHealthStatus,
        health
      },
      giftSourceStatus,
      autoReaderStatus
    }),
    ...observedMetrics
  };
  if (Number(metrics.data_confirmation_p95_ms ?? 0) > trialPlan.max_live_lag_ms_p95) {
    issues.push(createFieldTrialIssue("data_confirmation_p95_ms", "collector_lag_above_target", "blocked", {
      value: Number(metrics.data_confirmation_p95_ms),
      target: trialPlan.max_live_lag_ms_p95
    }));
  }
  if (Number(metrics.gift_record_rejected_ratio ?? 0) > trialPlan.max_rejected_ratio) {
    issues.push(createFieldTrialIssue("gift_record_rejected_ratio", "collector_rejected_ratio_above_target", "blocked", {
      value: Number(metrics.gift_record_rejected_ratio),
      target: trialPlan.max_rejected_ratio
    }));
  }
  if (Number(metrics.gift_record_mismatch_ratio ?? 0) > trialPlan.max_mismatch_ratio) {
    issues.push(createFieldTrialIssue("gift_record_mismatch_ratio", "collector_mismatch_ratio_above_target", "blocked", {
      value: Number(metrics.gift_record_mismatch_ratio),
      target: trialPlan.max_mismatch_ratio
    }));
  }
  if (trialPlan.require_replay && replaySummary?.ok !== true) {
    issues.push(createFieldTrialIssue("replay", "collector_replay_required", "blocked"));
  }
  if (trialPlan.require_after_live_review && officialReviewStatus.ready !== true) {
    issues.push(createFieldTrialIssue("official_review", "collector_after_live_review_required", "blocked"));
  }

  const blockingIssues = issues.filter((issue) => issue.severity === "blocked");
  const level = blockingIssues.length > 0
    ? GIFT_SOURCE_CONTRACT_LEVELS.BLOCKED
    : issues.length > 0
      ? GIFT_SOURCE_CONTRACT_LEVELS.ATTENTION
      : GIFT_SOURCE_CONTRACT_LEVELS.READY;

  return {
    ok: level === GIFT_SOURCE_CONTRACT_LEVELS.READY,
    ready_for_field_trial: level === GIFT_SOURCE_CONTRACT_LEVELS.READY,
    level,
    evaluated_at: now,
    collector_id: trialPlan.collector_id,
    expected_live_room_id: trialPlan.expected_live_room_id,
    issue_count: issues.length,
    blocking_issue_count: blockingIssues.length,
    issues,
    role_summaries: roleSummaries,
    metrics,
    gates: {
      contracts_ready: !issues.some((issue) => issue.reason_code.includes("contract") || issue.reason_code.includes("raw_backup")),
      samples_ready: !issues.some((issue) => issue.reason_code.includes("sample")),
      connection_ready: health.connected === true,
      replay_ready: trialPlan.require_replay ? replaySummary?.ok === true : true,
      after_live_review_ready: trialPlan.require_after_live_review ? officialReviewStatus.ready === true : true
    },
    next_actions: fieldTrialNextActions(issues),
    customer_message: level === GIFT_SOURCE_CONTRACT_LEVELS.READY ? "礼物记录实测准备完成。" : "礼物记录实测需要继续检查。"
  };
}

export function createOfficialGiftReviewStatus({
  realtimeEvents = [],
  officialEvents = [],
  expectedLiveRoomId = null,
  generatedAt = nowIso()
} = {}) {
  const realtimeByKey = eventsByReviewKey(realtimeEvents);
  const officialByKey = eventsByReviewKey(officialEvents);
  const officialKeys = new Set(officialByKey.keys());
  const realtimeKeys = new Set(realtimeByKey.keys());
  const matchedKeys = [...officialKeys].filter((key) => realtimeKeys.has(key));
  const realtimeOnlyKeys = [...realtimeKeys].filter((key) => !officialKeys.has(key));
  const officialOnlyKeys = [...officialKeys].filter((key) => !realtimeKeys.has(key));
  const adjustedKeys = matchedKeys.filter((key) => {
    const realtime = realtimeByKey.get(key);
    const official = officialByKey.get(key);
    return Number(realtime.gift_total_value) !== Number(official.gift_total_value)
      || Number(realtime.gift_count) !== Number(official.gift_count);
  });
  const roomMismatchCount = [...realtimeEvents, ...officialEvents].filter((event) => {
    return expectedLiveRoomId && event.live_room_id && event.live_room_id !== expectedLiveRoomId;
  }).length;
  const ready = officialEvents.length > 0
    && roomMismatchCount === 0
    && adjustedKeys.length === 0
    && realtimeOnlyKeys.length === 0
    && officialOnlyKeys.length === 0;

  return {
    ok: ready,
    ready,
    generated_at: generatedAt,
    expected_live_room_id: expectedLiveRoomId,
    realtime_count: realtimeEvents.length,
    official_count: officialEvents.length,
    matched_count: matchedKeys.length,
    realtime_only_count: realtimeOnlyKeys.length,
    official_only_count: officialOnlyKeys.length,
    adjustment_count: adjustedKeys.length,
    room_mismatch_count: roomMismatchCount,
    next_actions: officialGiftReviewActions({
      officialCount: officialEvents.length,
      realtimeOnlyCount: realtimeOnlyKeys.length,
      officialOnlyCount: officialOnlyKeys.length,
      adjustmentCount: adjustedKeys.length,
      roomMismatchCount
    }),
    customer_message: ready ? "下播复核记录已对齐。" : "下播复核记录需要继续检查。"
  };
}

export function createGiftSourceFieldTrialWorkspace({
  fieldTrial = {},
  giftSourceStatus = {},
  bridgeHealthStatus = {},
  officialReviewStatus = {},
  liveState = {},
  generatedAt = nowIso()
} = {}) {
  const steps = [
    createFieldTrialWorkspaceStep({
      step_id: "room_confirmed",
      title: "确认直播间",
      completed: Boolean(fieldTrial.expected_live_room_id) && !hasFieldTrialIssue(fieldTrial, "room"),
      requirement: "确认正在测试的是当前公会自己的直播间。",
      details: [
        "打开本场要测试的直播间。",
        "确认软件显示的直播间和当前开播画面一致。",
        "如果切换过直播间，先重新进入本页检查。"
      ],
      markers: ["当前直播间", "开播画面", "软件状态"],
      caption: "先确认直播间，再开始礼物测试。",
      action_label: "查看礼物数据",
      action_href: "/gift-source",
      evidence: {
        live_room_id: fieldTrial.expected_live_room_id ?? giftSourceStatus.live_room_id ?? null
      }
    }),
    createFieldTrialWorkspaceStep({
      step_id: "primary_ready",
      title: "主线路收礼",
      completed: roleSampleCount(fieldTrial, GIFT_SOURCE_ROLES.PRIMARY_REALTIME) >= 2
        && !hasRoleSampleIssue(fieldTrial, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
      requirement: "主线路需要收到至少两条真实礼物记录。",
      details: [
        "让测试账号送出两次小额礼物。",
        "回到本页确认主线路数字增加。",
        "看到积分变化后再继续下一步。"
      ],
      markers: ["测试礼物", "主线路数字", "积分变化"],
      caption: "主线路负责直播中快速带动画面变化。",
      action_label: "打开控制台",
      action_href: "/control",
      evidence: {
        sample_count: roleSampleCount(fieldTrial, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
        primary_count: giftSourceStatus.primary_count ?? 0
      }
    }),
    createFieldTrialWorkspaceStep({
      step_id: "backup_ready",
      title: "备用线路复核",
      completed: roleSampleCount(fieldTrial, GIFT_SOURCE_ROLES.BACKUP_REALTIME) >= 2
        && !hasRoleSampleIssue(fieldTrial, GIFT_SOURCE_ROLES.BACKUP_REALTIME),
      requirement: "备用线路需要同步收到至少两条礼物记录。",
      details: [
        "保持备用记录入口打开。",
        "确认同一批测试礼物能在备用线路看到。",
        "如果数字没有变化，先检查备用记录入口。"
      ],
      markers: ["备用入口", "同批礼物", "复核状态"],
      caption: "备用线路用于直播中互相确认，降低误差。",
      action_label: "查看读取进度",
      action_href: "/gift-source/inbox-cursor.json",
      evidence: {
        sample_count: roleSampleCount(fieldTrial, GIFT_SOURCE_ROLES.BACKUP_REALTIME),
        backup_count: giftSourceStatus.backup_count ?? 0
      }
    }),
    createFieldTrialWorkspaceStep({
      step_id: "connection_ready",
      title: "连接检查",
      completed: fieldTrial.gates?.connection_ready === true,
      requirement: "礼物记录连接要保持正常。",
      details: [
        "确认礼物数据页显示连接正常。",
        "测试期间不要关闭礼物记录接收程序。",
        "如果刚刚重开过软件，等页面刷新后再继续。"
      ],
      markers: ["连接状态", "最近记录", "继续观察"],
      caption: "连接稳定后，直播中分数变化才会稳定。",
      action_label: "查看连接状态",
      action_href: "/gift-source/bridge-health.json",
      evidence: {
        connected: bridgeHealthStatus.health?.connected === true,
        accepted_count: bridgeHealthStatus.accepted_count ?? 0,
        rejected_count: bridgeHealthStatus.rejected_count ?? 0
      }
    }),
    createFieldTrialWorkspaceStep({
      step_id: "replay_ready",
      title: "回放检查",
      completed: fieldTrial.gates?.replay_ready === true,
      requirement: "同一批礼物要能重新检查并得到一致结果。",
      details: [
        "完成一轮主备礼物测试。",
        "确认本页显示回放检查已完成。",
        "再进行下播复核导入。"
      ],
      markers: ["测试批次", "重新检查", "结果一致"],
      caption: "回放检查用于确认本机记录可以复盘。",
      action_label: "查看实测结果",
      action_href: "/real-room-test.json",
      evidence: {
        replay_ready: fieldTrial.gates?.replay_ready === true
      }
    }),
    createFieldTrialWorkspaceStep({
      step_id: "official_review_ready",
      title: "下播复核",
      completed: fieldTrial.gates?.after_live_review_ready === true && officialReviewStatus.ready === true,
      requirement: "下播后把官方复核记录导入，并确认和直播中记录对齐。",
      details: [
        "下播后导出本场礼物明细。",
        "导入复核记录。",
        "确认页面显示下播复核记录已对齐。"
      ],
      markers: ["下播明细", "导入复核", "结果对齐"],
      caption: "下播复核决定这条链路能不能进入外部测试。",
      action_label: "导入复核记录",
      action_href: "/real-room-test",
      evidence: {
        official_count: officialReviewStatus.official_count ?? giftSourceStatus.official_review_count ?? 0,
        matched_count: officialReviewStatus.matched_count ?? 0,
        adjustment_count: officialReviewStatus.adjustment_count ?? 0
      }
    })
  ];
  const requiredSteps = steps.filter((step) => step.required);
  const completedRequired = requiredSteps.filter((step) => step.completed);
  const ready = fieldTrial.ready_for_field_trial === true && completedRequired.length === requiredSteps.length;

  return {
    ok: true,
    workspace_id: createId("gift_trial_workspace"),
    generated_at: generatedAt,
    title: "直播间实测",
    ready_for_external_test: ready,
    completed_count: completedRequired.length,
    total_count: requiredSteps.length,
    progress_ratio: requiredSteps.length ? completedRequired.length / requiredSteps.length : 1,
    status_label: ready ? "可以小范围实测" : "继续检查",
    customer_message: ready ? "直播间实测准备完成，可以进入小范围观察。" : "直播间实测需要继续检查。",
    steps,
    health_cards: createFieldTrialHealthCards({
      fieldTrial,
      giftSourceStatus,
      bridgeHealthStatus,
      officialReviewStatus
    }),
    next_actions: fieldTrialWorkspaceActions(steps, fieldTrial, officialReviewStatus),
    live_state: {
      scores: liveState.scores ?? null,
      gift_count: liveState.gift_count ?? 0,
      status_text: liveState.status_text ?? null
    },
    gates: fieldTrial.gates ?? {},
    field_trial: {
      level: fieldTrial.level ?? GIFT_SOURCE_CONTRACT_LEVELS.BLOCKED,
      ready_for_field_trial: fieldTrial.ready_for_field_trial === true,
      issue_count: fieldTrial.issue_count ?? 0,
      blocking_issue_count: fieldTrial.blocking_issue_count ?? 0,
      role_summaries: fieldTrial.role_summaries ?? [],
      metrics: fieldTrial.metrics ?? {}
    },
    official_review: officialReviewStatus
  };
}

export function createGiftSourceFieldTrialOpsSummary({
  workspaces = [],
  generatedAt = nowIso()
} = {}) {
  const rows = workspaces.map((workspace) => ({
    workspace_id: workspace.workspace_id,
    title: workspace.title,
    ready_for_external_test: workspace.ready_for_external_test === true,
    completed_count: workspace.completed_count ?? 0,
    total_count: workspace.total_count ?? 0,
    status_label: workspace.status_label,
    next_actions: workspace.next_actions ?? [],
    blocking_issue_count: workspace.field_trial?.blocking_issue_count ?? 0,
    official_review_ready: workspace.official_review?.ready === true
  }));
  const blockedRows = rows.filter((row) => !row.ready_for_external_test);
  const readyCount = rows.length - blockedRows.length;
  const gateKeys = ["contracts_ready", "samples_ready", "connection_ready", "replay_ready", "after_live_review_ready"];
  const gateSummary = Object.fromEntries(gateKeys.map((key) => [
    key,
    workspaces.filter((workspace) => workspace.gates?.[key] === true).length
  ]));

  return {
    ok: blockedRows.length === 0,
    generated_at: generatedAt,
    headline: {
      workspace_count: rows.length,
      ready_count: readyCount,
      attention_count: blockedRows.length,
      affected_live_room_count: blockedRows.length
    },
    homepage_cards: [
      { label: "实测房间", value: rows.length, action: "查看直播间实测状态" },
      { label: "可以外测", value: readyCount, action: "安排小范围观察" },
      { label: "需要跟进", value: blockedRows.length, action: "优先处理未完成步骤" },
      { label: "复核完成", value: workspaces.filter((workspace) => workspace.official_review?.ready === true).length, action: "查看下播复核结果" }
    ],
    charts: {
      readiness: {
        ready_count: readyCount,
        attention_count: blockedRows.length
      },
      gates: gateSummary
    },
    attention_rooms: blockedRows.slice(0, 50),
    next_actions: blockedRows.length > 0
      ? [...new Set(blockedRows.flatMap((row) => row.next_actions))].slice(0, 6)
      : ["继续观察小范围实测表现"]
  };
}

export class MemoryGiftSourceProductionStore {
  constructor({ sessions = [] } = {}) {
    this.sessions = sessions.map((session) => normalizeProductionGiftSourceSession(session));
  }

  createSession(session) {
    const normalized = normalizeProductionGiftSourceSession(session);
    this.sessions.push(normalized);
    return normalized;
  }

  getSession(sessionId) {
    return this.sessions.find((session) => session.session_id === sessionId) ?? null;
  }

  updateSession(session) {
    const normalized = normalizeProductionGiftSourceSession(session);
    const index = this.sessions.findIndex((item) => item.session_id === normalized.session_id);
    if (index < 0) {
      this.sessions.push(normalized);
    } else {
      this.sessions[index] = normalized;
    }
    return normalized;
  }

  listSessions({ tenantId = null, liveRoomId = null } = {}) {
    return this.sessions.filter((session) => {
      return (!tenantId || session.tenant_id === tenantId)
        && (!liveRoomId || session.live_room_id === liveRoomId);
    });
  }
}

export class GiftSourceProductionService {
  constructor({
    store = new MemoryGiftSourceProductionStore(),
    contractsFactory = createDefaultGiftSourceContracts,
    rawBackupStoreFactory = createProductionRawBackupStore,
    p1RawStoreFactory = createProductionP1RawStore,
    p1EvidenceService = null,
    requireRawBackup = true
  } = {}) {
    this.store = store;
    this.contractsFactory = contractsFactory;
    this.rawBackupStoreFactory = rawBackupStoreFactory;
    this.p1RawStoreFactory = p1RawStoreFactory;
    this.p1EvidenceService = p1EvidenceService;
    this.requireRawBackup = requireRawBackup;
    this.rawBackupStores = new Map();
    this.p1RawStores = new Map();
  }

  registerLiveRoomSource(input = {}) {
    const session = this.store.createSession({
      session_id: input.session_id ?? createId("gift_source_session"),
      tenant_id: input.tenant_id,
      live_room_id: input.live_room_id,
      creator_handle: input.creator_handle ?? null,
      collector_id: input.collector_id ?? "local_collector",
      status: "testing",
      connector_plan: createTiktokGiftConnectorPlan(input),
      events_by_role: {},
      rejected_events: [],
      heartbeat: null,
      heartbeats_by_role: {},
      created_at: input.created_at ?? nowIso(),
      updated_at: input.updated_at ?? input.created_at ?? nowIso()
    });
    return {
      ok: true,
      session: sanitizeProductionGiftSourceSession(session),
      workspace: this.fieldTrialWorkspace({ sessionId: session.session_id })
    };
  }

  ingestGiftEvent({ sessionId, tenant_id, role = GIFT_SOURCE_ROLES.PRIMARY_REALTIME, event = {}, receivedAt = nowIso() } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const safeRole = normalizeGiftSourceRole(role);
    const descriptor = createGiftSourceDescriptor({
      role: safeRole,
      source_id: sourceIdForProductionRole(safeRole)
    });
    const candidate = {
      live_room_id: session.live_room_id,
      received_at: receivedAt,
      ...event
    };
    const validation = validateLocalGiftBridgeEvent(candidate);
    if (!validation.ok) {
      const rejected = {
        rejected_event_id: createId("gift_rejected"),
        role: safeRole,
        source_event_id: candidate.source_event_id ?? null,
        reason_codes: validation.issues.map((issue) => issue.reason_code),
        received_at: receivedAt
      };
      session.rejected_events.push(rejected);
      this.store.updateSession({
        ...session,
        updated_at: receivedAt
      });
      return {
        ok: false,
        reason_code: validation.issues[0]?.reason_code ?? "gift_record_check_needed",
        rejected,
        workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
      };
    }
    const prepared = prepareGiftForLiveConfirmation({
      rawGift: candidate,
      sourceDescriptor: descriptor,
      rawBackupStore: this.#rawBackupStoreFor(session, safeRole, descriptor),
      requireRawBackup: this.requireRawBackup && rawBackupRequiredForRole(safeRole),
      sessionContext: {
        tenant_id: session.tenant_id,
        live_room_id: session.live_room_id,
        session_id: session.session_id
      },
      seenKeys: new Set(this.#events(session, safeRole).map((item) => item.idempotency_key).filter(Boolean)),
      receivedAt
    });
    if (prepared.status === GIFT_CONFIRMATION_STATUS.DUPLICATE_IGNORED) {
      return {
        ok: true,
        reason_code: "duplicate_event",
        deduped: true,
        role: safeRole,
        event: sanitizeGiftSourceEvent(prepared.gift),
        workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
      };
    }
    if (prepared.status !== GIFT_CONFIRMATION_STATUS.LIVE_CONFIRMED) {
      const rejected = {
        rejected_event_id: createId("gift_rejected"),
        role: safeRole,
        source_event_id: candidate.source_event_id ?? null,
        reason_codes: prepared.issues,
        received_at: receivedAt
      };
      session.rejected_events.push(rejected);
      this.store.updateSession({
        ...session,
        updated_at: receivedAt
      });
      return {
        ok: false,
        reason_code: prepared.issues[0] ?? "gift_record_check_needed",
        rejected,
        workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
      };
    }
    const normalized = prepared.gift;
    session.events_by_role[safeRole] = [
      ...(session.events_by_role[safeRole] ?? []),
      normalized
    ];
    this.store.updateSession({
      ...session,
      updated_at: receivedAt
    });
    return {
      ok: true,
      role: safeRole,
      event: sanitizeGiftSourceEvent(normalized),
      live_score_allowed: canRoleDriveLiveScore(safeRole),
      settlement_locked: false,
      raw_backup_record_id: prepared.rawBackupRecord?.raw_record_id ?? null,
      workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
    };
  }

  recordHeartbeat({ sessionId, tenant_id, heartbeat = {}, observedAt = nowIso() } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const safeRole = normalizeGiftSourceRole(heartbeat.source_role ?? heartbeat.role ?? GIFT_SOURCE_ROLES.PRIMARY_REALTIME);
    const nextHeartbeat = createLocalGiftBridgeHeartbeat({
      collector_id: session.collector_id,
      tenant_id: session.tenant_id,
      live_room_id: session.live_room_id,
      session_id: session.session_id,
      source_role: safeRole,
      event_count: this.#events(session, safeRole).length,
      accepted_count: productionAcceptedCount(session),
      rejected_count: session.rejected_events.length,
      observed_at: observedAt,
      ...heartbeat
    });
    this.store.updateSession({
      ...session,
      heartbeat: nextHeartbeat,
      heartbeats_by_role: {
        ...(session.heartbeats_by_role ?? {}),
        [safeRole]: nextHeartbeat
      },
      updated_at: observedAt
    });
    return {
      ok: true,
      heartbeat: nextHeartbeat,
      workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
    };
  }

  importAfterLiveReview({ sessionId, tenant_id, events = [], importedAt = nowIso() } = {}) {
    const inputEvents = Array.isArray(events) ? events : [events].filter(Boolean);
    if (inputEvents.length === 0) {
      return {
        ok: false,
        reason_code: "after_live_review_required",
        customer_message: "请先选择复核记录。"
      };
    }
    const imported = [];
    for (const event of inputEvents) {
      const result = this.ingestGiftEvent({
        sessionId,
        tenant_id,
        role: GIFT_SOURCE_ROLES.OFFICIAL_BACKEND,
        event,
        receivedAt: importedAt
      });
      if (result.ok) {
        imported.push(result.event);
      }
    }
    return {
      ok: imported.length === inputEvents.length,
      imported_count: imported.length,
      review_status: this.officialReviewStatus({ sessionId, tenant_id }),
      workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
    };
  }

  ingestTiktokWebcastGiftPayload({
    sessionId,
    tenant_id,
    role = GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    payload = {},
    provider = "tiktok_webcast",
    receivedAt = nowIso()
  } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const safeRole = normalizeGiftSourceRole(role);
    const descriptor = createGiftSourceDescriptor({
      role: safeRole,
      source_id: sourceIdForProductionRole(safeRole)
    });
    const validation = validateTiktokWebcastGiftPayload(payload, {
      sourceDescriptor: descriptor,
      liveRoomId: session.live_room_id,
      provider,
      receivedAt
    });
    if (!validation.ok) {
      const rejected = {
        rejected_event_id: createId("gift_rejected"),
        role: safeRole,
        source_event_id: validation.normalized?.source_event_id ?? null,
        reason_codes: validation.issues.map((issue) => issue.reason_code),
        received_at: receivedAt
      };
      session.rejected_events.push(rejected);
      this.store.updateSession({
        ...session,
        updated_at: receivedAt
      });
      return {
        ok: false,
        platform: "tiktok",
        reason_code: validation.issues[0]?.reason_code ?? "tiktok_webcast_payload_invalid",
        rejected,
        normalized_preview: validation.normalized ? sanitizeGiftSourceEvent(validation.normalized) : null,
        workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
      };
    }
    const result = this.ingestGiftEvent({
      sessionId,
      tenant_id,
      role: safeRole,
      event: validation.normalized,
      receivedAt
    });
    const routeResilience = result.ok && result.deduped !== true
      ? this.tiktokRouteResilience({ sessionId, tenant_id, event: validation.normalized })
      : null;
    return {
      ...result,
      platform: "tiktok",
      provider,
      live_score_allowed: result.live_score_allowed === true && routeResilience?.can_drive_live_score === true && routeResilience?.decision !== TIKTOK_GIFT_RESILIENCE_DECISIONS.HOLD_FOR_CONFIRMATION,
      settlement_locked: routeResilience?.needs_after_live_review === true || routeResilience?.ok === false,
      route_resilience: routeResilience
    };
  }

  tiktokRouteResilience({
    sessionId,
    tenant_id,
    event = null,
    now = nowIso(),
    bigGiftValueThreshold,
    highFrequencyEventsPerSecondThreshold
  } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const primaryEvents = this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME);
    const backupEvents = this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME);
    return evaluateTiktokGiftRouteResilience({
      sources: this.#sourceDescriptorsFromSession(session, { now }),
      primaryEvents,
      backupEvents,
      event,
      recentEvents: [...primaryEvents, ...backupEvents],
      expectedLiveRoomId: session.live_room_id,
      bigGiftValueThreshold,
      highFrequencyEventsPerSecondThreshold
    });
  }

  tiktokBackfillPlan({ sessionId, tenant_id, generatedAt = nowIso() } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    return createTiktokGiftBackfillPlan({
      primaryEvents: this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
      backupEvents: this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME),
      officialEvents: this.#events(session, GIFT_SOURCE_ROLES.OFFICIAL_BACKEND),
      expectedLiveRoomId: session.live_room_id,
      generatedAt
    });
  }

  tiktokResilienceBacktest({ sessionId, tenant_id, routeReports = [], chaosScenarios = [], generatedAt = nowIso() } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const effectiveRouteReports = routeReports.length > 0
      ? routeReports
      : [this.tiktokRouteResilience({ sessionId, tenant_id, now: generatedAt })];
    return createTiktokGiftResilienceBacktestReport({
      primaryEvents: this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
      backupEvents: this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME),
      officialEvents: this.#events(session, GIFT_SOURCE_ROLES.OFFICIAL_BACKEND),
      rejected: session.rejected_events,
      routeReports: effectiveRouteReports,
      chaosScenarios,
      expectedLiveRoomId: session.live_room_id,
      generatedAt
    });
  }

  createTiktokWebcastCollector({
    sessionId,
    tenant_id,
    role = GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    config = {},
    fetchImpl = globalThis.fetch,
    WebSocketImpl = globalThis.WebSocket,
    messageDecoder = decodeTiktokWebcastFrame,
    setTimeoutImpl = globalThis.setTimeout,
    clearTimeoutImpl = globalThis.clearTimeout
  } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const safeRole = normalizeGiftSourceRole(role);
    return new TiktokWebcastGiftCollector({
      config: {
        live_room_id: session.live_room_id,
        provider: safeRole === GIFT_SOURCE_ROLES.BACKUP_REALTIME ? "tiktok_webcast_backup" : "tiktok_webcast_primary",
        ...config,
        role: safeRole
      },
      sourceDescriptor: createGiftSourceDescriptor({
        role: safeRole,
        source_id: sourceIdForProductionRole(safeRole)
      }),
      fetchImpl,
      WebSocketImpl,
      messageDecoder,
      setTimeoutImpl,
      clearTimeoutImpl,
      onGift: ({ event, receivedAt }) => {
        this.ingestTiktokWebcastGiftPayload({
          sessionId,
          tenant_id,
          role: safeRole,
          payload: event,
          provider: config.provider ?? (safeRole === GIFT_SOURCE_ROLES.BACKUP_REALTIME ? "tiktok_webcast_backup" : "tiktok_webcast_primary"),
          receivedAt
        });
      },
      onStatus: (status) => {
        this.recordHeartbeat({
          sessionId,
          tenant_id,
          heartbeat: {
            source_role: safeRole,
            collector_id: status.collector_id,
            connected: status.connected,
            event_count: status.event_count,
            accepted_count: status.accepted_count,
            rejected_count: status.rejected_count,
            last_event_at: status.last_event_at,
            app_version: status.state
          },
          observedAt: status.observed_at
        });
      },
      p1RawStore: this.#p1RawStoreFor(session, safeRole),
      onP1Evidence: ({ event }) => {
        if (!this.p1EvidenceService) {
          return;
        }
        const registered = this.p1EvidenceService.registerSession({
          tenant_id: session.tenant_id,
          live_room_id: session.live_room_id,
          session_id: session.session_id,
          source_ids: [sourceIdForProductionRole(safeRole)]
        });
        if (registered.ok === false) {
          return;
        }
        this.p1EvidenceService.ingestEvents({
          tenant_id: session.tenant_id,
          live_room_id: session.live_room_id,
          session_id: session.session_id,
          events: [event]
        });
      }
    });
  }

  ingestDouyinLiveGiftCallback({
    sessionId,
    tenant_id,
    headers = {},
    body = "",
    secret,
    allowTestData = false,
    receivedAt = nowIso()
  } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const parsed = normalizeDouyinLiveGiftCallback({
      headers,
      body,
      secret,
      expectedRoomId: session.live_room_id,
      allowTestData,
      sourceDescriptor: createGiftSourceDescriptor({
        role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
        source_id: "source_douyin_live_data"
      }),
      receivedAt
    });
    const rejected = [...parsed.rejected];
    if (parsed.issues.length > 0) {
      rejected.push({
        index: null,
        source_event_id: null,
        issues: parsed.issues
      });
    }
    if (rejected.length > 0) {
      session.rejected_events.push(...rejected.map((item) => ({
        rejected_event_id: createId("gift_rejected"),
        role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
        source_event_id: item.source_event_id ?? null,
        reason_codes: (item.issues ?? []).map((issue) => issue.reason_code),
        received_at: receivedAt
      })));
      this.store.updateSession({
        ...session,
        updated_at: receivedAt
      });
    }
    const ingested = [];
    const failed = [];
    if (parsed.signature.ok && parsed.issues.length === 0) {
      for (const event of parsed.events) {
        const result = this.ingestGiftEvent({
          sessionId,
          tenant_id,
          role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
          event,
          receivedAt
        });
        if (result.ok) {
          ingested.push(result);
        } else {
          failed.push(result);
        }
      }
    }
    return {
      ok: parsed.signature.ok === true && parsed.issues.length === 0 && rejected.length === 0 && failed.length === 0,
      provider: "douyin_live_data",
      accepted_count: ingested.filter((item) => item.deduped !== true).length,
      duplicate_count: ingested.filter((item) => item.deduped === true).length,
      rejected_count: rejected.length + failed.length,
      callback: {
        msg_type: parsed.msg_type,
        room_id: parsed.room_id,
        payload_count: parsed.payload_count,
        event_count: parsed.event_count,
        signature_valid: parsed.signature.signature_valid,
        audit_record: parsed.audit_record
      },
      ack: {
        received_items: parsed.ack_items,
        processed_items: ingested.map((item) => createDouyinLiveDataAckItem({
          msg_id: item.event?.source_event_id,
          msg_type: DOUYIN_LIVE_DATA_MSG_TYPES.GIFT,
          client_time: Date.parse(receivedAt)
        })).filter((item) => item.msg_id)
      },
      rejected: [...rejected, ...failed],
      workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
    };
  }

  ingestDouyinFailedGiftDataPage({
    sessionId,
    tenant_id,
    response = {},
    allowTestData = false,
    receivedAt = nowIso()
  } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const page = normalizeDouyinFailedGiftDataPage(response, {
      sourceDescriptor: createGiftSourceDescriptor({
        role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
        source_id: "source_douyin_failed_data"
      }),
      allowTestData,
      receivedAt
    });
    const rejected = [...page.rejected];
    if (rejected.length > 0) {
      session.rejected_events.push(...rejected.map((item) => ({
        rejected_event_id: createId("gift_rejected"),
        role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
        source_event_id: item.source_event_id ?? null,
        reason_codes: (item.issues ?? []).map((issue) => issue.reason_code),
        received_at: receivedAt
      })));
      this.store.updateSession({
        ...session,
        updated_at: receivedAt
      });
    }

    const ingested = [];
    const failed = [];
    if (page.events.length > 0) {
      for (const event of page.events) {
        const result = this.ingestGiftEvent({
          sessionId,
          tenant_id,
          role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
          event,
          receivedAt
        });
        if (result.ok) {
          ingested.push(result);
        } else {
          failed.push(result);
        }
      }
    }

    return {
      ok: page.ok && rejected.length === 0 && failed.length === 0,
      provider: "douyin_live_data_failed_query",
      compensation_page: {
        page_num: page.page_num,
        total_count: page.total_count,
        row_count: page.row_count,
        event_count: page.event_count
      },
      accepted_count: ingested.filter((item) => item.deduped !== true).length,
      duplicate_count: ingested.filter((item) => item.deduped === true).length,
      rejected_count: rejected.length + failed.length,
      rejected: [...rejected, ...failed],
      backtest_hint: "failed_data_has_no_ack_keep_external_page_cursor",
      workspace: this.fieldTrialWorkspace({ sessionId, tenant_id })
    };
  }

  officialReviewStatus({ sessionId, tenant_id } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    return createOfficialGiftReviewStatus({
      realtimeEvents: [
        ...this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
        ...this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME)
      ],
      officialEvents: this.#events(session, GIFT_SOURCE_ROLES.OFFICIAL_BACKEND),
      expectedLiveRoomId: session.live_room_id
    });
  }

  fieldTrialWorkspace({ sessionId, tenant_id } = {}) {
    const session = this.#requireSession(sessionId, tenant_id);
    const bridgeStatus = this.#bridgeHealthStatus(session);
    const giftStatus = this.#giftSourceStatus(session);
    const officialReviewStatus = this.officialReviewStatus({ sessionId, tenant_id });
    const fieldTrial = evaluateGiftCollectorFieldTrial({
      plan: createGiftCollectorFieldTrialPlan({
        collector_id: session.collector_id,
        expected_live_room_id: session.live_room_id,
        min_events_per_role: 2
      }),
      contracts: this.contractsFactory({
        baseDir: `gift-source/${session.tenant_id}`,
        sessionId: session.session_id
      }),
      bridgeHealthStatus: bridgeStatus,
      giftSourceStatus: giftStatus,
      autoReaderStatus: {
        tick_count: 1,
        last_issue_count: session.rejected_events.length
      },
      sampleEventsByRole: {
        [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
        [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME)
      },
      replaySummary: {
        ok: this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME).length >= 2
          && this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME).length >= 2
          && session.rejected_events.length === 0
          && this.#hasRequiredRawRefs(session)
      },
      officialReviewStatus,
      observedMetrics: {
        data_confirmation_p95_ms: bridgeStatus.health.connected ? 1200 : 9999
      }
    });
    return createGiftSourceFieldTrialWorkspace({
      fieldTrial,
      giftSourceStatus: giftStatus,
      bridgeHealthStatus: bridgeStatus,
      officialReviewStatus,
      liveState: {
        gift_count: productionAcceptedCount(session),
        status_text: "积分确认中，请稍等"
      }
    });
  }

  opsSummary({ tenantId = null } = {}) {
    const workspaces = this.store.listSessions({ tenantId }).map((session) => {
      return this.fieldTrialWorkspace({ sessionId: session.session_id, tenant_id: session.tenant_id });
    });
    return createGiftSourceFieldTrialOpsSummary({
      workspaces
    });
  }

  #requireSession(sessionId, tenantId = null) {
    const session = this.store.getSession(sessionId);
    if (!session) {
      throw new Error(`gift source session not found: ${sessionId}`);
    }
    if (tenantId && session.tenant_id !== tenantId) {
      throw new Error("gift source session tenant mismatch");
    }
    return session;
  }

  #events(session, role) {
    return session.events_by_role?.[role] ?? [];
  }

  #rawBackupStoreFor(session, role, descriptor) {
    if (!rawBackupRequiredForRole(role) || !this.rawBackupStoreFactory) {
      return null;
    }
    const key = `${session.session_id}:${role}`;
    if (this.rawBackupStores.has(key)) {
      return this.rawBackupStores.get(key);
    }
    const store = this.rawBackupStoreFactory({
      session,
      role,
      descriptor
    });
    this.rawBackupStores.set(key, store);
    return store;
  }

  #p1RawStoreFor(session, role) {
    if (!this.p1RawStoreFactory) {
      return null;
    }
    const key = `${session.session_id}:${role}:p1`;
    if (this.p1RawStores.has(key)) {
      return this.p1RawStores.get(key);
    }
    const descriptor = createP1EvidenceSourceDescriptor({
      role,
      source_id: sourceIdForProductionRole(role),
      display_name: role
    });
    const store = this.p1RawStoreFactory({
      session,
      role,
      descriptor
    });
    this.p1RawStores.set(key, store);
    return store;
  }

  #hasRequiredRawRefs(session) {
    return [
      GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
      GIFT_SOURCE_ROLES.BACKUP_REALTIME,
      GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN
    ].every((role) => {
      const events = this.#events(session, role);
      return events.length === 0 || events.every((event) => event.raw_ref?.raw_record_id);
    });
  }

  #bridgeHealthStatus(session) {
    const health = evaluateLocalGiftBridgeHealth({
      heartbeat: session.heartbeat,
      expectedLiveRoomId: session.live_room_id,
      rejectedCount: session.rejected_events.length
    });
    return {
      ok: health.ok,
      tenant_id: session.tenant_id,
      live_room_id: session.live_room_id,
      health,
      last_heartbeat: session.heartbeat,
      accepted_count: productionAcceptedCount(session),
      rejected_count: session.rejected_events.length,
      last_status: health.connected ? "礼物记录连接正常" : "礼物记录连接中，请稍等"
    };
  }

  #sourceDescriptorsFromSession(session, { now = nowIso() } = {}) {
    return [
      GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
      GIFT_SOURCE_ROLES.BACKUP_REALTIME,
      GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN
    ].map((role) => {
      const heartbeat = session.heartbeats_by_role?.[role]
        ?? (session.heartbeat?.source_role === role ? session.heartbeat : null);
      const health = evaluateLocalGiftBridgeHealth({
        heartbeat,
        expectedLiveRoomId: session.live_room_id,
        now,
        rejectedCount: role === GIFT_SOURCE_ROLES.PRIMARY_REALTIME ? session.rejected_events.length : 0
      });
      const events = this.#events(session, role);
      return createGiftSourceDescriptor({
        source_id: sourceIdForProductionRole(role),
        role,
        connected: health.connected,
        health: health.health,
        event_lag_ms_p95: health.connected ? Math.min(Number(health.stale_ms ?? 0), 1500) : 9999,
        event_count: events.length,
        last_event_at: events.at(-1)?.event_time ?? null,
        updated_at: heartbeat?.observed_at ?? now
      });
    });
  }

  #giftSourceStatus(session) {
    const merge = mergeRealtimeSourceEvents({
      primaryEvents: this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME),
      backupEvents: this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME)
    });
    return {
      ok: true,
      live_room_id: session.live_room_id,
      source_count: productionAcceptedCount(session),
      primary_count: this.#events(session, GIFT_SOURCE_ROLES.PRIMARY_REALTIME).length,
      backup_count: this.#events(session, GIFT_SOURCE_ROLES.BACKUP_REALTIME).length,
      guardian_count: this.#events(session, GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN).length,
      official_review_count: this.#events(session, GIFT_SOURCE_ROLES.OFFICIAL_BACKEND).length,
      confirmed_by_backup_count: merge.confirmations.length,
      mismatch_count: merge.mismatches.length,
      review: {
        accepted_count: merge.accepted.length,
        duplicate_count: merge.duplicates.length,
        confirmation_count: merge.confirmations.length,
        mismatch_count: merge.mismatches.length,
        mismatches: merge.mismatches.slice(0, 5)
      }
    };
  }
}

export function createTiktokGiftConnectorPlan(input = {}) {
  const connectors = [
    createTiktokConnectorRole({
      role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
      mode: input.primary_mode ?? TIKTOK_GIFT_CONNECTOR_MODES.WEBCAST_WEBSOCKET_PRIMARY,
      label: "主线路"
    }),
    createTiktokConnectorRole({
      role: GIFT_SOURCE_ROLES.BACKUP_REALTIME,
      mode: input.backup_mode ?? TIKTOK_GIFT_CONNECTOR_MODES.LOCAL_BROWSER_OBSERVER_BACKUP,
      label: "备用线路"
    }),
    createTiktokConnectorRole({
      role: GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN,
      mode: input.guardian_mode ?? TIKTOK_GIFT_CONNECTOR_MODES.LIVE_STUDIO_GUARDIAN,
      label: "直播工具核对"
    }),
    createTiktokConnectorRole({
      role: GIFT_SOURCE_ROLES.OFFICIAL_BACKEND,
      mode: input.official_mode ?? TIKTOK_GIFT_CONNECTOR_MODES.OFFICIAL_AFTER_LIVE_REVIEW,
      label: "下播复核"
    })
  ];
  return {
    connector_plan_id: input.connector_plan_id ?? createId("tiktok_gift_plan"),
    tenant_id: input.tenant_id ?? null,
    live_room_id: input.live_room_id ?? null,
    creator_handle: input.creator_handle ?? null,
    platform: "tiktok",
    connectors,
    local_raw_backup_required: true,
    after_live_review_required: true,
    realtime_data_entry: "webcast_websocket",
    live_studio_role: "broadcast_and_overlay_guardian",
    customer_message: "礼物记录接入方案已准备。",
    created_at: input.created_at ?? nowIso()
  };
}

function createTiktokConnectorRole({ role, mode, label }) {
  return {
    role,
    mode,
    label,
    required: true,
    can_drive_live_score: role === GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    local_backup_required: role !== GIFT_SOURCE_ROLES.OFFICIAL_BACKEND,
    customer_hint: `${label}需要完成接入和实测`
  };
}

export function appendLocalGiftSourceInboxEvents({
  inboxDir,
  role = GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
  events = [],
  fileNames = LOCAL_GIFT_SOURCE_INBOX_FILES,
  defaults = {},
  validate = true,
  receivedAt = nowIso()
} = {}) {
  if (!inboxDir) {
    throw new Error("gift source inboxDir is required");
  }
  const inputEvents = Array.isArray(events)
    ? events
    : events
      ? [events]
      : [];
  const fileName = fileNames[role] ?? `${role}.gifts.jsonl`;
  const filePath = join(inboxDir, fileName);
  mkdirSync(dirname(filePath), { recursive: true });
  if (!existsSync(filePath)) {
    writeFileSync(filePath, "", "utf8");
  }

  const accepted = [];
  const rejected = [];
  inputEvents.forEach((event, index) => {
    const candidate = {
      received_at: receivedAt,
      ...defaults,
      ...event
    };
    const validation = validate
      ? validateLocalGiftBridgeEvent(candidate)
      : { ok: true, issues: [] };
    if (!validation.ok) {
      rejected.push({
        index,
        source_event_id: candidate.source_event_id ?? null,
        issues: validation.issues
      });
      return;
    }
    accepted.push(candidate);
  });

  const normalizedLines = accepted.map((event) => JSON.stringify(event));
  if (normalizedLines.length > 0) {
    appendFileSync(filePath, `${normalizedLines.join("\n")}\n`, "utf8");
  }

  return {
    ok: rejected.length === 0,
    role,
    file_path: filePath,
    appended_count: normalizedLines.length,
    rejected_count: rejected.length,
    rejected,
    received_at: receivedAt,
    customer_message: rejected.length > 0 ? "部分礼物记录需要检查。" : normalizedLines.length > 0 ? "礼物记录已接收。" : "等待礼物记录。"
  };
}

function latestCursorEventTime(files = []) {
  return files
    .map((file) => file.last_event_time)
    .filter(Boolean)
    .sort()
    .at(-1) ?? null;
}

function localGiftSourceDiagnosticActions({ disconnected, rejectedCount, mismatchCount, readyFileCount }) {
  const actions = [];
  if (disconnected) {
    actions.push("确认礼物记录接收程序正在运行");
  }
  if (rejectedCount > 0) {
    actions.push("查看最近被拦截的礼物记录字段");
  }
  if (mismatchCount > 0) {
    actions.push("复核主线路和备用线路的差异记录");
  }
  if (readyFileCount === 0) {
    actions.push("准备本地记录入口或先使用模拟礼物测试");
  }
  if (actions.length === 0) {
    actions.push("继续观察本场直播数据");
  }
  return actions;
}

function normalizeSampleEventsForRole(sampleEventsByRole, role) {
  if (Array.isArray(sampleEventsByRole)) {
    return sampleEventsByRole;
  }
  return sampleEventsByRole[role] ?? sampleEventsByRole[String(role)] ?? [];
}

function validateFieldTrialSamples({
  role,
  events = [],
  expectedLiveRoomId,
  allowMockData
}) {
  const issues = [];
  events.forEach((event, index) => {
    const validation = validateLocalGiftBridgeEvent(event);
    for (const issue of validation.issues) {
      issues.push(createFieldTrialIssue(issue.field, issue.reason_code, "blocked", {
        role,
        index
      }));
    }
    if (expectedLiveRoomId && event.live_room_id && event.live_room_id !== expectedLiveRoomId) {
      issues.push(createFieldTrialIssue("live_room_id", "collector_sample_room_mismatch", "blocked", {
        role,
        index
      }));
    }
    if (!allowMockData && (event.source === EVENT_SOURCES.MOCK || event.is_mock === true || String(event.source_event_id ?? "").startsWith("mock_"))) {
      issues.push(createFieldTrialIssue("sample_events", "collector_mock_sample_not_allowed", "blocked", {
        role,
        index
      }));
    }
  });
  return issues;
}

function createFieldTrialIssue(field, reasonCode, severity = "blocked", extra = {}) {
  return {
    field,
    reason_code: reasonCode,
    severity,
    ...extra
  };
}

function fieldTrialNextActions(issues = []) {
  const actions = [];
  if (issues.some((issue) => issue.reason_code.includes("contract") || issue.reason_code.includes("raw_backup"))) {
    actions.push("补齐礼物记录接收和本地备份设置");
  }
  if (issues.some((issue) => issue.reason_code.includes("sample"))) {
    actions.push("使用真实直播间重新采集主备样本");
  }
  if (issues.some((issue) => issue.reason_code.includes("connection") || issue.reason_code.includes("room"))) {
    actions.push("确认礼物记录接收程序连接到当前直播间");
  }
  if (issues.some((issue) => issue.reason_code.includes("lag") || issue.reason_code.includes("rejected") || issue.reason_code.includes("mismatch"))) {
    actions.push("复核礼物记录字段、延迟和主备差异");
  }
  if (issues.some((issue) => issue.reason_code.includes("replay"))) {
    actions.push("先完成礼物链路回放测试");
  }
  if (issues.some((issue) => issue.reason_code.includes("official_review"))) {
    actions.push("补齐下播复核记录来源");
  }
  if (actions.length === 0) {
    actions.push("进入小范围实测观察");
  }
  return [...new Set(actions)];
}

function eventsByMatchKey(events = []) {
  const rows = new Map();
  for (const event of events) {
    const key = event.match_key ?? makeGiftMatchKey(event);
    if (!rows.has(key)) {
      rows.set(key, event);
    }
  }
  return rows;
}

function eventsByReviewKey(events = []) {
  const rows = new Map();
  for (const event of events) {
    const key = makeGiftReviewKey(event);
    if (!rows.has(key)) {
      rows.set(key, event);
    }
  }
  return rows;
}

function normalizeProductionGiftSourceSession(session = {}) {
  return {
    session_id: session.session_id ?? createId("gift_source_session"),
    tenant_id: session.tenant_id ?? null,
    live_room_id: session.live_room_id ?? null,
    creator_handle: session.creator_handle ?? null,
    collector_id: session.collector_id ?? "local_collector",
    status: session.status ?? "testing",
    connector_plan: session.connector_plan ?? createTiktokGiftConnectorPlan(session),
    events_by_role: {
      [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: [],
      [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: [],
      [GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN]: [],
      [GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]: [],
      ...(session.events_by_role ?? {})
    },
    rejected_events: session.rejected_events ?? [],
    heartbeat: session.heartbeat ?? null,
    heartbeats_by_role: session.heartbeats_by_role ?? {},
    created_at: session.created_at ?? nowIso(),
    updated_at: session.updated_at ?? session.created_at ?? nowIso()
  };
}

function sanitizeProductionGiftSourceSession(session = {}) {
  return {
    session_id: session.session_id,
    tenant_id: session.tenant_id,
    live_room_id: session.live_room_id,
    creator_handle: session.creator_handle,
    status: session.status,
    connector_plan: session.connector_plan,
    counts: {
      primary_count: session.events_by_role?.[GIFT_SOURCE_ROLES.PRIMARY_REALTIME]?.length ?? 0,
      backup_count: session.events_by_role?.[GIFT_SOURCE_ROLES.BACKUP_REALTIME]?.length ?? 0,
      official_review_count: session.events_by_role?.[GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]?.length ?? 0,
      rejected_count: session.rejected_events?.length ?? 0
    },
    heartbeats_by_role: Object.fromEntries(Object.entries(session.heartbeats_by_role ?? {}).map(([role, heartbeat]) => [
      role,
      {
        connected: heartbeat.connected,
        observed_at: heartbeat.observed_at,
        event_count: heartbeat.event_count,
        accepted_count: heartbeat.accepted_count,
        rejected_count: heartbeat.rejected_count
      }
    ])),
    created_at: session.created_at,
    updated_at: session.updated_at
  };
}

function sanitizeGiftSourceEvent(event = {}) {
  return {
    source_role: event.source_role,
    source_event_id: event.source_event_id,
    platform_event_id: event.platform_event_id ?? null,
    event_time: event.event_time,
    live_room_id: event.live_room_id,
    gifter_user_id: event.gifter_user_id,
    gifter_display_name: event.gifter_display_name,
    gift_id: event.gift_id,
    gift_name: event.gift_name,
    gift_count: event.gift_count,
    gift_total_value: event.gift_total_value,
    audience_sec_open_id: event.audience_sec_open_id ?? null,
    recipient_user_id: event.recipient_user_id ?? null,
    recipient_source: event.recipient_source ?? null,
    platform_payload: event.platform_payload ?? null,
    idempotency_key: event.idempotency_key,
    match_key: event.match_key,
    raw_ref: event.raw_ref ? {
      raw_record_id: event.raw_ref.raw_record_id,
      record_hash: event.raw_ref.raw_payload_hash,
      source_id: event.raw_ref.source_id
    } : null
  };
}

function normalizeGiftSourceRole(role) {
  if (Object.values(GIFT_SOURCE_ROLES).includes(role)) {
    return role;
  }
  if (role === "backup") {
    return GIFT_SOURCE_ROLES.BACKUP_REALTIME;
  }
  if (role === "official") {
    return GIFT_SOURCE_ROLES.OFFICIAL_BACKEND;
  }
  if (role === "guardian") {
    return GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN;
  }
  return GIFT_SOURCE_ROLES.PRIMARY_REALTIME;
}

function sourceIdForProductionRole(role) {
  return {
    [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: "source_tiktok_primary",
    [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: "source_tiktok_backup",
    [GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN]: "source_tiktok_guardian",
    [GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]: "source_tiktok_official"
  }[role] ?? "source_tiktok_primary";
}

function productionAcceptedCount(session = {}) {
  return Object.entries(session.events_by_role ?? {})
    .filter(([role]) => role !== GIFT_SOURCE_ROLES.OFFICIAL_BACKEND)
    .reduce((sum, [, events]) => sum + events.length, 0);
}

function officialGiftReviewActions({
  officialCount,
  realtimeOnlyCount,
  officialOnlyCount,
  adjustmentCount,
  roomMismatchCount
}) {
  const actions = [];
  if (officialCount === 0) {
    actions.push("下播后导入本场礼物明细");
  }
  if (roomMismatchCount > 0) {
    actions.push("确认复核记录属于当前直播间");
  }
  if (realtimeOnlyCount > 0 || officialOnlyCount > 0) {
    actions.push("核对直播中记录和下播明细是否同场");
  }
  if (adjustmentCount > 0) {
    actions.push("复核礼物数量和积分规则");
  }
  if (actions.length === 0) {
    actions.push("下播复核记录已对齐");
  }
  return actions;
}

function createFieldTrialWorkspaceStep({
  step_id,
  title,
  completed,
  requirement,
  details = [],
  markers = [],
  caption,
  action_label,
  action_href,
  evidence = {},
  required = true
}) {
  return {
    step_id,
    title,
    completed: completed === true,
    required,
    status_label: completed === true ? "已完成" : "待检查",
    requirement,
    details,
    image_cards: [{
      title,
      caption,
      markers
    }],
    action_label,
    action_href,
    evidence
  };
}

function hasFieldTrialIssue(fieldTrial = {}, pattern) {
  return (fieldTrial.issues ?? []).some((issue) => {
    return String(issue.field ?? "").includes(pattern) || String(issue.reason_code ?? "").includes(pattern);
  });
}

function hasRoleSampleIssue(fieldTrial = {}, role) {
  return (fieldTrial.issues ?? []).some((issue) => {
    return issue.role === role && issue.severity === "blocked";
  });
}

function roleSampleCount(fieldTrial = {}, role) {
  const summary = (fieldTrial.role_summaries ?? []).find((item) => item.role === role);
  return Number(summary?.sample_count ?? 0);
}

function createFieldTrialHealthCards({
  fieldTrial = {},
  giftSourceStatus = {},
  bridgeHealthStatus = {},
  officialReviewStatus = {}
}) {
  return [
    createFieldTrialHealthCard("主线路", giftSourceStatus.primary_count ?? 0, roleSampleCount(fieldTrial, GIFT_SOURCE_ROLES.PRIMARY_REALTIME) >= 2, "确认礼物能快速进入玩法"),
    createFieldTrialHealthCard("备用线路", giftSourceStatus.backup_count ?? 0, roleSampleCount(fieldTrial, GIFT_SOURCE_ROLES.BACKUP_REALTIME) >= 2, "确认备用记录能复核"),
    createFieldTrialHealthCard("连接状态", bridgeHealthStatus.health?.connected ? "正常" : "检查中", fieldTrial.gates?.connection_ready === true, "保持接收程序运行"),
    createFieldTrialHealthCard("回放检查", fieldTrial.gates?.replay_ready ? "完成" : "待完成", fieldTrial.gates?.replay_ready === true, "确认本机记录可复盘"),
    createFieldTrialHealthCard("下播复核", officialReviewStatus.official_count ?? 0, officialReviewStatus.ready === true, "导入官方复核明细"),
    createFieldTrialHealthCard("差异记录", giftSourceStatus.mismatch_count ?? 0, Number(giftSourceStatus.mismatch_count ?? 0) === 0 && Number(officialReviewStatus.adjustment_count ?? 0) === 0, "发现差异时先复核")
  ];
}

function createFieldTrialHealthCard(label, value, ready, action) {
  return {
    label,
    value,
    status: ready ? "ready" : "attention",
    action
  };
}

function fieldTrialWorkspaceActions(steps = [], fieldTrial = {}, officialReviewStatus = {}) {
  const nextStep = steps.find((step) => !step.completed && step.required);
  if (nextStep) {
    return [nextStep.requirement, ...(officialReviewStatus.next_actions ?? [])].slice(0, 4);
  }
  return fieldTrial.next_actions?.length ? fieldTrial.next_actions : ["进入小范围实测观察"];
}

export function readLocalGiftSourceInbox({
  inboxDir,
  roles = [
    GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    GIFT_SOURCE_ROLES.BACKUP_REALTIME,
    GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN,
    GIFT_SOURCE_ROLES.OFFICIAL_BACKEND
  ],
  fileNames = LOCAL_GIFT_SOURCE_INBOX_FILES,
  sourceDescriptors = {},
  defaults = {}
} = {}) {
  const plan = createLocalGiftSourceInboxPlan({
    inboxDir,
    roles,
    fileNames
  });
  const batches = [];
  const issues = [];

  for (const file of plan.files) {
    if (!file.exists) {
      batches.push({
        role: file.role,
        file_path: file.file_path,
        event_count: 0,
        events: [],
        missing: true
      });
      continue;
    }

    const sourceDescriptor = sourceDescriptors[file.role] ?? createGiftSourceDescriptor({
      role: file.role
    });
    const fileResult = readLocalGiftSourceInboxFile({
      filePath: file.file_path,
      role: file.role,
      sourceDescriptor,
      defaults
    });
    batches.push(fileResult.batch);
    issues.push(...fileResult.issues);
  }

  return {
    ok: issues.length === 0,
    inbox_dir: inboxDir,
    plan,
    event_count: batches.reduce((sum, batch) => sum + batch.event_count, 0),
    batches,
    issues,
    customer_message: issues.length === 0 ? "礼物记录读取完成。" : "部分记录需要检查。"
  };
}

export function readIncrementalLocalGiftSourceInbox({
  inboxDir,
  cursorState = {},
  roles = [
    GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    GIFT_SOURCE_ROLES.BACKUP_REALTIME,
    GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN,
    GIFT_SOURCE_ROLES.OFFICIAL_BACKEND
  ],
  fileNames = LOCAL_GIFT_SOURCE_INBOX_FILES,
  sourceDescriptors = {},
  defaults = {},
  scannedAt = nowIso()
} = {}) {
  const plan = createLocalGiftSourceInboxPlan({
    inboxDir,
    roles,
    fileNames
  });
  const batches = [];
  const issues = [];
  const nextCursor = {
    cursor_id: cursorState.cursor_id ?? createId("gift_inbox_cursor"),
    inbox_dir: inboxDir,
    updated_at: scannedAt,
    files: {
      ...(cursorState.files ?? {})
    }
  };

  for (const file of plan.files) {
    const previous = cursorState.files?.[file.role] ?? {};
    if (!file.exists) {
      nextCursor.files[file.role] = {
        file_path: file.file_path,
        processed_line_count: 0,
        last_scan_at: scannedAt,
        missing: true
      };
      batches.push({
        role: file.role,
        file_path: file.file_path,
        event_count: 0,
        events: [],
        missing: true,
        start_line_no: 1,
        processed_line_count: 0
      });
      continue;
    }

    const sourceDescriptor = sourceDescriptors[file.role] ?? createGiftSourceDescriptor({
      role: file.role
    });
    const rawLines = readFileSync(file.file_path, "utf8")
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean);
    let startIndex = Number(previous.processed_line_count ?? 0);
    const fileWasReset = startIndex > rawLines.length;
    if (fileWasReset) {
      issues.push({
        field: "file",
        reason_code: "gift_record_file_reset",
        file_path: file.file_path,
        role: file.role
      });
      startIndex = 0;
    }

    const fileResult = readLocalGiftSourceLines({
      lines: rawLines.slice(startIndex),
      role: file.role,
      sourceDescriptor,
      defaults,
      filePath: file.file_path,
      startLineNo: startIndex + 1,
      stopOnFirstIssue: true
    });
    const firstIssueLine = fileResult.issues.reduce((lowest, issue) => {
      const lineNo = Number(issue.line_no);
      return Number.isFinite(lineNo) ? Math.min(lowest, lineNo) : lowest;
    }, Number.POSITIVE_INFINITY);
    const processedLineCount = Number.isFinite(firstIssueLine)
      ? Math.max(startIndex, firstIssueLine - 1)
      : rawLines.length;
    batches.push({
      ...fileResult.batch,
      missing: false,
      start_line_no: startIndex + 1,
      processed_line_count: processedLineCount,
      newly_seen_line_count: Math.max(0, processedLineCount - startIndex)
    });
    issues.push(...fileResult.issues);
    const lastEvent = fileResult.batch.events.at(-1);
    nextCursor.files[file.role] = {
      file_path: file.file_path,
      processed_line_count: processedLineCount,
      last_scan_at: scannedAt,
      missing: false,
      last_source_event_id: lastEvent?.source_event_id ?? previous.last_source_event_id ?? null,
      last_event_time: lastEvent?.event_time ?? previous.last_event_time ?? null
    };
  }

  return {
    ok: issues.length === 0,
    inbox_dir: inboxDir,
    scanned_at: scannedAt,
    plan,
    event_count: batches.reduce((sum, batch) => sum + batch.event_count, 0),
    batches,
    cursor: nextCursor,
    issues,
    customer_message: issues.length === 0 ? "新增礼物记录读取完成。" : "部分新增记录需要检查。"
  };
}

export class LocalGiftSourceInboxScanner {
  constructor({
    inboxDir,
    cursorPath,
    sourceDescriptors = {},
    defaults = {}
  } = {}) {
    if (!inboxDir) {
      throw new Error("gift source inboxDir is required");
    }
    if (!cursorPath) {
      throw new Error("gift source cursorPath is required");
    }
    this.inboxDir = inboxDir;
    this.cursorPath = cursorPath;
    this.sourceDescriptors = sourceDescriptors;
    this.defaults = defaults;
    mkdirSync(inboxDir, { recursive: true });
    mkdirSync(dirname(cursorPath), { recursive: true });
    if (!existsSync(cursorPath)) {
      writeFileSync(cursorPath, `${JSON.stringify({
        cursor_id: createId("gift_inbox_cursor"),
        inbox_dir: inboxDir,
        updated_at: nowIso(),
        files: {}
      }, null, 2)}\n`, "utf8");
    }
  }

  getPlan() {
    return createLocalGiftSourceInboxPlan({
      inboxDir: this.inboxDir
    });
  }

  readCursor() {
    return JSON.parse(readFileSync(this.cursorPath, "utf8"));
  }

  scan(input = {}) {
    const result = readIncrementalLocalGiftSourceInbox({
      inboxDir: input.inbox_dir || this.inboxDir,
      cursorState: this.readCursor(),
      sourceDescriptors: this.sourceDescriptors,
      defaults: {
        ...this.defaults,
        ...(input.defaults ?? {})
      }
    });
    writeFileSync(this.cursorPath, `${JSON.stringify(result.cursor, null, 2)}\n`, "utf8");
    return result;
  }

  append(input = {}) {
    return appendLocalGiftSourceInboxEvents({
      inboxDir: input.inbox_dir || this.inboxDir,
      role: input.role,
      events: input.events ?? input.event ?? input.gifts ?? [],
      defaults: {
        ...this.defaults,
        ...(input.defaults ?? {})
      }
    });
  }
}

export function readLocalGiftSourceInboxFile({
  filePath,
  role = GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
  sourceDescriptor = createGiftSourceDescriptor({ role }),
  defaults = {}
} = {}) {
  if (!filePath) {
    throw new Error("gift source inbox filePath is required");
  }
  const lines = readFileSync(filePath, "utf8")
    .split("\n")
    .map((line) => line.trim())
    .filter(Boolean);
  return readLocalGiftSourceLines({
    lines,
    role,
    sourceDescriptor,
    defaults,
    filePath,
    startLineNo: 1
  });
}

function readLocalGiftSourceLines({
  lines = [],
  role = GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
  sourceDescriptor = createGiftSourceDescriptor({ role }),
  defaults = {},
  filePath,
  startLineNo = 1,
  stopOnFirstIssue = false
} = {}) {
  const events = [];
  const issues = [];

  for (const [index, line] of lines.entries()) {
    try {
      const rawGift = {
        ...defaults,
        ...JSON.parse(line)
      };
      const validation = validateLocalGiftBridgeEvent(rawGift, {
        requireLiveRoomId: true
      });
      if (!validation.ok) {
        issues.push(...validation.issues.map((issue) => ({
          ...issue,
          file_path: filePath,
          line_no: startLineNo + index,
          role
        })));
        if (stopOnFirstIssue) {
          break;
        }
        continue;
      }
      events.push(normalizeGiftEvent(rawGift, sourceDescriptor));
    } catch {
      issues.push({
        field: "line",
        reason_code: "gift_record_parse_failed",
        file_path: filePath,
        line_no: startLineNo + index,
        role
      });
      if (stopOnFirstIssue) {
        break;
      }
    }
  }

  return {
    batch: {
      role,
      file_path: filePath,
      event_count: events.length,
      events,
      missing: false
    },
    issues
  };
}

export function createRawGiftBackupManifest({
  filePath,
  records = [],
  sourceDescriptor = createGiftSourceDescriptor(),
  sessionContext = {},
  uploadRawDefault = false
} = {}) {
  const eventTimes = records.map((record) => record.event_time).filter(Boolean).sort();
  const receivedTimes = records.map((record) => record.received_at).filter(Boolean).sort();
  return {
    manifest_id: createId("raw_gift_manifest"),
    created_at: nowIso(),
    file_path: filePath,
    tenant_id: sessionContext.tenant_id ?? records[0]?.tenant_id ?? null,
    live_room_id: sessionContext.live_room_id ?? records[0]?.live_room_id ?? null,
    session_id: sessionContext.session_id ?? records[0]?.session_id ?? null,
    source_role: sourceDescriptor.role,
    source_id: sourceDescriptor.source_id,
    record_count: records.length,
    first_event_time: eventTimes[0] ?? null,
    last_event_time: eventTimes.at(-1) ?? null,
    last_received_at: receivedTimes.at(-1) ?? null,
    raw_payload_hashes: records.map((record) => record.raw_payload_hash),
    raw_payload_included: false,
    upload_raw_default: uploadRawDefault,
    local_only: true,
    customer_message: records.length > 0 ? "礼物记录准备完成。" : "礼物记录准备中，请稍等。"
  };
}

export function validateRawGiftBackupManifest(manifest = {}) {
  const issues = [];
  if (!manifest.file_path) {
    issues.push(createContractIssue("file_path", "raw_backup_path_required"));
  }
  if (manifest.raw_payload_included !== false) {
    issues.push(createContractIssue("raw_payload_included", "manifest_must_not_include_raw_payload"));
  }
  if (manifest.upload_raw_default !== false) {
    issues.push(createContractIssue("upload_raw_default", "raw_upload_disabled_by_default"));
  }
  if (manifest.local_only !== true) {
    issues.push(createContractIssue("local_only", "raw_backup_local_only"));
  }
  if (!Array.isArray(manifest.raw_payload_hashes)) {
    issues.push(createContractIssue("raw_payload_hashes", "raw_payload_hashes_required"));
  }
  return {
    ok: issues.length === 0,
    level: issues.length === 0 ? GIFT_SOURCE_CONTRACT_LEVELS.READY : GIFT_SOURCE_CONTRACT_LEVELS.BLOCKED,
    issues
  };
}

export function prepareGiftForLiveConfirmation({
  rawGift,
  sourceDescriptor = createGiftSourceDescriptor(),
  rawBackupStore = null,
  requireRawBackup = false,
  sessionContext = {},
  confirmationContext = {},
  seenKeys = new Set(),
  receivedAt = nowIso()
} = {}) {
  const issues = [];
  const normalized = normalizeGiftEvent(rawGift, sourceDescriptor);
  let rawBackupRecord = null;

  if (rawBackupStore) {
    try {
      rawBackupRecord = rawBackupStore.append(rawGift, {
        normalizedGift: normalized,
        receivedAt
      });
    } catch {
      issues.push("raw_backup_write_failed");
    }
  } else if (requireRawBackup) {
    issues.push("raw_backup_required");
  }

  const gift = rawBackupRecord
    ? {
        ...normalized,
        raw_ref: {
          raw_record_id: rawBackupRecord.raw_record_id,
          raw_payload_hash: rawBackupRecord.raw_payload_hash,
          source_id: rawBackupRecord.source_id
        }
      }
    : normalized;

  if (issues.length > 0) {
    return {
      status: GIFT_CONFIRMATION_STATUS.NEEDS_REVIEW,
      gift,
      rawBackupRecord,
      issues
    };
  }

  return {
    ...confirmRealtimeGift({
      gift,
      sourceDescriptor,
      context: {
        live_room_id: sessionContext.live_room_id,
        ...confirmationContext
      },
      seenKeys
    }),
    rawBackupRecord
  };
}

export function createMockGift(overrides = {}) {
  const giftCount = overrides.gift_count ?? 1;
  const unitValue = overrides.gift_unit_value ?? 1;

  return {
    source: overrides.source ?? EVENT_SOURCES.MOCK,
    source_event_id: overrides.source_event_id ?? createId("mock_gift"),
    event_time: overrides.event_time ?? nowIso(),
    live_room_id: overrides.live_room_id ?? "room_demo",
    gifter_user_id: overrides.gifter_user_id ?? "gifter_demo",
    gifter_display_name: overrides.gifter_display_name ?? "Demo Gifter",
    gift_id: overrides.gift_id ?? "rose",
    gift_name: overrides.gift_name ?? "Rose",
    gift_unit_value: unitValue,
    gift_count: giftCount,
    gift_total_value: overrides.gift_total_value ?? unitValue * giftCount,
    gift_combo_id: overrides.gift_combo_id ?? null,
    combo_count: overrides.combo_count ?? null,
    is_combo_final: overrides.is_combo_final ?? false
  };
}

export function createMockGiftSequence(session = {}) {
  const base = {
    live_room_id: session.live_room_id ?? "room_demo"
  };

  return [
    createMockGift({
      ...base,
      source_event_id: "gift_left_001",
      gifter_user_id: "user_left_fan",
      gift_id: "rose",
      gift_name: "Rose",
      gift_count: 10,
      gift_total_value: 10
    }),
    createMockGift({
      ...base,
      source_event_id: "gift_right_001",
      gifter_user_id: "user_right_fan",
      gift_id: "rocket",
      gift_name: "Rocket",
      gift_unit_value: 10,
      gift_count: 1,
      gift_total_value: 10
    }),
    createMockGift({
      ...base,
      source_event_id: "combo_left_001_10",
      gifter_user_id: "user_combo",
      gift_id: "rose",
      gift_name: "Rose",
      gift_combo_id: "combo_left_001",
      combo_count: 10,
      gift_count: 10,
      gift_total_value: 10
    }),
    createMockGift({
      ...base,
      source_event_id: "combo_left_001_20",
      gifter_user_id: "user_combo",
      gift_id: "rose",
      gift_name: "Rose",
      gift_combo_id: "combo_left_001",
      combo_count: 20,
      gift_count: 20,
      gift_total_value: 20,
      is_combo_final: true
    })
  ];
}

export function makeGiftIdempotencyKey(gift) {
  if (gift.source_event_id) {
    return `${gift.source}:${gift.source_event_id}`;
  }
  const bucket = Math.floor(Date.parse(gift.event_time) / 1000);
  return [
    gift.live_room_id,
    gift.source,
    gift.gifter_user_id,
    gift.gift_id,
    gift.gift_count,
    gift.gift_total_value,
    bucket
  ].join(":");
}

export function makeGiftMatchKey(gift) {
  const bucket = Math.floor(Date.parse(gift.event_time) / 1000);
  const recipientUserId = gift.audience_sec_open_id ?? gift.recipient_user_id ?? null;
  const platformEventId = stringOrNull(firstDefined(
    gift.platform_event_id,
    gift.platform_payload?.msg_id,
    gift.platform_payload?.message_id,
    gift.platform_payload?.messageId
  ));
  if (platformEventId) {
    const platformParts = [
      gift.live_room_id,
      "platform_event",
      platformEventId
    ];
    if (recipientUserId) {
      platformParts.push(`recipient:${recipientUserId}`);
    }
    return platformParts.join(":");
  }
  const parts = [
    gift.live_room_id,
    gift.gifter_user_id,
    gift.gift_id
  ];
  if (recipientUserId) {
    parts.push(`recipient:${recipientUserId}`);
  }
  parts.push(
    gift.source_event_id ?? "no_source_event",
    gift.gift_combo_id ?? "single",
    bucket,
    Number(gift.gift_count ?? 0),
    Number(gift.gift_total_value ?? 0)
  );
  return parts.join(":");
}

function makeGiftReviewKey(gift) {
  if (gift.review_match_key) {
    return gift.review_match_key;
  }
  const bucket = Math.floor(Date.parse(gift.event_time) / 1000);
  const recipientUserId = gift.audience_sec_open_id ?? gift.recipient_user_id ?? null;
  const platformEventId = stringOrNull(firstDefined(
    gift.platform_event_id,
    gift.platform_payload?.msg_id,
    gift.platform_payload?.message_id,
    gift.platform_payload?.messageId
  ));
  if (platformEventId) {
    const platformParts = [
      gift.live_room_id,
      "platform_event",
      platformEventId
    ];
    if (recipientUserId) {
      platformParts.push(`recipient:${recipientUserId}`);
    }
    return platformParts.join(":");
  }
  const parts = [
    gift.live_room_id,
    gift.gifter_user_id,
    gift.gift_id
  ];
  if (recipientUserId) {
    parts.push(`recipient:${recipientUserId}`);
  }
  parts.push(gift.gift_combo_id ?? "single", bucket);
  return parts.join(":");
}

export function normalizeGiftEvent(rawGift, sourceDescriptor = createGiftSourceDescriptor()) {
  const eventTime = rawGift.event_time ?? nowIso();
  const giftCount = Number(rawGift.gift_count ?? rawGift.count ?? 1);
  const unitValue = Number(rawGift.gift_unit_value ?? rawGift.unit_value ?? rawGift.gift_value ?? 1);
  const giftTotalValue = Number(rawGift.gift_total_value ?? unitValue * giftCount);
  const platformEventId = stringOrNull(firstDefined(
    rawGift.platform_event_id,
    rawGift.msg_id,
    rawGift.msgId,
    rawGift.message_id,
    rawGift.messageId,
    rawGift.platform_payload?.msg_id,
    rawGift.platform_payload?.message_id,
    rawGift.platform_payload?.messageId
  ));
  const normalized = {
    source: rawGift.source ?? sourceDescriptor.source,
    source_role: sourceDescriptor.role,
    source_id: sourceDescriptor.source_id,
    source_event_id: rawGift.source_event_id ?? platformEventId ?? rawGift.id ?? createId("gift_event"),
    platform_event_id: platformEventId,
    event_time: eventTime,
    observed_at: rawGift.observed_at ?? nowIso(),
    live_room_id: rawGift.live_room_id,
    gifter_user_id: rawGift.gifter_user_id ?? rawGift.user_id,
    gifter_display_name: rawGift.gifter_display_name ?? rawGift.display_name ?? "Guest",
    gift_id: rawGift.gift_id,
    gift_name: rawGift.gift_name ?? rawGift.gift_id,
    gift_unit_value: unitValue,
    gift_count: giftCount,
    gift_total_value: giftTotalValue,
    gift_combo_id: rawGift.gift_combo_id ?? null,
    combo_count: rawGift.combo_count ?? null,
    is_combo_final: rawGift.is_combo_final ?? false,
    audience_sec_open_id: rawGift.audience_sec_open_id ?? null,
    recipient_user_id: rawGift.recipient_user_id ?? rawGift.to_user_id ?? rawGift.audience_sec_open_id ?? null,
    recipient_source: rawGift.recipient_source ?? null,
    platform_payload: rawGift.platform_payload ?? null,
    raw_ref: rawGift.raw_ref ?? null
  };
  return {
    ...normalized,
    idempotency_key: makeGiftIdempotencyKey(normalized),
    match_key: makeGiftMatchKey(normalized)
  };
}

export function dedupeGiftEvents(gifts = [], seenKeys = new Set()) {
  const accepted = [];
  const duplicates = [];
  const nextSeen = new Set(seenKeys);

  for (const gift of gifts) {
    const key = gift.idempotency_key ?? makeGiftIdempotencyKey(gift);
    if (nextSeen.has(key)) {
      duplicates.push({
        ...gift,
        idempotency_key: key,
        confirmation_status: GIFT_CONFIRMATION_STATUS.DUPLICATE_IGNORED
      });
      continue;
    }
    nextSeen.add(key);
    accepted.push({
      ...gift,
      idempotency_key: key
    });
  }

  return {
    accepted,
    duplicates,
    seen_keys: nextSeen
  };
}

export function confirmRealtimeGift({
  gift,
  sourceDescriptor = createGiftSourceDescriptor(),
  context = {},
  seenKeys = new Set()
}) {
  const normalized = gift.idempotency_key ? gift : normalizeGiftEvent(gift, sourceDescriptor);
  const issues = [];

  if (context.live_room_id && normalized.live_room_id !== context.live_room_id) {
    issues.push("live_room_mismatch");
  }
  if (context.game_started_at && toEpochMs(normalized.event_time) < toEpochMs(context.game_started_at)) {
    issues.push("before_game_window");
  }
  if (context.game_ended_at && toEpochMs(normalized.event_time) > toEpochMs(context.game_ended_at) + Number(context.confirmation_window_ms ?? 0)) {
    issues.push("after_game_window");
  }
  if (seenKeys.has(normalized.idempotency_key)) {
    issues.push("duplicate_event");
  }
  if (![GIFT_SOURCE_HEALTH.HEALTHY, GIFT_SOURCE_HEALTH.LAGGING].includes(sourceDescriptor.health)) {
    issues.push("source_not_ready");
  }

  if (issues.includes("duplicate_event")) {
    return {
      status: GIFT_CONFIRMATION_STATUS.DUPLICATE_IGNORED,
      gift: normalized,
      issues
    };
  }

  if (issues.length > 0) {
    return {
      status: GIFT_CONFIRMATION_STATUS.NEEDS_REVIEW,
      gift: normalized,
      issues
    };
  }

  return {
    status: GIFT_CONFIRMATION_STATUS.LIVE_CONFIRMED,
    gift: normalized,
    issues: []
  };
}

export function selectGiftSourceRoute({
  sources = [],
  sensitivity = GAMEPLAY_SENSITIVITY.NORMAL
} = {}) {
  const targetLag = sensitivity === GAMEPLAY_SENSITIVITY.HIGH ? 3000 : 1500;
  const confirmationWindowMs = sensitivity === GAMEPLAY_SENSITIVITY.HIGH ? 3000 : 1500;
  const primary = sources.find((source) => source.role === GIFT_SOURCE_ROLES.PRIMARY_REALTIME);
  const backup = sources.find((source) => source.role === GIFT_SOURCE_ROLES.BACKUP_REALTIME);

  if (isHealthyForTarget(primary, targetLag)) {
    return {
      decision: GIFT_ROUTE_DECISIONS.USE_PRIMARY,
      active_source_id: primary.source_id,
      standby_source_id: backup?.source_id ?? null,
      confirmation_window_ms: confirmationWindowMs,
      reason_code: "primary_ready"
    };
  }

  if (isHealthyForTarget(backup, targetLag)) {
    return {
      decision: GIFT_ROUTE_DECISIONS.USE_BACKUP,
      active_source_id: backup.source_id,
      standby_source_id: primary?.source_id ?? null,
      confirmation_window_ms: Math.max(confirmationWindowMs, 3000),
      reason_code: primary ? "primary_not_ready_backup_ready" : "backup_ready"
    };
  }

  if (primary?.connected || backup?.connected) {
    return {
      decision: GIFT_ROUTE_DECISIONS.WAIT_FOR_CONFIRMATION,
      active_source_id: primary?.source_id ?? backup?.source_id ?? null,
      standby_source_id: backup?.source_id ?? null,
      confirmation_window_ms: Math.max(confirmationWindowMs, 3000),
      reason_code: "source_confirmation_needed"
    };
  }

  return {
    decision: GIFT_ROUTE_DECISIONS.NEEDS_REVIEW,
    active_source_id: null,
    standby_source_id: null,
    confirmation_window_ms: Math.max(confirmationWindowMs, 3000),
    reason_code: "source_not_connected"
  };
}

export function mergeRealtimeSourceEvents({
  primaryEvents = [],
  backupEvents = []
} = {}) {
  const acceptedByKey = new Map();
  const acceptedByReviewKey = new Map();
  const duplicates = [];
  const confirmations = [];
  const mismatches = [];

  for (const gift of primaryEvents) {
    const key = gift.idempotency_key ?? makeGiftIdempotencyKey(gift);
    const matchKey = gift.match_key ?? makeGiftMatchKey(gift);
    const reviewKey = makeGiftReviewKey(gift);
    const existing = acceptedByKey.get(matchKey);
    if (existing) {
      duplicates.push(gift);
      existing.source_confirmations = [
        ...new Set([...existing.source_confirmations, gift.source_id ?? gift.source ?? "primary"])
      ];
      if (Number(existing.gift_total_value) !== Number(gift.gift_total_value)
        || Number(existing.gift_count) !== Number(gift.gift_count)) {
        mismatches.push({
          idempotency_key: existing.idempotency_key,
          match_key: matchKey,
          primary_match_key: existing.match_key,
          backup_match_key: null,
          ambiguous_match: existing.match_key !== matchKey,
          primary_value: Number(existing.gift_total_value),
          backup_value: Number(gift.gift_total_value),
          primary_count: Number(existing.gift_count),
          backup_count: Number(gift.gift_count),
          conflict_role: GIFT_SOURCE_ROLES.PRIMARY_REALTIME
        });
      }
      continue;
    }
    const row = {
      ...gift,
      idempotency_key: key,
      match_key: matchKey,
      source_confirmations: [gift.source_id ?? gift.source ?? "primary"]
    };
    acceptedByKey.set(matchKey, row);
    appendReviewMatch(acceptedByReviewKey, reviewKey, row);
  }

  for (const gift of backupEvents) {
    const key = gift.idempotency_key ?? makeGiftIdempotencyKey(gift);
    const matchKey = gift.match_key ?? makeGiftMatchKey(gift);
    const reviewKey = makeGiftReviewKey(gift);
    const existing = acceptedByKey.get(matchKey) ?? uniqueReviewMatch(acceptedByReviewKey, reviewKey);
    if (!existing) {
      const row = {
        ...gift,
        idempotency_key: key,
        match_key: matchKey,
        source_confirmations: [gift.source_id ?? gift.source ?? "backup"]
      };
      acceptedByKey.set(matchKey, row);
      appendReviewMatch(acceptedByReviewKey, reviewKey, row);
      continue;
    }
    duplicates.push(gift);
    existing.source_confirmations = [
      ...new Set([...existing.source_confirmations, gift.source_id ?? gift.source ?? "backup"])
    ];
    const primaryValue = Number(existing.gift_total_value);
    const backupValue = Number(gift.gift_total_value);
    if (primaryValue !== backupValue || Number(existing.gift_count) !== Number(gift.gift_count)) {
      mismatches.push({
        idempotency_key: existing.idempotency_key,
        match_key: matchKey,
        primary_match_key: existing.match_key,
        backup_match_key: matchKey,
        ambiguous_match: existing.match_key !== matchKey,
        primary_value: primaryValue,
        backup_value: backupValue,
        primary_count: Number(existing.gift_count),
        backup_count: Number(gift.gift_count)
      });
    } else {
      confirmations.push({
        idempotency_key: key,
        match_key: existing.match_key,
        backup_match_key: matchKey,
        confirmed_by: gift.source_id ?? gift.source ?? "backup"
      });
    }
  }

  return {
    accepted: [...acceptedByKey.values()],
    duplicates,
    confirmations,
    mismatches
  };
}

function appendReviewMatch(index, key, row) {
  index.set(key, [...(index.get(key) ?? []), row]);
}

function uniqueReviewMatch(index, key) {
  const rows = index.get(key) ?? [];
  return rows.length === 1 ? rows[0] : null;
}

export function createGiftRouteLedgerEvent({
  route,
  previousSourceId = null,
  sessionContext = {}
}) {
  return {
    event_type: EVENT_TYPES.GIFT_SOURCE_ROUTE_CHANGED,
    tenant_id: sessionContext.tenant_id,
    live_room_id: sessionContext.live_room_id,
    session_id: sessionContext.session_id ?? null,
    source: EVENT_SOURCES.SYSTEM,
    operator_id: null,
    idempotency_key: `gift_route:${sessionContext.session_id ?? "global"}:${previousSourceId ?? "none"}:${route.active_source_id ?? "none"}`,
    payload: {
      previous_source_id: previousSourceId,
      active_source_id: route.active_source_id,
      standby_source_id: route.standby_source_id,
      decision: route.decision,
      reason_code: route.reason_code,
      confirmation_window_ms: route.confirmation_window_ms
    }
  };
}

export function createGiftSourceMismatchLedgerEvent({
  mismatch,
  sessionContext = {}
}) {
  return {
    event_type: EVENT_TYPES.GIFT_SOURCE_MISMATCH_REPORTED,
    tenant_id: sessionContext.tenant_id,
    live_room_id: sessionContext.live_room_id,
    session_id: sessionContext.session_id ?? null,
    source: EVENT_SOURCES.SYSTEM,
    operator_id: null,
    idempotency_key: `gift_mismatch:${sessionContext.session_id ?? "global"}:${mismatch.idempotency_key}`,
    payload: {
      mismatch
    }
  };
}

function isHealthyForTarget(source, targetLag) {
  return Boolean(source)
    && source.connected
    && source.health === GIFT_SOURCE_HEALTH.HEALTHY
    && Number(source.event_lag_ms_p95 ?? 0) <= targetLag;
}

function sourceForRole(role) {
  const map = {
    [GIFT_SOURCE_ROLES.MOCK]: EVENT_SOURCES.MOCK,
    [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: EVENT_SOURCES.REALTIME_PRIMARY,
    [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: EVENT_SOURCES.REALTIME_BACKUP,
    [GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN]: EVENT_SOURCES.LIVE_TOOL_GUARDIAN,
    [GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]: EVENT_SOURCES.OFFICIAL_BACKEND,
    [GIFT_SOURCE_ROLES.MANUAL_CORRECTION]: EVENT_SOURCES.OPERATOR
  };
  return map[role] ?? EVENT_SOURCES.MOCK;
}

function defaultCapabilitiesForRole(role) {
  const map = {
    [GIFT_SOURCE_ROLES.MOCK]: [
      GIFT_SOURCE_CAPABILITIES.RAW_EVENT_BACKUP,
      GIFT_SOURCE_CAPABILITIES.NORMALIZED_GIFT_DETAIL
    ],
    [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: [
      GIFT_SOURCE_CAPABILITIES.RAW_EVENT_BACKUP,
      GIFT_SOURCE_CAPABILITIES.NORMALIZED_GIFT_DETAIL
    ],
    [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: [
      GIFT_SOURCE_CAPABILITIES.RAW_EVENT_BACKUP,
      GIFT_SOURCE_CAPABILITIES.NORMALIZED_GIFT_DETAIL
    ],
    [GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN]: [
      GIFT_SOURCE_CAPABILITIES.RAW_EVENT_BACKUP,
      GIFT_SOURCE_CAPABILITIES.ROOM_IDENTITY,
      GIFT_SOURCE_CAPABILITIES.LIVE_STATUS
    ],
    [GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]: [
      GIFT_SOURCE_CAPABILITIES.OFFICIAL_RECORDS
    ],
    [GIFT_SOURCE_ROLES.MANUAL_CORRECTION]: [
      GIFT_SOURCE_CAPABILITIES.MANUAL_REVIEW
    ]
  };
  return map[role] ?? [];
}

function createNormalizedGiftPreview(gift) {
  return {
    idempotency_key: gift.idempotency_key ?? null,
    match_key: gift.match_key ?? null,
    live_room_id: gift.live_room_id ?? null,
    source_event_id: gift.source_event_id ?? null,
    platform_event_id: gift.platform_event_id ?? null,
    event_time: gift.event_time ?? null,
    gift_id: gift.gift_id ?? null,
    gift_count: Number(gift.gift_count ?? 0),
    gift_total_value: Number(gift.gift_total_value ?? 0)
  };
}

function normalizeDouyinHeaders(headers = {}) {
  const normalized = {};
  if (headers && typeof headers.forEach === "function") {
    headers.forEach((value, key) => {
      normalized[String(key).toLowerCase()] = String(value);
    });
    return normalized;
  }
  for (const [key, value] of Object.entries(headers ?? {})) {
    normalized[String(key).toLowerCase()] = Array.isArray(value) ? String(value[0]) : String(value);
  }
  return normalized;
}

function bodyToUtf8String(body) {
  if (body === undefined || body === null) {
    return "";
  }
  if (typeof body === "string") {
    return body;
  }
  if (body instanceof Uint8Array) {
    return Buffer.from(body).toString("utf8");
  }
  return JSON.stringify(body);
}

function constantTimeEqual(expected, provided) {
  const expectedBuffer = Buffer.from(String(expected));
  const providedBuffer = Buffer.from(String(provided));
  if (expectedBuffer.length !== providedBuffer.length) {
    return false;
  }
  return timingSafeEqual(expectedBuffer, providedBuffer);
}

function parseJsonMaybe(value) {
  if (value === undefined || value === null || value === "") {
    return {
      value: [],
      error: null
    };
  }
  if (typeof value !== "string") {
    return {
      value,
      error: null
    };
  }
  try {
    return {
      value: JSON.parse(value),
      error: null
    };
  } catch (error) {
    return {
      value: null,
      error
    };
  }
}

function extractDouyinPayloadList(value) {
  const parsed = typeof value === "string" ? parseJsonMaybe(value).value : value;
  if (!parsed) {
    return [];
  }
  if (Array.isArray(parsed)) {
    return parsed;
  }
  if (Array.isArray(parsed.payload)) {
    return parsed.payload;
  }
  if (Array.isArray(parsed.data)) {
    return parsed.data;
  }
  if (Array.isArray(parsed.data_list)) {
    return parsed.data_list;
  }
  if (typeof parsed.payload === "string") {
    return extractDouyinPayloadList(parsed.payload);
  }
  if (typeof parsed.body === "string") {
    return extractDouyinPayloadList(parsed.body);
  }
  return [parsed];
}

function timestampToIso(value) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) {
    return null;
  }
  const millis = numeric < 10_000_000_000 ? numeric * 1000 : numeric;
  const date = new Date(millis);
  if (Number.isNaN(date.getTime())) {
    return null;
  }
  return date.toISOString();
}

function createReadinessCheck(checkId, ready, action) {
  return {
    check_id: checkId,
    ready: ready === true,
    action
  };
}

async function parseFetchJson(response) {
  try {
    return await response.json();
  } catch {
    try {
      return JSON.parse(await response.text());
    } catch {
      return null;
    }
  }
}

function canRoleDriveLiveScore(role) {
  return [
    GIFT_SOURCE_ROLES.MOCK,
    GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    GIFT_SOURCE_ROLES.BACKUP_REALTIME
  ].includes(role);
}

function rawBackupRequiredForRole(role) {
  return [
    GIFT_SOURCE_ROLES.MOCK,
    GIFT_SOURCE_ROLES.PRIMARY_REALTIME,
    GIFT_SOURCE_ROLES.BACKUP_REALTIME,
    GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN
  ].includes(role);
}

function defaultTargetLagForRole(role) {
  if (role === GIFT_SOURCE_ROLES.BACKUP_REALTIME) {
    return 2500;
  }
  if (role === GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN) {
    return 5000;
  }
  return 1500;
}

function defaultConfirmationWindowForRole(role) {
  if (role === GIFT_SOURCE_ROLES.OFFICIAL_BACKEND) {
    return 0;
  }
  return role === GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN ? 5000 : 3000;
}

function defaultFieldMapForRole(role) {
  if (![GIFT_SOURCE_ROLES.MOCK, GIFT_SOURCE_ROLES.PRIMARY_REALTIME, GIFT_SOURCE_ROLES.BACKUP_REALTIME].includes(role)) {
    return {};
  }
  return {
    source_event_id: "source_event_id",
    event_time: "event_time",
    live_room_id: "live_room_id",
    gifter_user_id: "gifter_user_id",
    gifter_display_name: "gifter_display_name",
    gift_id: "gift_id",
    gift_name: "gift_name",
    gift_count: "gift_count",
    gift_total_value: "gift_total_value",
    gift_combo_id: "gift_combo_id"
  };
}

function labelForLocalInboxRole(role) {
  const map = {
    [GIFT_SOURCE_ROLES.PRIMARY_REALTIME]: "主线路记录",
    [GIFT_SOURCE_ROLES.BACKUP_REALTIME]: "备用线路记录",
    [GIFT_SOURCE_ROLES.LIVE_TOOL_GUARDIAN]: "画面核对记录",
    [GIFT_SOURCE_ROLES.OFFICIAL_BACKEND]: "下播复核记录"
  };
  return map[role] ?? "礼物记录";
}

function createContractIssue(field, reasonCode) {
  return {
    field,
    reason_code: reasonCode
  };
}
