// Типы для обмена сообщениями между компонентами

export type MessageType =
  | 'REQUEST_PERMISSIONS'
  | 'PERMISSIONS_GRANTED'
  | 'PERMISSIONS_DENIED'
  | 'GET_VIDEO_BUFFER'
  | 'VIDEO_BUFFER_RESPONSE'
  | 'GET_RRWEB_EVENTS'
  | 'RRWEB_EVENTS_RESPONSE'
  | 'SAVE_ARCHIVE'
  | 'ARCHIVE_SAVED'
  | 'START_RECORDING'
  | 'STOP_RECORDING'
  | 'RECORDING_ERROR'
  | 'OFFSCREEN_READY';

// IN-05 fix: Message<T = unknown> instead of `T = any`. Call sites that
// access `(msg as Message).data` MUST narrow before use — `unknown`
// forces that discipline, which `any` silently bypassed (every
// downstream access was implicitly `any` and skipped type checking).
// The Message inbox in src/background/index.ts and src/offscreen/recorder.ts
// already destructure via `msg.type` switch and never read `.data`
// directly, so the migration was safe (no widening needed).
export interface Message<T = unknown> {
  type: MessageType;
  data?: T;
  tabId?: number;
}

// Типы сообщений в long-lived port (offscreen ↔ SW; D-17 / Plan 04 / D-13)
//
// Option C (debug session empty-archive-port-race) extends the surface:
//   - 'PONG' completes the health-probe loop: offscreen pings, SW echoes,
//     offscreen tracks last-pong time. Replaces the 290 s pre-emptive
//     `setTimeout` reconnect (whose race window weaponised the upstream
//     silent-skip in createArchive — see the bisect notes in the debug
//     session for full lineage).
//   - REQUEST_BUFFER + BUFFER carry an architectural `requestId` so the
//     SW can match a response to the in-flight request that issued it.
//     This retires the silent-cross-talk failure mode where a stale
//     BUFFER from a prior request would route into a newer Promise.
//
// D-P2-01 Blob URL migration triplet (P0-6 fix, Phase 02 Plan 02-02):
//   - CREATE_DOWNLOAD_URL + DOWNLOAD_URL + REVOKE_DOWNLOAD_URL.
//     SW posts CREATE_DOWNLOAD_URL with the archive bytes as base64;
//     offscreen mints URL.createObjectURL and responds with DOWNLOAD_URL;
//     SW calls chrome.downloads.download(url); when chrome.downloads
//     .onChanged reports 'complete' or 'interrupted', SW posts
//     REVOKE_DOWNLOAD_URL so offscreen can free the URL. The base64
//     wire-format reuses the D-12 precedent from src/shared/binary.ts —
//     chrome.runtime.Port JSON-serializes payloads, so Blob → empty
//     object; base64 round-trips cleanly. See
//     .planning/phases/02-stabilize-export-pipeline/02-CONTEXT.md
//     D-P2-01 for the full architectural rationale.
export type PortMessageType =
  | 'PING'
  | 'PONG'
  | 'REQUEST_BUFFER'
  | 'BUFFER'
  | 'CREATE_DOWNLOAD_URL'    // SW → offscreen: "here is a Blob as base64; mint a URL for it"
  | 'DOWNLOAD_URL'           // offscreen → SW: "here is the minted blob:URL"
  | 'REVOKE_DOWNLOAD_URL';   // SW → offscreen: "you can revoke this URL now"

export interface PortMessage {
  type: PortMessageType;
  // Per-request correlation id (Option C). The SW generates a uuid for
  // each REQUEST_BUFFER call and only resolves on BUFFER responses that
  // echo the same id. PING/PONG do not carry a requestId — they are
  // pure liveness signals.
  // Also used by the D-P2-01 triplet: CREATE_DOWNLOAD_URL → DOWNLOAD_URL
  // pair carries a per-mint requestId so concurrent mints (theoretically
  // possible across two SAVE flows) cannot cross-talk. REVOKE_DOWNLOAD_URL
  // is fire-and-forget — no requestId required.
  requestId?: string;
  // Wire-format (D-12 base64 transfer + D-13 segment lifecycle):
  // segments travel as TransferredVideoSegment[] because
  // chrome.runtime.Port JSON-serializes payloads across extension
  // contexts and JSON.stringify(blob) === "{}" loses binary content.
  // Each entry is one self-contained ~10 s WebM segment (EBML header
  // + seed keyframe). The receive side reconstructs VideoSegment[]
  // via src/shared/binary.ts.
  segments?: TransferredVideoSegment[];
  // D-P2-01 (P0-6 fix): archive bytes as base64 + MIME type for Blob
  // reconstruction in the offscreen document on CREATE_DOWNLOAD_URL.
  // Reuses src/shared/binary.ts blobToBase64/base64ToBlob helpers; same
  // wire-format precedent as the BUFFER segment transfer.
  dataBase64?: string;
  mimeType?: string;
  // D-P2-01: the minted blob:URL string on DOWNLOAD_URL (offscreen → SW)
  // OR the URL to free on REVOKE_DOWNLOAD_URL (SW → offscreen). The
  // string is a `blob:chrome-extension://<id>/<uuid>` URL minted by
  // URL.createObjectURL in the offscreen document origin.
  url?: string;
}

// In-memory segment shape used by mergeVideoSegments after the SW
// decodes the wire format. Под D-13 каждый сегмент — самодостаточный
// WebM-блок ≈ 10 секунд (свой EBML-заголовок и стартовый keyframe).
export interface VideoSegment {
  data: Blob;
  timestamp: number;
}

// Wire-format for video segments traveling across the offscreen↔SW
// chrome.runtime.Port boundary. Replaces the previous Blob payload,
// which failed because Blob is not JSON-serializable.
// See debug session d12-blob-port-transfer-fails (resolved) and the
// GREEN block of tests/offscreen/port-serialization.test.ts for the
// pinned shape. The header-pin flag from the D-09..D-11 era is gone
// — under D-13 every segment IS implicitly its own header.
export interface TransferredVideoSegment {
  data: string;       // base64-encoded blob bytes (no data: prefix)
  type: string;       // MIME type to apply when reconstructing the Blob
  timestamp: number;
}

// Лог событий пользователя
// IN-05-companion: `meta` uses `unknown` values instead of `any`. The
// field carries free-form diagnostic data (HTTP status, stack frames,
// XHR method, etc.); none of the SW consumers READ from meta — it
// only flows from content script → archive JSON. `unknown` documents
// "treat me opaquely" honestly; `any` silently allowed any access.
export interface UserEvent {
  timestamp: number;
  type: 'click' | 'input' | 'navigation' | 'js_error' | 'network_error';
  target: string;
  value?: string;
  url: string;
  meta?: Record<string, unknown>;
}

// Метаданные сессии.
//
// Phase 2 Plan 02-03 — D-P2-02 + D-P2-03 schema-breaking amendment
// (2026-05-20; closes audit P1 #10):
//
//   - `url: string` REPLACED by `urls: string[]`. Captures the operator's
//     multi-tab context during the rolling 30 s recording window — not
//     just the active-at-save tab. Fed by the new
//     `src/background/tab-url-tracker.ts` module via chrome.tabs.onActivated
//     + chrome.tabs.onUpdated listeners + a SAVE-time
//     chrome.tabs.query({}) snapshot (DEC-011 Amendment 1 grants the
//     `tabs` permission). Empty array IS permitted (F2 — whole-desktop-
//     no-tab session).
//
//   - `schemaVersion: string` ADDED as the 8th field. Value '2' marks the
//     D-P2-02 url→urls cutover; future schema bumps increment. The 8th
//     field name was planner-suggested in Plan 02-01 Task 3 and ratified
//     here as the lockstep for tests/build/strict-meta-json-validation.test.ts
//     EXPECTED_KEYS.
//
// Total field count: 8 (per D-P2-03 strict exact-shape rule).
//
// Field-emission order follows source-declaration order: TypeScript object
// literals preserve insertion order, and JSON.stringify emits in insertion
// order per ECMA-262 §9.1.11.1 — so the meta.json output mirrors this
// interface line-by-line.
export interface SessionMetadata {
  schemaVersion: string;
  timestamp: string;
  urls: string[];
  userAgent: string;
  extensionVersion: string;
  videoBufferSeconds: number;
  logDurationMinutes: number;
  totalEvents: number;
}

// Сообщения для popup
export interface PopupState {
  isRecording: boolean;
  hasPermissions: boolean;
  status: 'idle' | 'saving' | 'done';
}

// Ответы от Service Worker
export interface VideoBufferResponse {
  segments: VideoSegment[];
}

// IN-05-companion: `events: unknown[]` instead of `any[]`. The events
// array carries rrweb's `EventWithTime` shape which is too large to
// import here without pulling rrweb into the shared module; the
// consumers (background/index.ts) already type it as `unknown[]` and
// pass through to JSZip. `any[]` would have silently bypassed all
// downstream type-checking.
export interface RrwebEventsResponse {
  events: unknown[];
}
