import { createHash } from "node:crypto";
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";

import {
  EVENT_SOURCES,
  EVENT_TYPES,
  createId,
  nowIso,
  toEpochMs
} from "../../shared-types/src/index.js";

export const P1_EVIDENCE_SOURCE_ROLES = Object.freeze({
  PRIMARY_REALTIME: "primary_realtime",
  BACKUP_REALTIME: "backup_realtime",
  LIVE_TOOL_GUARDIAN: "live_tool_guardian",
  RAW_RECORDING_GUARDIAN: "raw_recording_guardian",
  AFTER_LIVE_REPAIR: "after_live_repair",
  MANUAL_REVIEW: "manual_review"
});

export const P1_CONFIRMATION_LEVELS = Object.freeze({
  CONFIRMED: "confirmed",
  CORROBORATED: "corroborated",
  INFERRED: "inferred",
  PROVISIONAL: "provisional",
  MISSING: "missing"
});

export const P1_REPORT_MODES = Object.freeze({
  FORMAL_ATTRIBUTION: "formal_attribution",
  PARTIAL_REPORT: "partial_report",
  FACTS_ONLY: "facts_only",
  HOLD_FOR_REPAIR: "hold_for_repair",
  BLOCKED_MISSING_EVIDENCE: "blocked_missing_evidence"
});

export const P1_EVIDENCE_EVENT_TYPES = Object.freeze({
  PUBLIC_CHAT_RECEIVED: EVENT_TYPES.PUBLIC_CHAT_RECEIVED,
  VIEWER_MEMBER_JOINED: EVENT_TYPES.VIEWER_MEMBER_JOINED,
  VIEWER_FOLLOWED: EVENT_TYPES.VIEWER_FOLLOWED,
  VIEWER_SHARED: EVENT_TYPES.VIEWER_SHARED,
  LIKE_BURST_RECORDED: EVENT_TYPES.LIKE_BURST_RECORDED,
  ROOM_METRIC_SAMPLED: EVENT_TYPES.ROOM_METRIC_SAMPLED,
  VIEWER_VISIBLE_LABELS_OBSERVED: EVENT_TYPES.VIEWER_VISIBLE_LABELS_OBSERVED,
  FAN_CLUB_EVENT_RECORDED: EVENT_TYPES.FAN_CLUB_EVENT_RECORDED,
  ROOM_PIN_UPDATED: EVENT_TYPES.ROOM_PIN_UPDATED,
  PROMOTION_OR_PRODUCT_PINNED: EVENT_TYPES.PROMOTION_OR_PRODUCT_PINNED,
  LIVE_BATTLE_STATE_CHANGED: EVENT_TYPES.LIVE_BATTLE_STATE_CHANGED,
  SYSTEM_NOTICE_RECEIVED: EVENT_TYPES.SYSTEM_NOTICE_RECEIVED,
  MODERATION_ACTION_RECORDED: EVENT_TYPES.MODERATION_ACTION_RECORDED,
  HOST_OR_ROLE_SNAPSHOT: EVENT_TYPES.HOST_OR_ROLE_SNAPSHOT,
  LIVE_SOURCE_HEALTH_REPORTED: EVENT_TYPES.LIVE_SOURCE_HEALTH_REPORTED,
  STREAM_LIFECYCLE_RECORDED: EVENT_TYPES.STREAM_LIFECYCLE_RECORDED,
  MEDIA_RECORDING_STARTED: EVENT_TYPES.MEDIA_RECORDING_STARTED,
  MEDIA_SEGMENT_WRITTEN: EVENT_TYPES.MEDIA_SEGMENT_WRITTEN,
  MEDIA_RECORDING_ENDED: EVENT_TYPES.MEDIA_RECORDING_ENDED,
  P1_CAPTURE_DEGRADED: EVENT_TYPES.P1_CAPTURE_DEGRADED,
  P1_CAPTURE_RECOVERED: EVENT_TYPES.P1_CAPTURE_RECOVERED,
  P1_REPLAY_COMPLETED: EVENT_TYPES.P1_REPLAY_COMPLETED,
  ANALYTICS_EVIDENCE_HEALTH_REPORTED: EVENT_TYPES.ANALYTICS_EVIDENCE_HEALTH_REPORTED,
  ANALYTICS_CANDIDATE_WINDOW_CREATED: EVENT_TYPES.ANALYTICS_CANDIDATE_WINDOW_CREATED
});

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

export const P1_CAPTURE_PRESSURE_STATES = Object.freeze({
  HEALTHY: "healthy",
  ATTENTION: "attention",
  DEGRADED: "degraded"
});

export const P1_LOCAL_INBOX_STATES = Object.freeze({
  READY: "ready",
  NEEDS_REVIEW: "needs_review"
});

const USER_RELATED_EVENTS = new Set([
  EVENT_TYPES.PUBLIC_CHAT_RECEIVED,
  EVENT_TYPES.VIEWER_MEMBER_JOINED,
  EVENT_TYPES.VIEWER_FOLLOWED,
  EVENT_TYPES.VIEWER_SHARED,
  EVENT_TYPES.VIEWER_VISIBLE_LABELS_OBSERVED,
  EVENT_TYPES.FAN_CLUB_EVENT_RECORDED,
  EVENT_TYPES.MODERATION_ACTION_RECORDED
]);

const P1_EVENT_SYNC_HINTS = Object.freeze({
  [EVENT_TYPES.LIVE_SOURCE_HEALTH_REPORTED]: P1_SYNC_PRIORITY_HINTS.HIGH,
  [EVENT_TYPES.P1_CAPTURE_DEGRADED]: P1_SYNC_PRIORITY_HINTS.HIGH,
  [EVENT_TYPES.P1_CAPTURE_RECOVERED]: P1_SYNC_PRIORITY_HINTS.HIGH,
  [EVENT_TYPES.P1_REPLAY_COMPLETED]: P1_SYNC_PRIORITY_HINTS.HIGH,
  [EVENT_TYPES.ANALYTICS_EVIDENCE_HEALTH_REPORTED]: P1_SYNC_PRIORITY_HINTS.HIGH,
  [EVENT_TYPES.LIKE_BURST_RECORDED]: P1_SYNC_PRIORITY_HINTS.LOW,
  [EVENT_TYPES.ROOM_METRIC_SAMPLED]: P1_SYNC_PRIORITY_HINTS.LOW
});

export function p1SyncPriorityHint(eventType) {
  return P1_EVENT_SYNC_HINTS[eventType] ?? P1_SYNC_PRIORITY_HINTS.NORMAL;
}

export function isP1EvidenceEventType(eventType) {
  return Object.values(P1_EVIDENCE_EVENT_TYPES).includes(eventType);
}

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

export function createP1EvidenceSourceDescriptor(input = {}) {
  const role = input.role ?? P1_EVIDENCE_SOURCE_ROLES.PRIMARY_REALTIME;
  return {
    source_id: input.source_id ?? createId("p1_source"),
    role,
    source: input.source ?? sourceForRole(role),
    platform: input.platform ?? "tiktok",
    display_name: input.display_name ?? role,
    connected: input.connected ?? true,
    health: input.health ?? "healthy",
    event_lag_ms_p95: Number(input.event_lag_ms_p95 ?? 0),
    event_count: Number(input.event_count ?? 0),
    last_event_at: input.last_event_at ?? null,
    updated_at: input.updated_at ?? nowIso()
  };
}

export function createRawP1EvidenceRecord({
  rawEvent = {},
  sourceDescriptor = createP1EvidenceSourceDescriptor(),
  sessionContext = {},
  normalizedEvent = null,
  sequenceNo = 1,
  receivedAt = nowIso(),
  decoderVersion = "p1_decoder_v1",
  adapterVersion = "p1_adapter_v1"
} = {}) {
  const eventTime = firstDefined(rawEvent.event_time, rawEvent.timestamp, rawEvent.create_time, rawEvent.createTime, receivedAt);
  const platformEventType = stringOrNull(firstDefined(
    rawEvent.platform_event_type,
    rawEvent.type,
    rawEvent.event_type,
    rawEvent.event,
    rawEvent.method,
    rawEvent.message_type,
    rawEvent.messageType
  ));
  const sourceEventId = stringOrNull(firstDefined(
    rawEvent.source_event_id,
    rawEvent.msg_id,
    rawEvent.msgId,
    rawEvent.message_id,
    rawEvent.messageId,
    rawEvent.id
  ));
  const rawPayloadHash = hashRawP1Payload(rawEvent);
  return {
    raw_record_id: createId("raw_p1"),
    tenant_id: sessionContext.tenant_id ?? null,
    live_room_id: firstString(rawEvent.live_room_id, rawEvent.room_id, rawEvent.roomId, sessionContext.live_room_id),
    session_id: sessionContext.session_id ?? null,
    source_role: sourceDescriptor.role,
    source_id: sourceDescriptor.source_id,
    platform: sourceDescriptor.platform ?? "tiktok",
    platform_event_type: platformEventType,
    source_event_id: sourceEventId,
    event_time: normalizeEventTime(eventTime),
    received_at: receivedAt,
    sequence_no: Number(sequenceNo),
    raw_payload_hash: rawPayloadHash,
    raw_payload: rawEvent,
    normalized_preview: normalizedEvent ? createNormalizedP1Preview(normalizedEvent) : null,
    decoder_version: decoderVersion,
    adapter_version: adapterVersion
  };
}

export class NdjsonRawP1EvidenceStore {
  constructor({ filePath, sourceDescriptor = createP1EvidenceSourceDescriptor(), sessionContext = {} } = {}) {
    if (!filePath) {
      throw new Error("raw P1 evidence filePath is required");
    }
    this.filePath = filePath;
    this.sourceDescriptor = sourceDescriptor;
    this.sessionContext = sessionContext;
    mkdirSync(dirname(filePath), { recursive: true });
    if (!existsSync(filePath)) {
      writeFileSync(filePath, "", "utf8");
    }
  }

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

  listRecords(options = {}) {
    return readRawP1EvidenceRecordsFromFile(this.filePath, options).records;
  }

  scan(options = {}) {
    const parsed = readRawP1EvidenceRecordsFromFile(this.filePath, {
      tolerateBadLines: true,
      ...options
    });
    const manifest = this.createManifest({ records: parsed.records });
    const integrity = scanRawP1EvidenceIntegrity({
      records: parsed.records,
      manifest,
      sessionContext: this.sessionContext
    });
    return {
      ok: parsed.rejected.length === 0 && integrity.ok,
      file: createLocalP1FileIdentity(this.filePath),
      record_count: parsed.records.length,
      rejected_count: parsed.rejected.length,
      rejected: parsed.rejected,
      integrity_issues: integrity.issues,
      state: parsed.rejected.length === 0 && integrity.ok
        ? P1_LOCAL_INBOX_STATES.READY
        : P1_LOCAL_INBOX_STATES.NEEDS_REVIEW
    };
  }

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

  replay(input = {}) {
    return replayRawP1EvidenceRecords({
      records: this.listRecords(),
      sourceDescriptor: this.sourceDescriptor,
      sessionContext: this.sessionContext,
      ...input
    });
  }
}

export function createRawP1EvidenceManifest({
  filePath = null,
  records = [],
  sourceDescriptor = createP1EvidenceSourceDescriptor(),
  sessionContext = {},
  uploadRawDefault = false,
  createdAt = nowIso()
} = {}) {
  return {
    manifest_id: createId("p1_manifest"),
    created_at: createdAt,
    tenant_id: sessionContext.tenant_id ?? records.find((record) => record.tenant_id)?.tenant_id ?? null,
    live_room_id: sessionContext.live_room_id ?? records.find((record) => record.live_room_id)?.live_room_id ?? null,
    session_id: sessionContext.session_id ?? records.find((record) => record.session_id)?.session_id ?? null,
    source_role: sourceDescriptor.role,
    source_id: sourceDescriptor.source_id,
    platform: sourceDescriptor.platform ?? "tiktok",
    file_path: filePath,
    file_hash: hashRawP1Payload(records.map((record) => ({
      raw_record_id: record.raw_record_id,
      sequence_no: record.sequence_no,
      raw_payload_hash: record.raw_payload_hash
    }))),
    record_count: records.length,
    first_sequence_no: records[0]?.sequence_no ?? null,
    last_sequence_no: records.at(-1)?.sequence_no ?? null,
    adapter_versions: [...new Set(records.map((record) => record.adapter_version).filter(Boolean))],
    decoder_versions: [...new Set(records.map((record) => record.decoder_version).filter(Boolean))],
    raw_payload_included: false,
    upload_raw_default: uploadRawDefault === true
  };
}

export function validateRawP1EvidenceManifest(manifest = {}, { records = null, allowPending = false } = {}) {
  const issues = [];
  const requiredFields = ["tenant_id", "session_id", "source_role", "source_id"];
  const recordCount = Number(manifest.record_count);
  const pendingWithoutRecords = allowPending === true && Number.isFinite(recordCount) && recordCount === 0;
  if (!pendingWithoutRecords) {
    requiredFields.push("live_room_id");
  }
  for (const field of requiredFields) {
    if (!manifest[field]) {
      issues.push(createIssue(field, "field_required"));
    }
  }
  if (manifest.raw_payload_included === true) {
    issues.push(createIssue("raw_payload_included", "manifest_must_not_include_raw_payload"));
  }
  if (manifest.upload_raw_default === true) {
    issues.push(createIssue("upload_raw_default", "raw_upload_disabled_by_default"));
  }
  if (!Number.isInteger(manifest.record_count) || manifest.record_count < 0) {
    issues.push(createIssue("record_count", "record_count_invalid"));
  }
  if (records) {
    issues.push(...scanRawP1EvidenceIntegrity({ records, manifest }).issues);
  }
  return {
    ok: issues.length === 0,
    issues
  };
}

export function normalizeP1EvidenceEvent(input = {}) {
  const eventType = input.event_type;
  const issues = [];
  const expectedLiveRoomId = input.expected_live_room_id ?? input.expectedLiveRoomId ?? null;
  const liveRoomId = input.live_room_id ?? null;
  const rawRef = normalizeP1RawRef(input.raw_ref);
  if (input.raw_ref && !rawRef) {
    issues.push(createIssue("raw_ref", "raw_ref_must_be_opaque"));
  }
  if (!isP1EvidenceEventType(eventType)) {
    issues.push(createIssue("event_type", "unknown_p1_event_type"));
  }
  if (!liveRoomId) {
    issues.push(createIssue("live_room_id", "live_room_id_required"));
  }
  if (expectedLiveRoomId && liveRoomId && String(expectedLiveRoomId) !== String(liveRoomId)) {
    issues.push(createIssue("live_room_id", "p1_live_room_mismatch"));
  }
  if (USER_RELATED_EVENTS.has(eventType) && !input.actor_user_id && !input.payload?.user_snapshot?.platform_user_id) {
    issues.push(createIssue("actor_user_id", "user_id_required"));
  }
  const eventTime = normalizeEventTime(input.event_time ?? input.payload?.time_provenance?.platform_event_time ?? nowIso());
  const fieldMappingStatus = input.field_mapping_status ?? null;
  const confidence = input.confidence ?? confidenceForP1Evidence({
    rawRef,
    fieldMappingStatus
  });
  const payload = createP1PayloadEnvelope({
    eventType,
    platform: input.platform,
    sourceRole: input.source_role,
    sourceId: input.source_id,
    sourceEventId: input.source_event_id,
    confidence,
    rawRef,
    rawPayloadHash: input.raw_payload_hash,
    schemaVersion: input.schema_version,
    adapterVersion: input.adapter_version,
    captureQualityFlags: input.capture_quality_flags,
    timeProvenance: input.time_provenance,
    userSnapshot: input.user_snapshot,
    fieldMappingStatus,
    data: input.data,
    syncPriorityHint: input.sync_priority_hint ?? p1SyncPriorityHint(eventType),
    canonicalFactKey: input.canonical_fact_key ?? makeP1CanonicalFactKey({
      tenantId: input.tenant_id,
      liveRoomId,
      sessionId: input.session_id,
      eventType,
      sourceEventId: input.source_event_id,
      platformUserId: input.actor_user_id ?? input.user_snapshot?.platform_user_id,
      eventTime,
      semanticPayload: input.data
    })
  });
  issues.push(...validateP1EventData(eventType, payload.data));
  const idempotencyKey = input.idempotency_key ?? makeP1IdempotencyKey({
    tenantId: input.tenant_id,
    liveRoomId,
    sessionId: input.session_id,
    eventType,
    sourceRole: payload.source_role,
    sourceEventId: payload.source_event_id,
    platformUserId: input.actor_user_id ?? payload.user_snapshot?.platform_user_id,
    eventTime,
    semanticPayload: payload.data
  });
  return {
    ok: issues.length === 0,
    issues,
    event: {
      event_type: eventType,
      tenant_id: input.tenant_id,
      live_room_id: liveRoomId,
      session_id: input.session_id,
      source: input.source ?? sourceForRole(payload.source_role),
      actor_user_id: input.actor_user_id ?? payload.user_snapshot?.platform_user_id ?? null,
      event_time: eventTime,
      received_time: input.received_time ?? payload.time_provenance.received_wall_time,
      idempotency_key: idempotencyKey,
      payload
    }
  };
}

export function normalizeTiktokWebcastP1Payload(payload, options = {}) {
  const items = extractTiktokWebcastMessageItems(payload);
  const events = [];
  const rejected = [];
  for (const item of items) {
    const normalized = normalizeTiktokWebcastP1Event(item, options);
    if (normalized.ok) {
      events.push(...normalized.events);
    } else if (normalized.reason_code !== "p1_event_not_supported" || !isTiktokGiftLike(extractTiktokShape(item))) {
      rejected.push({
        source_event_id: sourceEventIdFromShape(extractTiktokShape(item)),
        reason_code: normalized.reason_code,
        issues: normalized.issues ?? []
      });
    }
  }
  return {
    ok: rejected.length === 0,
    event_count: events.length,
    events,
    rejected,
    issues: rejected.flatMap((item) => item.issues ?? [])
  };
}

export function normalizeTiktokWebcastP1Event(payload, {
  tenantId = null,
  sessionId = null,
  liveRoomId = null,
  provider = "tiktok_webcast",
  sourceRole = P1_EVIDENCE_SOURCE_ROLES.PRIMARY_REALTIME,
  sourceId = "source_tiktok_p1_primary",
  receivedAt = nowIso(),
  decoderVersion = "tiktok_webcast_p1_decoder_v1",
  adapterVersion = "tiktok_webcast_p1_adapter_v1",
  localMonotonicMs = null,
  collectorClockOffsetMs = null,
  rawRef = null,
  enforceLiveRoomMatch = true
} = {}) {
  const shape = extractTiktokShape(payload);
  if (!shape || typeof shape !== "object") {
    return {
      ok: false,
      reason_code: "p1_event_payload_invalid",
      issues: [createIssue("payload", "p1_event_payload_invalid")]
    };
  }
  if (isTiktokGiftLike(shape)) {
    return {
      ok: false,
      reason_code: "p1_event_not_supported",
      issues: []
    };
  }
  const mappedType = mapTiktokP1EventType(shape);
  const userSnapshot = createTiktokUserSnapshot(shape, receivedAt);
  const liveRoom = firstString(shape.live_room_id, shape.room_id, shape.roomId, shape.room?.id, liveRoomId);
  if (enforceLiveRoomMatch && liveRoomId && liveRoom && String(liveRoomId) !== String(liveRoom)) {
    return {
      ok: false,
      reason_code: "p1_live_room_mismatch",
      issues: [createIssue("live_room_id", "p1_live_room_mismatch")]
    };
  }
  const sourceEventId = sourceEventIdFromShape(shape);
  if (mappedType && !sourceEventId) {
    return {
      ok: false,
      reason_code: "p1_source_event_id_required",
      issues: [createIssue("source_event_id", "p1_source_event_id_required")]
    };
  }
  const eventTime = normalizeEventTime(firstDefined(
    shape.event_time,
    shape.timestamp,
    shape.create_time,
    shape.createTime,
    shape.eventTime,
    receivedAt
  ));
  const common = {
    tenant_id: tenantId,
    live_room_id: liveRoom,
    session_id: sessionId,
    platform: "tiktok",
    source_role: sourceRole,
    source_id: sourceId,
    source_event_id: sourceEventId,
    event_time: eventTime,
    received_time: receivedAt,
    actor_user_id: userSnapshot.platform_user_id,
    confidence: rawRef ? P1_CONFIRMATION_LEVELS.CONFIRMED : P1_CONFIRMATION_LEVELS.PROVISIONAL,
    raw_ref: rawRef,
    raw_payload_hash: hashRawP1Payload(payload),
    schema_version: "p1_event_v1",
    adapter_version: adapterVersion,
    capture_quality_flags: rawRef ? [] : ["raw_backup_missing"],
    time_provenance: createTimeProvenance({
      platformEventTime: eventTime,
      receivedWallTime: receivedAt,
      localMonotonicMs,
      collectorClockOffsetMs
    }),
    user_snapshot: userSnapshot,
    field_mapping_status: "mapped",
    expected_live_room_id: liveRoomId
  };
  const events = [];
  const issues = [];
  const pushEvent = (input) => {
    const normalized = normalizeP1EvidenceEvent(input);
    if (normalized.ok) {
      events.push(normalized.event);
    } else {
      issues.push(...normalized.issues);
    }
  };
  if (mappedType) {
    pushEvent({
      ...common,
      event_type: mappedType,
      data: createTiktokEventData(mappedType, shape, provider)
    });
  }
  if (userSnapshot.visible_labels.length > 0 || userSnapshot.badge_summary.length > 0) {
    pushEvent({
      ...common,
      event_type: EVENT_TYPES.VIEWER_VISIBLE_LABELS_OBSERVED,
      confidence: P1_CONFIRMATION_LEVELS.PROVISIONAL,
      field_mapping_status: "provisional",
      data: {
        observed_labels: userSnapshot.visible_labels,
        badge_summary: userSnapshot.badge_summary
      }
    });
  }
  if (events.length === 0) {
    if (issues.length > 0) {
      return {
        ok: false,
        reason_code: issues[0].reason_code,
        issues
      };
    }
    return {
      ok: false,
      reason_code: "p1_event_not_supported",
      issues: []
    };
  }
  return {
    ok: true,
    events,
    raw_payload_hash: common.raw_payload_hash,
    decoder_version: decoderVersion
  };
}

export function replayRawP1EvidenceRecords({
  records = [],
  manifest = null,
  sourceDescriptor = createP1EvidenceSourceDescriptor(),
  sessionContext = {},
  normalizer = normalizeTiktokWebcastP1Event,
  generatedAt = nowIso()
} = {}) {
  const integrity = scanRawP1EvidenceIntegrity({ records, manifest, sessionContext });
  const normalizedEvents = [];
  const rejected = [];
  let mismatchCount = 0;
  for (const record of records) {
    const result = normalizer(record.raw_payload, {
      tenantId: record.tenant_id ?? sessionContext.tenant_id,
      sessionId: record.session_id ?? sessionContext.session_id,
      liveRoomId: record.live_room_id ?? sessionContext.live_room_id,
      sourceRole: record.source_role ?? sourceDescriptor.role,
      sourceId: record.source_id ?? sourceDescriptor.source_id,
      receivedAt: record.received_at,
      adapterVersion: record.adapter_version,
      decoderVersion: record.decoder_version,
      rawRef: createP1RawRef({ rawRecord: record })
    });
    if (result.ok) {
      normalizedEvents.push(...result.events);
      if (record.normalized_preview && !result.events.some((event) => event.event_type === record.normalized_preview.event_type)) {
        mismatchCount += 1;
      }
    } else {
      rejected.push({
        raw_record_id: record.raw_record_id,
        reason_code: result.reason_code,
        issues: result.issues ?? []
      });
    }
  }
  const duplicateKeys = normalizedEvents
    .map((event) => event.payload?.canonical_fact_key ?? event.idempotency_key)
    .filter(Boolean)
    .filter((key, index, keys) => keys.indexOf(key) !== index);
  const replayHash = hashRawP1Payload(normalizedEvents.map((event) => ({
    event_type: event.event_type,
    idempotency_key: event.idempotency_key,
    canonical_fact_key: event.payload?.canonical_fact_key ?? null,
    payload_hash: hashRawP1Payload(event.payload)
  })));
  return {
    ok: rejected.length === 0 && mismatchCount === 0 && duplicateKeys.length === 0 && integrity.ok,
    replay_id: createId("p1_replay"),
    generated_at: generatedAt,
    raw_count: records.length,
    normalized_count: normalizedEvents.length,
    rejected_count: rejected.length,
    duplicate_count: new Set(duplicateKeys).size,
    mismatch_count: mismatchCount,
    integrity_ok: integrity.ok,
    integrity_issues: integrity.issues,
    replay_hash: replayHash,
    events: normalizedEvents,
    rejected
  };
}

export function createP1EvidenceHealthReport({
  sessionContext = {},
  rawCoverageByRole = {},
  sourceHealth = {},
  replaySummary = null,
  requiredCategories = [],
  availableCategories = [],
  degradedWindows = [],
  timelineAlignmentScore = 1,
  generatedAt = nowIso()
} = {}) {
  const missingCategories = requiredCategories.filter((category) => !availableCategories.includes(category));
  const replayOk = replaySummary ? replaySummary.ok === true : false;
  const disconnectedRoles = Object.entries(sourceHealth)
    .filter(([, status]) => status?.connected === false)
    .map(([role]) => role);
  const healthScore = Math.max(0, Math.min(1,
    1
    - (missingCategories.length * 0.15)
    - (degradedWindows.length * 0.05)
    - (disconnectedRoles.length * 0.1)
    - (replayOk ? 0 : 0.2)
  ));
  let reportMode = P1_REPORT_MODES.FORMAL_ATTRIBUTION;
  if (missingCategories.length > 0 || !replayOk) {
    reportMode = missingCategories.includes("gift")
      ? P1_REPORT_MODES.HOLD_FOR_REPAIR
      : P1_REPORT_MODES.PARTIAL_REPORT;
  }
  if (healthScore < 0.5) {
    reportMode = P1_REPORT_MODES.FACTS_ONLY;
  }
  if (healthScore <= 0.2) {
    reportMode = P1_REPORT_MODES.BLOCKED_MISSING_EVIDENCE;
  }
  return {
    evidence_health_id: createId("p1_health"),
    generated_at: generatedAt,
    tenant_id: sessionContext.tenant_id ?? null,
    live_room_id: sessionContext.live_room_id ?? null,
    session_id: sessionContext.session_id ?? null,
    health_score: Number(healthScore.toFixed(3)),
    gate_result: reportMode,
    raw_backup_coverage_by_role: rawCoverageByRole,
    source_health: sourceHealth,
    replay_ok: replayOk,
    replay_summary: replaySummary ? {
      replay_id: replaySummary.replay_id,
      raw_count: replaySummary.raw_count,
      normalized_count: replaySummary.normalized_count,
      rejected_count: replaySummary.rejected_count,
      mismatch_count: replaySummary.mismatch_count,
      duplicate_count: replaySummary.duplicate_count,
      replay_hash: replaySummary.replay_hash
    } : null,
    missing_categories: missingCategories,
    degraded_windows: degradedWindows,
    timeline_alignment_score: Number(timelineAlignmentScore)
  };
}

export function evaluateP1CapturePressure({
  queueDepth = 0,
  writeLatencyP95Ms = 0,
  diskRemainingBytes = Number.POSITIVE_INFINITY,
  memoryAvailableBytes = Number.POSITIVE_INFINITY,
  droppedEventRatio = 0,
  primaryDisconnectedMs = 0,
  backupDisconnectedMs = 0,
  thresholds = {}
} = {}) {
  const limits = {
    queue_attention: thresholds.queue_attention ?? 5000,
    queue_degraded: thresholds.queue_degraded ?? 20000,
    write_attention_ms: thresholds.write_attention_ms ?? 250,
    write_degraded_ms: thresholds.write_degraded_ms ?? 1000,
    disk_attention_bytes: thresholds.disk_attention_bytes ?? 10 * 1024 ** 3,
    disk_degraded_bytes: thresholds.disk_degraded_bytes ?? 2 * 1024 ** 3,
    memory_attention_bytes: thresholds.memory_attention_bytes ?? 2 * 1024 ** 3,
    memory_degraded_bytes: thresholds.memory_degraded_bytes ?? 512 * 1024 ** 2,
    dropped_attention_ratio: thresholds.dropped_attention_ratio ?? 0.001,
    dropped_degraded_ratio: thresholds.dropped_degraded_ratio ?? 0.01,
    disconnected_attention_ms: thresholds.disconnected_attention_ms ?? 30_000,
    disconnected_degraded_ms: thresholds.disconnected_degraded_ms ?? 180_000
  };
  const reasons = [];
  const degraded = [];
  const attention = [];
  const add = (bucket, condition, reason) => {
    if (condition) {
      bucket.push(reason);
      reasons.push(reason);
    }
  };
  add(degraded, Number(queueDepth) >= limits.queue_degraded, "queue_depth_degraded");
  add(attention, Number(queueDepth) >= limits.queue_attention, "queue_depth_attention");
  add(degraded, Number(writeLatencyP95Ms) >= limits.write_degraded_ms, "write_latency_degraded");
  add(attention, Number(writeLatencyP95Ms) >= limits.write_attention_ms, "write_latency_attention");
  add(degraded, Number(diskRemainingBytes) <= limits.disk_degraded_bytes, "disk_space_degraded");
  add(attention, Number(diskRemainingBytes) <= limits.disk_attention_bytes, "disk_space_attention");
  add(degraded, Number(memoryAvailableBytes) <= limits.memory_degraded_bytes, "memory_degraded");
  add(attention, Number(memoryAvailableBytes) <= limits.memory_attention_bytes, "memory_attention");
  add(degraded, Number(droppedEventRatio) >= limits.dropped_degraded_ratio, "dropped_ratio_degraded");
  add(attention, Number(droppedEventRatio) >= limits.dropped_attention_ratio, "dropped_ratio_attention");
  add(degraded, Number(primaryDisconnectedMs) >= limits.disconnected_degraded_ms
    && Number(backupDisconnectedMs) >= limits.disconnected_degraded_ms, "all_sources_disconnected_degraded");
  add(attention, Number(primaryDisconnectedMs) >= limits.disconnected_attention_ms, "primary_source_disconnected_attention");

  const state = degraded.length
    ? P1_CAPTURE_PRESSURE_STATES.DEGRADED
    : attention.length
      ? P1_CAPTURE_PRESSURE_STATES.ATTENTION
      : P1_CAPTURE_PRESSURE_STATES.HEALTHY;
  return {
    ok: state === P1_CAPTURE_PRESSURE_STATES.HEALTHY,
    state,
    reason_codes: [...new Set(reasons)],
    recommended_actions: recommendedActionsForPressureState(state),
    metrics: {
      queue_depth: Number(queueDepth),
      write_latency_p95_ms: Number(writeLatencyP95Ms),
      disk_remaining_bytes: Number(diskRemainingBytes),
      memory_available_bytes: Number(memoryAvailableBytes),
      dropped_event_ratio: Number(droppedEventRatio),
      primary_disconnected_ms: Number(primaryDisconnectedMs),
      backup_disconnected_ms: Number(backupDisconnectedMs)
    }
  };
}

export function createObservedUserPathRollups(events = [], {
  highPotentialLevelThreshold = 30,
  generatedAt = nowIso()
} = {}) {
  const users = new Map();
  const sorted = [...events].sort((a, b) => toEpochMs(a.event_time ?? a.received_time ?? generatedAt) - toEpochMs(b.event_time ?? b.received_time ?? generatedAt));
  for (const event of sorted) {
    const userId = event.actor_user_id ?? event.payload?.user_snapshot?.platform_user_id ?? event.payload?.gifter_user_id;
    if (!userId) {
      continue;
    }
    const current = users.get(userId) ?? {
      platform_user_id: userId,
      display_name: event.payload?.user_snapshot?.display_name ?? event.payload?.gifter_display_name ?? null,
      first_observed_at: null,
      entry_observed_at: null,
      first_interaction_at: null,
      last_observed_interaction_at: null,
      last_observed_event_at: null,
      observed_event_count: 0,
      observed_chat_count: 0,
      observed_gift_value: 0,
      observed_follow_or_share_count: 0,
      user_consume_level: null,
      high_potential: false,
      observed_exit_at: null,
      stay_duration_seconds: null,
      stay_observation_status: "not_observed_by_source",
      no_later_interaction_after_entry: false,
      silent_observer_possible: false,
      evidence_notes: []
    };
    const at = event.event_time ?? event.received_time ?? generatedAt;
    current.first_observed_at ??= at;
    current.last_observed_event_at = at;
    current.display_name ??= event.payload?.user_snapshot?.display_name ?? event.payload?.gifter_display_name ?? null;
    current.observed_event_count += 1;
    const level = userConsumeLevelFromEvent(event);
    if (Number.isFinite(level)) {
      current.user_consume_level = Math.max(Number(current.user_consume_level ?? 0), level);
      current.high_potential = current.user_consume_level >= Number(highPotentialLevelThreshold);
    }
    if (event.event_type === EVENT_TYPES.VIEWER_MEMBER_JOINED) {
      current.entry_observed_at ??= at;
    }
    if (event.event_type === EVENT_TYPES.PUBLIC_CHAT_RECEIVED) {
      current.observed_chat_count += 1;
      current.first_interaction_at ??= at;
      current.last_observed_interaction_at = at;
    }
    if ([EVENT_TYPES.VIEWER_FOLLOWED, EVENT_TYPES.VIEWER_SHARED].includes(event.event_type)) {
      current.observed_follow_or_share_count += 1;
      current.first_interaction_at ??= at;
      current.last_observed_interaction_at = at;
    }
    if (event.event_type === EVENT_TYPES.GIFT_RECEIVED) {
      current.observed_gift_value += Number(event.payload?.gift_total_value ?? 0);
      current.first_interaction_at ??= at;
      current.last_observed_interaction_at = at;
    }
    users.set(userId, current);
  }
  return [...users.values()].map((user) => ({
    ...user,
    no_later_interaction_after_entry: Boolean(user.entry_observed_at && !user.last_observed_interaction_at),
    silent_observer_possible: Boolean(user.entry_observed_at && !user.first_interaction_at),
    evidence_notes: user.entry_observed_at && !user.first_interaction_at
      ? ["observed_entry_without_later_interaction"]
      : user.evidence_notes
  }));
}

export class MemoryP1EvidenceService {
  constructor({ maxBatchSize = 500, store = null } = {}) {
    this.maxBatchSize = maxBatchSize;
    this.store = store;
    const snapshot = store?.readSnapshot ? store.readSnapshot() : {};
    this.sessions = new Map((snapshot.sessions ?? []).map((session) => [session.session_id, session]));
    this.eventsBySession = new Map(Object.entries(snapshot.events_by_session ?? {}));
    this.eventKeysBySession = new Map([...this.eventsBySession.entries()].map(([sessionId, events]) => [
      sessionId,
      new Set(events.map((event) => event.payload?.canonical_fact_key ?? event.idempotency_key).filter(Boolean))
    ]));
    this.sourceHealthBySession = new Map(Object.entries(snapshot.source_health_by_session ?? {}).map(([sessionId, health]) => [
      sessionId,
      new Map(Object.entries(health))
    ]));
    this.replaysBySession = new Map(Object.entries(snapshot.replays_by_session ?? {}));
    this.manualCorrectionsBySession = new Map(Object.entries(snapshot.manual_corrections_by_session ?? {}));
  }

  registerSession(input = {}) {
    const tenantId = requireValue(input.tenant_id, "tenant_id");
    const liveRoomId = requireValue(input.live_room_id, "live_room_id");
    const sessionId = requireValue(input.session_id, "session_id");
    const existing = this.sessions.get(sessionId);
    if (existing) {
      if (String(existing.tenant_id) !== String(tenantId) || String(existing.live_room_id) !== String(liveRoomId)) {
        return {
          ok: false,
          reason_code: "p1_session_scope_mismatch",
          session: existing
        };
      }
      return {
        ok: true,
        reason_code: "p1_session_already_registered",
        session: existing
      };
    }
    const session = {
      p1_session_id: input.p1_session_id ?? createId("p1_session"),
      tenant_id: tenantId,
      live_room_id: liveRoomId,
      session_id: sessionId,
      source_ids: Array.isArray(input.source_ids) ? input.source_ids : [],
      schema_version: input.schema_version ?? "p1_event_v1",
      status: input.status ?? "collecting",
      created_at: input.created_at ?? nowIso(),
      updated_at: input.updated_at ?? input.created_at ?? nowIso()
    };
    this.sessions.set(session.session_id, session);
    this.eventsBySession.set(session.session_id, this.eventsBySession.get(session.session_id) ?? []);
    this.eventKeysBySession.set(session.session_id, this.eventKeysBySession.get(session.session_id) ?? new Set());
    this.#persist();
    return {
      ok: true,
      session
    };
  }

  ingestEvents({ tenant_id, live_room_id, session_id, events = [], receivedAt = nowIso() } = {}) {
    const session = this.#requireSession({ tenant_id, live_room_id, session_id });
    if (!Array.isArray(events) || events.length === 0) {
      return {
        ok: false,
        reason_code: "p1_events_required",
        accepted_count: 0,
        rejected_count: 0,
        rejected: []
      };
    }
    if (events.length > this.maxBatchSize) {
      return {
        ok: false,
        reason_code: "p1_batch_too_large",
        accepted_count: 0,
        rejected_count: events.length,
        rejected: events.map((event, index) => ({ index, reason_code: "p1_batch_too_large", event_type: event?.event_type ?? null }))
      };
    }
    const accepted = [];
    const rejected = [];
    const existingKeys = this.eventKeysBySession.get(session.session_id) ?? new Set();
    for (const [index, event] of events.entries()) {
      const normalized = normalizeP1EvidenceEvent({
        ...event,
        source_role: event.source_role ?? event.payload?.source_role,
        source_id: event.source_id ?? event.payload?.source_id,
        source_event_id: event.source_event_id ?? event.payload?.source_event_id,
        raw_ref: event.raw_ref ?? event.payload?.raw_ref,
        raw_payload_hash: event.raw_payload_hash ?? event.payload?.raw_payload_hash,
        confidence: event.confidence ?? event.payload?.confidence,
        field_mapping_status: event.field_mapping_status ?? event.payload?.field_mapping_status,
        time_provenance: event.time_provenance ?? event.payload?.time_provenance,
        user_snapshot: event.user_snapshot ?? event.payload?.user_snapshot,
        data: event.data ?? event.payload?.data,
        canonical_fact_key: event.canonical_fact_key ?? event.payload?.canonical_fact_key,
        tenant_id: event.tenant_id ?? tenant_id,
        live_room_id: event.live_room_id ?? live_room_id,
        session_id: event.session_id ?? session_id,
        expected_live_room_id: live_room_id,
        received_time: event.received_time ?? receivedAt
      });
      if (!normalized.ok) {
        rejected.push({ index, reason_code: normalized.issues[0]?.reason_code ?? "p1_event_invalid", issues: normalized.issues });
        continue;
      }
      const key = normalized.event.payload?.canonical_fact_key ?? normalized.event.idempotency_key;
      if (existingKeys.has(key)) {
        rejected.push({ index, reason_code: "p1_event_duplicate", event_type: normalized.event.event_type });
        continue;
      }
      existingKeys.add(key);
      accepted.push(normalized.event);
    }
    const sessionEvents = this.eventsBySession.get(session.session_id) ?? [];
    sessionEvents.push(...accepted);
    this.eventsBySession.set(session.session_id, sessionEvents);
    this.eventKeysBySession.set(session.session_id, existingKeys);
    this.#persist();
    return {
      ok: rejected.length === 0,
      session_id: session.session_id,
      accepted_count: accepted.length,
      rejected_count: rejected.length,
      accepted,
      rejected
    };
  }

  recordHeartbeat({ tenant_id, live_room_id, session_id, heartbeat = {}, observedAt = nowIso() } = {}) {
    const session = this.#requireSession({ tenant_id, live_room_id, session_id });
    const sourceId = heartbeat.source_id ?? heartbeat.sourceId ?? heartbeat.source_role ?? P1_EVIDENCE_SOURCE_ROLES.PRIMARY_REALTIME;
    const status = createP1EvidenceSourceDescriptor({
      ...heartbeat,
      source_id: sourceId,
      role: heartbeat.source_role ?? heartbeat.role ?? P1_EVIDENCE_SOURCE_ROLES.PRIMARY_REALTIME,
      connected: heartbeat.connected !== false,
      updated_at: observedAt
    });
    const map = this.sourceHealthBySession.get(session.session_id) ?? new Map();
    map.set(sourceId, status);
    this.sourceHealthBySession.set(session.session_id, map);
    this.#persist();
    return {
      ok: true,
      source_health: status
    };
  }

  requestReplay({ tenant_id, live_room_id, session_id, records = [], manifest = null, generatedAt = nowIso() } = {}) {
    const session = this.#requireSession({ tenant_id, live_room_id, session_id });
    const replay = replayRawP1EvidenceRecords({
      records,
      manifest,
      sessionContext: {
        tenant_id,
        live_room_id,
        session_id
      },
      generatedAt
    });
    const replays = this.replaysBySession.get(session.session_id) ?? [];
    replays.push(replay);
    this.replaysBySession.set(session.session_id, replays);
    this.#persist();
    return {
      ok: replay.ok,
      replay
    };
  }

  healthSummary({ tenant_id, live_room_id, session_id, requiredCategories = defaultP1RequiredCategories(), generatedAt = nowIso() } = {}) {
    const session = this.#requireSession({ tenant_id, live_room_id, session_id });
    const events = this.eventsBySession.get(session.session_id) ?? [];
    const replays = this.replaysBySession.get(session.session_id) ?? [];
    const sourceHealthMap = this.sourceHealthBySession.get(session.session_id) ?? new Map();
    const availableCategories = categoriesFromP1Events(events);
    if (sourceHealthMap.size > 0 && !availableCategories.includes("source_health")) {
      availableCategories.push("source_health");
    }
    const report = createP1EvidenceHealthReport({
      sessionContext: {
        tenant_id,
        live_room_id,
        session_id
      },
      sourceHealth: Object.fromEntries([...sourceHealthMap.entries()].map(([id, health]) => [id, health])),
      replaySummary: replays.at(-1) ?? null,
      requiredCategories,
      availableCategories,
      generatedAt
    });
    return {
      ok: true,
      evidence_health: report,
      event_count: events.length,
      user_path_count: createObservedUserPathRollups(events).length
    };
  }

  appendManualCorrection({ tenant_id, live_room_id, session_id, correction = {}, actor_id = null, at = nowIso() } = {}) {
    const session = this.#requireSession({ tenant_id, live_room_id, session_id });
    const normalized = normalizeP1EvidenceEvent({
      event_type: correction.event_type ?? EVENT_TYPES.SYSTEM_NOTICE_RECEIVED,
      tenant_id,
      live_room_id,
      session_id,
      source_role: P1_EVIDENCE_SOURCE_ROLES.MANUAL_REVIEW,
      source_id: correction.source_id ?? "manual_review",
      source_event_id: correction.correction_id ?? createId("p1_manual"),
      event_time: at,
      confidence: correction.confidence ?? P1_CONFIRMATION_LEVELS.PROVISIONAL,
      field_mapping_status: "manual",
      data: {
        correction_type: correction.correction_type ?? "manual_note",
        reason_code: correction.reason_code ?? "manual_review",
        note: correction.note ?? null,
        actor_id
      }
    });
    if (!normalized.ok) {
      return {
        ok: false,
        reason_code: normalized.issues[0]?.reason_code ?? "manual_correction_invalid",
        issues: normalized.issues
      };
    }
    const corrections = this.manualCorrectionsBySession.get(session.session_id) ?? [];
    corrections.push(normalized.event);
    this.manualCorrectionsBySession.set(session.session_id, corrections);
    this.#persist();
    return this.ingestEvents({
      tenant_id,
      live_room_id,
      session_id,
      events: [normalized.event],
      receivedAt: at
    });
  }

  #requireSession({ tenant_id, live_room_id, session_id }) {
    const session = this.sessions.get(requireValue(session_id, "session_id"));
    if (!session) {
      throw new Error("p1_evidence_session_not_found");
    }
    if (String(session.tenant_id) !== String(tenant_id) || String(session.live_room_id) !== String(live_room_id)) {
      throw new Error("p1_evidence_scope_mismatch");
    }
    return session;
  }

  #persist() {
    this.store?.writeSnapshot?.({
      schema_version: 1,
      updated_at: nowIso(),
      sessions: [...this.sessions.values()],
      events_by_session: Object.fromEntries(this.eventsBySession),
      source_health_by_session: Object.fromEntries([...this.sourceHealthBySession.entries()].map(([sessionId, health]) => [
        sessionId,
        Object.fromEntries(health)
      ])),
      replays_by_session: Object.fromEntries(this.replaysBySession),
      manual_corrections_by_session: Object.fromEntries(this.manualCorrectionsBySession)
    });
  }
}

export class JsonP1EvidenceServiceStore {
  constructor({ filePath } = {}) {
    if (!filePath) {
      throw new Error("P1 evidence service store filePath is required");
    }
    this.filePath = filePath;
    mkdirSync(dirname(filePath), { recursive: true });
    if (!existsSync(filePath)) {
      this.writeSnapshot({});
    }
  }

  readSnapshot() {
    if (!existsSync(this.filePath)) {
      return {};
    }
    return JSON.parse(readFileSync(this.filePath, "utf8"));
  }

  writeSnapshot(snapshot = {}) {
    writeFileSync(this.filePath, `${JSON.stringify({
      schema_version: 1,
      ...snapshot,
      updated_at: nowIso()
    }, null, 2)}\n`, "utf8");
  }
}

function createP1PayloadEnvelope({
  eventType,
  platform = "tiktok",
  sourceRole = P1_EVIDENCE_SOURCE_ROLES.PRIMARY_REALTIME,
  sourceId = null,
  sourceEventId = null,
  confidence = P1_CONFIRMATION_LEVELS.CONFIRMED,
  rawRef = null,
  rawPayloadHash = null,
  schemaVersion = "p1_event_v1",
  adapterVersion = "p1_adapter_v1",
  captureQualityFlags = [],
  timeProvenance = {},
  userSnapshot = null,
  fieldMappingStatus = null,
  data = {},
  syncPriorityHint = P1_SYNC_PRIORITY_HINTS.NORMAL,
  canonicalFactKey = null
} = {}) {
  return {
    event_type: eventType,
    platform,
    source_role: sourceRole,
    source_id: sourceId,
    source_event_id: sourceEventId,
    confidence,
    raw_ref: rawRef,
    raw_payload_hash: rawPayloadHash,
    schema_version: schemaVersion,
    adapter_version: adapterVersion,
    capture_quality_flags: captureQualityFlags,
    time_provenance: createTimeProvenance(timeProvenance),
    user_snapshot: userSnapshot,
    field_mapping_status: fieldMappingStatus,
    sync_priority_hint: syncPriorityHint,
    canonical_fact_key: canonicalFactKey,
    data
  };
}

export function createTimeProvenance({
  platformEventTime = null,
  receivedWallTime = nowIso(),
  localMonotonicMs = null,
  collectorClockOffsetMs = null,
  mediaTimebaseMs = null,
  timeConfidence = P1_CONFIRMATION_LEVELS.CONFIRMED,
  platform_event_time = null,
  received_wall_time = null,
  local_monotonic_ms = null,
  collector_clock_offset_ms = null,
  media_timebase_ms = null,
  time_confidence = null
} = {}) {
  const finalPlatformEventTime = platformEventTime ?? platform_event_time;
  const finalReceivedWallTime = receivedWallTime ?? received_wall_time;
  return {
    platform_event_time: finalPlatformEventTime ? normalizeEventTime(finalPlatformEventTime) : null,
    received_wall_time: finalReceivedWallTime,
    local_monotonic_ms: localMonotonicMs ?? local_monotonic_ms,
    collector_clock_offset_ms: collectorClockOffsetMs ?? collector_clock_offset_ms,
    media_timebase_ms: mediaTimebaseMs ?? media_timebase_ms,
    time_confidence: timeConfidence ?? time_confidence
  };
}

export function makeP1IdempotencyKey({
  tenantId,
  liveRoomId,
  sessionId,
  eventType,
  sourceEventId,
  platformUserId,
  eventTime,
  semanticPayload = {}
} = {}) {
  const timeBucket = Math.floor(toEpochMs(eventTime ?? nowIso()) / 1000);
  const stableEventId = sourceEventId ?? hashRawP1Payload({
    platformUserId,
    eventType,
    timeBucket,
    semanticPayload
  }).slice(0, 16);
  return [
    "p1",
    tenantId ?? "tenant",
    liveRoomId ?? "room",
    sessionId ?? "session",
    eventType ?? "event",
    stableEventId,
    platformUserId ?? "actor",
    timeBucket
  ].join(":");
}

export function makeP1CanonicalFactKey({
  tenantId,
  liveRoomId,
  sessionId,
  eventType,
  sourceEventId,
  platformUserId,
  eventTime,
  semanticPayload = {}
} = {}) {
  return makeP1IdempotencyKey({
    tenantId,
    liveRoomId,
    sessionId,
    eventType,
    sourceEventId,
    platformUserId,
    eventTime,
    semanticPayload
  });
}

export function createP1RawRef({ rawRecord = null, manifestId = null, sourceId = null, sequenceNo = null } = {}) {
  const recordId = rawRecord?.raw_record_id ?? manifestId ?? sourceId;
  const sequence = rawRecord?.sequence_no ?? sequenceNo;
  if (!recordId || !sequence) {
    return null;
  }
  return `p1raw:${String(recordId).replace(/[^a-zA-Z0-9_.:-]/g, "")}:${Number(sequence)}`;
}

export function withP1RawReference(event = {}, { rawRecord = null, rawRef = null, confidence = P1_CONFIRMATION_LEVELS.CONFIRMED } = {}) {
  const finalRawRef = normalizeP1RawRef(rawRef ?? createP1RawRef({ rawRecord }));
  if (!finalRawRef) {
    return event;
  }
  const payload = {
    ...(event.payload ?? {}),
    confidence,
    raw_ref: finalRawRef,
    capture_quality_flags: (event.payload?.capture_quality_flags ?? []).filter((flag) => flag !== "raw_backup_missing")
  };
  return {
    ...event,
    payload
  };
}

export function createP1CaptureStateEvent({
  tenantId,
  liveRoomId,
  sessionId,
  eventType = EVENT_TYPES.P1_CAPTURE_DEGRADED,
  sourceRole = P1_EVIDENCE_SOURCE_ROLES.PRIMARY_REALTIME,
  sourceId = null,
  reasonCode = "p1_capture_degraded",
  observedAt = nowIso(),
  metrics = {},
  data = {}
} = {}) {
  return normalizeP1EvidenceEvent({
    event_type: eventType,
    tenant_id: tenantId,
    live_room_id: liveRoomId,
    session_id: sessionId,
    source_role: sourceRole,
    source_id: sourceId,
    source_event_id: `${reasonCode}:${observedAt}`,
    event_time: observedAt,
    confidence: P1_CONFIRMATION_LEVELS.CONFIRMED,
    field_mapping_status: "system",
    data: {
      reason_code: reasonCode,
      metrics,
      ...data
    }
  }).event;
}

export function scanRawP1EvidenceIntegrity({ records = [], manifest = null, sessionContext = {} } = {}) {
  const issues = [];
  let previousSequence = null;
  const seenSequences = new Set();
  for (const [index, record] of records.entries()) {
    const sequence = Number(record.sequence_no);
    if (!Number.isInteger(sequence) || sequence <= 0) {
      issues.push(createIssue(`records[${index}].sequence_no`, "sequence_no_invalid"));
    }
    if (seenSequences.has(sequence)) {
      issues.push(createIssue(`records[${index}].sequence_no`, "sequence_no_duplicate"));
    }
    seenSequences.add(sequence);
    if (previousSequence !== null && sequence !== previousSequence + 1) {
      issues.push(createIssue(`records[${index}].sequence_no`, "sequence_gap_or_out_of_order"));
    }
    previousSequence = sequence;
    if (record.raw_payload_hash !== hashRawP1Payload(record.raw_payload)) {
      issues.push(createIssue(`records[${index}].raw_payload_hash`, "raw_payload_hash_mismatch"));
    }
    for (const field of ["tenant_id", "live_room_id", "session_id"]) {
      if (sessionContext[field] && record[field] && String(sessionContext[field]) !== String(record[field])) {
        issues.push(createIssue(`records[${index}].${field}`, "raw_record_scope_mismatch"));
      }
    }
  }
  if (manifest) {
    if (Number(manifest.record_count) !== records.length) {
      issues.push(createIssue("manifest.record_count", "manifest_record_count_mismatch"));
    }
    const expectedHash = hashRawP1Payload(records.map((record) => ({
      raw_record_id: record.raw_record_id,
      sequence_no: record.sequence_no,
      raw_payload_hash: record.raw_payload_hash
    })));
    if (manifest.file_hash && manifest.file_hash !== expectedHash) {
      issues.push(createIssue("manifest.file_hash", "manifest_file_hash_mismatch"));
    }
  }
  return {
    ok: issues.length === 0,
    issues
  };
}

function createNormalizedP1Preview(event = {}) {
  return {
    event_type: event.event_type,
    actor_user_id: event.actor_user_id ?? null,
    event_time: event.event_time ?? null,
    idempotency_key: event.idempotency_key ?? null,
    payload_hash: hashRawP1Payload(event.payload ?? {})
  };
}

function confidenceForP1Evidence({ rawRef = null, fieldMappingStatus = null } = {}) {
  if (!rawRef) {
    return P1_CONFIRMATION_LEVELS.PROVISIONAL;
  }
  if (["provisional", "unmapped"].includes(String(fieldMappingStatus ?? "").toLowerCase())) {
    return P1_CONFIRMATION_LEVELS.PROVISIONAL;
  }
  return P1_CONFIRMATION_LEVELS.CONFIRMED;
}

function validateP1EventData(eventType, data = {}) {
  const issues = [];
  if (eventType === EVENT_TYPES.PUBLIC_CHAT_RECEIVED && !stringOrNull(data.text)) {
    issues.push(createIssue("data.text", "chat_text_required"));
  }
  if (eventType === EVENT_TYPES.LIKE_BURST_RECORDED && !Number.isFinite(Number(data.count))) {
    issues.push(createIssue("data.count", "like_count_required"));
  }
  if (eventType === EVENT_TYPES.ROOM_METRIC_SAMPLED
    && !Number.isFinite(Number(data.viewer_count))
    && !Number.isFinite(Number(data.total_user_count))) {
    issues.push(createIssue("data.viewer_count", "room_metric_count_required"));
  }
  return issues;
}

function defaultP1RequiredCategories() {
  return ["public_chat", "member_join", "source_health"];
}

function categoriesFromP1Events(events = []) {
  const categories = new Set();
  for (const event of events) {
    if (event.event_type === EVENT_TYPES.PUBLIC_CHAT_RECEIVED) {
      categories.add("public_chat");
    }
    if (event.event_type === EVENT_TYPES.VIEWER_MEMBER_JOINED) {
      categories.add("member_join");
    }
    if (event.event_type === EVENT_TYPES.LIVE_SOURCE_HEALTH_REPORTED) {
      categories.add("source_health");
    }
    if (event.event_type === EVENT_TYPES.GIFT_RECEIVED) {
      categories.add("gift");
    }
  }
  return [...categories];
}

function userConsumeLevelFromEvent(event = {}) {
  const labels = event.payload?.user_snapshot?.visible_labels ?? event.payload?.data?.observed_labels ?? [];
  for (const label of labels) {
    if (label?.key === "user_spend_level" || label?.key === "user_consume_level") {
      const level = Number(label.value);
      if (Number.isFinite(level)) {
        return level;
      }
    }
  }
  return null;
}

function recommendedActionsForPressureState(state) {
  if (state === P1_CAPTURE_PRESSURE_STATES.DEGRADED) {
    return ["persist_raw_first", "aggregate_low_value_events", "pause_low_priority_cloud_sync", "mark_report_health_degraded"];
  }
  if (state === P1_CAPTURE_PRESSURE_STATES.ATTENTION) {
    return ["increase_batching", "watch_disk_and_queue", "keep_gift_path_priority"];
  }
  return ["normal_capture"];
}

function requireValue(value, field) {
  if (value === undefined || value === null || value === "") {
    throw new Error(`${field}_required`);
  }
  return value;
}

function normalizeP1RawRef(value) {
  if (!value) {
    return null;
  }
  if (typeof value === "object") {
    return createP1RawRef({
      rawRecord: value.raw_record ?? value,
      manifestId: value.manifest_id,
      sourceId: value.source_id,
      sequenceNo: value.sequence_no
    });
  }
  const text = String(value);
  if (isPathLikeValue(text)) {
    return null;
  }
  return /^p1raw:[a-zA-Z0-9_.:-]+:\d+$/u.test(text) ? text : null;
}

function isPathLikeValue(value) {
  const text = String(value ?? "");
  return /(^[a-zA-Z]:[\\/])|(^\/[^/])|(\\\\)|([\\/].+\.[a-zA-Z0-9]{2,6}(:\d+)?$)/u.test(text);
}

function mapTiktokP1EventType(shape = {}) {
  const type = normalizedMessageType(shape);
  if (["chat", "comment", "live_comment", "webcastchatmessage", "webcast_chat_message"].includes(type)) {
    return EVENT_TYPES.PUBLIC_CHAT_RECEIVED;
  }
  if (["member", "join", "viewer_joined", "live_member", "webcastmembermessage", "webcast_member_message"].includes(type)) {
    return EVENT_TYPES.VIEWER_MEMBER_JOINED;
  }
  if (type === "follow" || (type === "social" && socialAction(shape) === "follow")) {
    return EVENT_TYPES.VIEWER_FOLLOWED;
  }
  if (type === "share" || (type === "social" && socialAction(shape) === "share")) {
    return EVENT_TYPES.VIEWER_SHARED;
  }
  if (["like", "live_like", "webcastlikemessage", "webcast_like_message"].includes(type)) {
    return EVENT_TYPES.LIKE_BURST_RECORDED;
  }
  if (["roomuserseq", "room_user_seq", "room_user", "roomuser", "room_metric", "live_room_user_seq"].includes(type)) {
    return EVENT_TYPES.ROOM_METRIC_SAMPLED;
  }
  if (["fansclub", "fanclub", "fan_club", "live_fansclub"].includes(type)) {
    return EVENT_TYPES.FAN_CLUB_EVENT_RECORDED;
  }
  if (["roompin", "room_pin", "room_pin_updated"].includes(type)) {
    return EVENT_TYPES.ROOM_PIN_UPDATED;
  }
  if (["product", "product_pin", "promotion", "promotion_pin", "shopping"].includes(type)) {
    return EVENT_TYPES.PROMOTION_OR_PRODUCT_PINNED;
  }
  if (["battle", "pk", "linkmicbattle", "live_battle", "webcastlinkmicbattlemethod"].includes(type)) {
    return EVENT_TYPES.LIVE_BATTLE_STATE_CHANGED;
  }
  if (["system", "notice", "system_notice", "toast"].includes(type)) {
    return EVENT_TYPES.SYSTEM_NOTICE_RECEIVED;
  }
  if (["moderation", "mute", "block", "ban", "user_banned"].includes(type)) {
    return EVENT_TYPES.MODERATION_ACTION_RECORDED;
  }
  if (["role_snapshot", "host_role", "host_snapshot"].includes(type)) {
    return EVENT_TYPES.HOST_OR_ROLE_SNAPSHOT;
  }
  return null;
}

function createTiktokEventData(eventType, shape = {}, provider = "tiktok_webcast") {
  if (eventType === EVENT_TYPES.PUBLIC_CHAT_RECEIVED) {
    return {
      text: stringOrNull(firstDefined(shape.comment, shape.text, shape.content, shape.message, shape.msg, shape.comment_text)) ?? "",
      provider
    };
  }
  if (eventType === EVENT_TYPES.LIKE_BURST_RECORDED) {
    return {
      count: numberOrNull(firstDefined(shape.like_count, shape.likeCount, shape.count, shape.total, shape.total_like_count)),
      aggregation_window_ms: numberOrNull(firstDefined(shape.aggregation_window_ms, shape.window_ms)) ?? 1000,
      provider
    };
  }
  if (eventType === EVENT_TYPES.ROOM_METRIC_SAMPLED) {
    return {
      viewer_count: numberOrNull(firstDefined(shape.viewer_count, shape.user_count, shape.total_user, shape.totalUser, shape.online_count, shape.onlineCount)),
      total_user_count: numberOrNull(firstDefined(shape.total, shape.total_user, shape.totalUser)),
      top_users: firstDefined(shape.top_users, shape.topUsers, shape.ranks, []),
      provider
    };
  }
  if (eventType === EVENT_TYPES.ROOM_PIN_UPDATED || eventType === EVENT_TYPES.PROMOTION_OR_PRODUCT_PINNED) {
    return {
      pin_type: stringOrNull(firstDefined(shape.pin_type, shape.pinType, shape.type)),
      text: stringOrNull(firstDefined(shape.text, shape.title, shape.name, shape.content)),
      product_id: stringOrNull(firstDefined(shape.product_id, shape.productId, shape.item_id, shape.itemId)),
      provider
    };
  }
  if (eventType === EVENT_TYPES.LIVE_BATTLE_STATE_CHANGED) {
    return {
      battle_state: stringOrNull(firstDefined(shape.battle_state, shape.status, shape.state, shape.event)),
      score: firstDefined(shape.score, shape.scores, null),
      round: stringOrNull(firstDefined(shape.round, shape.round_id, shape.roundId)),
      provider
    };
  }
  if (eventType === EVENT_TYPES.SYSTEM_NOTICE_RECEIVED) {
    return {
      notice_type: stringOrNull(firstDefined(shape.notice_type, shape.noticeType, shape.type, shape.method)),
      text: stringOrNull(firstDefined(shape.text, shape.content, shape.message, shape.msg)),
      provider
    };
  }
  if (eventType === EVENT_TYPES.MODERATION_ACTION_RECORDED) {
    return {
      action_type: stringOrNull(firstDefined(shape.action_type, shape.action, shape.type, shape.method)),
      target_user_id: stringOrNull(firstDefined(shape.target_user_id, shape.targetUserId, shape.to_user_id)),
      reason: stringOrNull(firstDefined(shape.reason, shape.reason_code, shape.reasonCode)),
      provider
    };
  }
  if (eventType === EVENT_TYPES.FAN_CLUB_EVENT_RECORDED) {
    return {
      fan_club_event_type: stringOrNull(firstDefined(shape.fan_club_event_type, shape.event_type, shape.type, shape.method)),
      level: numberOrNull(firstDefined(shape.level, shape.fan_club_level, shape.fansclub_level)),
      provider
    };
  }
  if (eventType === EVENT_TYPES.HOST_OR_ROLE_SNAPSHOT) {
    return {
      role_snapshot: firstDefined(shape.roles, shape.role_snapshot, shape.hosts, shape.performers, {}),
      provider
    };
  }
  return {
    action_type: stringOrNull(firstDefined(shape.action_type, shape.action, shape.type, shape.method)),
    provider
  };
}

function createTiktokUserSnapshot(shape = {}, observedAt = nowIso()) {
  const user = firstObject(shape.user, shape.from_user, shape.sender, shape.member, shape.viewer, shape.operator) ?? {};
  const labels = extractVisibleLabels(shape, user);
  const badges = extractBadges(shape, user);
  return {
    platform_user_id: firstString(
      shape.user_id,
      shape.userId,
      shape.user_id_str,
      user.user_id,
      user.userId,
      user.id,
      user.sec_uid,
      user.secUid,
      user.unique_id,
      user.uniqueId
    ),
    platform_public_handle: firstString(shape.unique_id, shape.uniqueId, user.unique_id, user.uniqueId),
    display_name: firstString(shape.nickname, shape.nickName, user.nickname, user.nickName, user.display_name, user.displayName) ?? "Guest",
    display_name_observed_at: observedAt,
    id_kind: firstString(shape.user_id, shape.userId, user.user_id, user.userId, user.id) ? "platform_user_id" : "display_or_handle",
    identity_confidence: firstString(shape.user_id, shape.userId, user.user_id, user.userId, user.id) ? "high" : "medium",
    avatar_hash: avatarHashFromUser(user),
    visible_labels: labels,
    badge_summary: badges
  };
}

function extractVisibleLabels(shape = {}, user = {}) {
  const labels = [];
  for (const [key, value] of Object.entries({
    user_spend_level: firstDefined(shape.user_spend_level, shape.userSpendLevel, user.user_spend_level, user.userSpendLevel),
    fan_club_level: firstDefined(shape.fan_club_level, shape.fanClubLevel, shape.fansclub_level, user.fan_club_level, user.fanClubLevel),
    display_id: firstDefined(shape.display_id, user.display_id),
    badge_level: firstDefined(shape.badge_level, user.badge_level)
  })) {
    if (value !== undefined && value !== null && value !== "") {
      labels.push({
        key,
        value,
        mapping_status: "provisional"
      });
    }
  }
  return labels;
}

function extractBadges(shape = {}, user = {}) {
  const rawBadges = firstDefined(shape.badges, user.badges, user.userBadges, shape.user_badges, []);
  if (!Array.isArray(rawBadges)) {
    return [];
  }
  return rawBadges.map((badge) => ({
    type: stringOrNull(firstDefined(badge.type, badge.badge_type, badge.badgeType, badge.name)),
    name: stringOrNull(firstDefined(badge.name, badge.display_name, badge.displayName, badge.title)),
    level: numberOrNull(firstDefined(
      badge.level,
      badge.badge_level,
      badge.badgeLevel,
      badge.privilegeLogExtra?.level,
      badge.attributes?.privilegeLogExtra?.level
    )),
    mapping_status: "provisional"
  }));
}

function sourceEventIdFromShape(shape = {}) {
  return firstString(
    shape.source_event_id,
    shape.msg_id,
    shape.msgId,
    shape.message_id,
    shape.messageId,
    shape.id,
    shape.event_id,
    shape.eventId
  );
}

function normalizedMessageType(shape = {}) {
  return String(firstDefined(
    shape.type,
    shape.event_type,
    shape.event,
    shape.method,
    shape.message_type,
    shape.messageType,
    shape.msg_type,
    shape.msgType,
    ""
  )).toLowerCase().replace(/[^a-z0-9_]/g, "");
}

function socialAction(shape = {}) {
  return String(firstDefined(shape.action, shape.social_action, shape.socialAction, shape.event, "")).toLowerCase();
}

function isTiktokGiftLike(shape = {}) {
  const type = normalizedMessageType(shape);
  return ["gift", "gift_event", "webcastgiftmessage", "webcast_gift_message"].includes(type)
    || Boolean(shape.gift || shape.giftId || shape.gift_id || shape.giftName || shape.gift_name);
}

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 extractTiktokShape(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 extractTiktokShape(parsed.payload);
  }
  if (parsed.data && typeof parsed.data === "object" && !Array.isArray(parsed.data)) {
    return extractTiktokShape(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 sourceForRole(role) {
  if (role === P1_EVIDENCE_SOURCE_ROLES.BACKUP_REALTIME) {
    return EVENT_SOURCES.REALTIME_BACKUP;
  }
  if (role === P1_EVIDENCE_SOURCE_ROLES.LIVE_TOOL_GUARDIAN || role === P1_EVIDENCE_SOURCE_ROLES.RAW_RECORDING_GUARDIAN) {
    return EVENT_SOURCES.LIVE_TOOL_GUARDIAN;
  }
  if (role === P1_EVIDENCE_SOURCE_ROLES.MANUAL_REVIEW) {
    return EVENT_SOURCES.OPERATOR;
  }
  if (role === P1_EVIDENCE_SOURCE_ROLES.AFTER_LIVE_REPAIR) {
    return EVENT_SOURCES.OFFICIAL_BACKEND;
  }
  return EVENT_SOURCES.REALTIME_PRIMARY;
}

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 countNdjsonRecords(filePath) {
  return readRawP1EvidenceRecordsFromFile(filePath, { tolerateBadLines: true }).records.length;
}

export function readRawP1EvidenceRecordsFromFile(filePath, { tolerateBadLines = false, startSequenceNo = null, limit = null } = {}) {
  const records = [];
  const rejected = [];
  const lines = readFileSync(filePath, "utf8").split("\n");
  for (const [index, line] of lines.entries()) {
    const trimmed = line.trim();
    if (!trimmed) {
      continue;
    }
    try {
      const record = JSON.parse(trimmed);
      if (startSequenceNo && Number(record.sequence_no) < Number(startSequenceNo)) {
        continue;
      }
      records.push(record);
      if (limit && records.length >= Number(limit)) {
        break;
      }
    } catch (error) {
      const rejectedRecord = {
        line_no: index + 1,
        reason_code: "raw_record_json_invalid",
        message: error?.message ?? "invalid JSON"
      };
      if (!tolerateBadLines) {
        throw new Error(`raw P1 evidence line ${rejectedRecord.line_no} is invalid JSON`);
      }
      rejected.push(rejectedRecord);
    }
  }
  return {
    records,
    rejected
  };
}

export function createLocalP1FileIdentity(filePath) {
  if (!filePath || !existsSync(filePath)) {
    return {
      exists: false,
      byte_size: 0,
      modified_ms: null,
      head_hash: null
    };
  }
  const stat = statSync(filePath);
  const content = readFileSync(filePath, "utf8");
  const firstLine = content.split("\n").find((line) => line.trim()) ?? "";
  return {
    exists: true,
    byte_size: stat.size,
    modified_ms: stat.mtimeMs,
    head_hash: hashRawP1Payload(firstLine)
  };
}

export function createLocalP1EvidenceCursor({ filePath, lastSequenceNo = 0, fileIdentity = null, updatedAt = nowIso() } = {}) {
  return {
    cursor_id: createId("p1_cursor"),
    file_path: filePath,
    last_sequence_no: Number(lastSequenceNo),
    file_identity: fileIdentity ?? createLocalP1FileIdentity(filePath),
    updated_at: updatedAt
  };
}

export function readIncrementalLocalP1EvidenceInbox({ filePath, cursor = null, limit = 1000 } = {}) {
  const currentIdentity = createLocalP1FileIdentity(filePath);
  const previousIdentity = cursor?.file_identity ?? null;
  if (!currentIdentity.exists) {
    return {
      ok: false,
      state: P1_LOCAL_INBOX_STATES.NEEDS_REVIEW,
      reset_detected: Boolean(previousIdentity?.exists),
      records: [],
      rejected: [{
        sequence_no: Number(cursor?.last_sequence_no ?? 0) + 1,
        reason_code: "p1_inbox_missing"
      }],
      next_cursor: createLocalP1EvidenceCursor({
        filePath,
        lastSequenceNo: Number(cursor?.last_sequence_no ?? 0),
        fileIdentity: currentIdentity
      })
    };
  }
  const resetDetected = Boolean(previousIdentity?.exists && (
    Number(currentIdentity.byte_size) < Number(previousIdentity.byte_size)
    || (previousIdentity.head_hash && currentIdentity.head_hash && previousIdentity.head_hash !== currentIdentity.head_hash)
  ));
  const startSequenceNo = resetDetected ? 1 : Number(cursor?.last_sequence_no ?? 0) + 1;
  const parsed = readRawP1EvidenceRecordsFromFile(filePath, {
    tolerateBadLines: true,
    startSequenceNo,
    limit
  });
  const lastSequenceNo = parsed.rejected.length > 0
    ? Number(cursor?.last_sequence_no ?? 0)
    : parsed.records.at(-1)?.sequence_no ?? (resetDetected ? 0 : Number(cursor?.last_sequence_no ?? 0));
  return {
    ok: parsed.rejected.length === 0 && !resetDetected,
    state: resetDetected || parsed.rejected.length
      ? P1_LOCAL_INBOX_STATES.NEEDS_REVIEW
      : P1_LOCAL_INBOX_STATES.READY,
    reset_detected: resetDetected,
    records: parsed.records,
    rejected: parsed.rejected,
    next_cursor: createLocalP1EvidenceCursor({
      filePath,
      lastSequenceNo,
      fileIdentity: currentIdentity
    })
  };
}

function parseJsonMaybe(value) {
  try {
    return {
      value: JSON.parse(value),
      error: null
    };
  } catch (error) {
    return {
      value: null,
      error
    };
  }
}

function normalizeEventTime(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 timestampToIso(value) {
  if (!Number.isFinite(value)) {
    return null;
  }
  const millis = value > 10_000_000_000 ? value : value * 1000;
  const date = new Date(millis);
  return Number.isNaN(date.getTime()) ? null : date.toISOString();
}

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

function firstString(...values) {
  for (const value of values) {
    const text = stringOrNull(value);
    if (text) {
      return text;
    }
  }
  return null;
}

function firstObject(...values) {
  return values.find((value) => value && typeof value === "object" && !Array.isArray(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 avatarHashFromUser(user = {}) {
  const avatar = firstString(user.avatar_thumb?.url_list?.[0], user.avatarThumb?.urlList?.[0], user.avatar_url, user.avatarUrl);
  return avatar ? hashRawP1Payload(avatar).slice(0, 16) : null;
}

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