/**
 * Diagnostics types — remote debug session management.
 *
 * Cosmos containers:
 * - debug_sessions (pk: /id, TTL: 7 days)
 * - debug_traces (pk: /pk composite ${productId}:${sessionId}, TTL: 7 days)
 * - debug_logs (pk: /pk composite ${productId}:${sessionId}, TTL: 3 days)
 * - debug_screenshots (pk: /sessionId) — metadata only, images in Azure Blob
 *
 * @module diagnostics
 */

import { z } from 'zod';

import { randomUUID } from 'node:crypto';

/**
 * Generate a prefixed ID for diagnostics documents.
 * Exported for use in routes and tests.
 */
export function generateId(prefix: string): string {
  return `${prefix}_${randomUUID().replace(/-/g, '')}`;
}

/**
 * Build composite partition key for traces/logs.
 * Exported for use in routes.
 */
export function buildPk(productId: string, sessionId: string): string {
  return `${productId}:${sessionId}`;
}

// ─────────────────────────────────────────────────────────────────────────────
// Enums
// ─────────────────────────────────────────────────────────────────────────────

export const SessionStatusEnum = z.enum(['pending', 'active', 'paused', 'completed', 'cancelled']);
export type SessionStatus = z.infer<typeof SessionStatusEnum>;

export const CollectionLevelEnum = z.enum(['standard', 'debug', 'trace']);
export type CollectionLevel = z.infer<typeof CollectionLevelEnum>;

export const LogLevelEnum = z.enum(['debug', 'info', 'warn', 'error', 'fatal']);
export type LogLevel = z.infer<typeof LogLevelEnum>;

export const ScreenshotTriggerEnum = z.enum(['manual', 'error', 'interval', 'user_request']);
export type ScreenshotTrigger = z.infer<typeof ScreenshotTriggerEnum>;

export const SpanStatusEnum = z.enum(['ok', 'error', 'unset']);
export type SpanStatus = z.infer<typeof SpanStatusEnum>;

export const SpanKindEnum = z.enum(['internal', 'server', 'client', 'producer', 'consumer']);
export type SpanKind = z.infer<typeof SpanKindEnum>;

// ─────────────────────────────────────────────────────────────────────────────
// DebugSessionDoc
// ─────────────────────────────────────────────────────────────────────────────

export interface DebugSessionDoc {
  id: string;
  productId: string;

  // Target (at least one required)
  targetUserId?: string;
  targetAnonymousId?: string;
  targetDeviceId?: string;
  targetSessionId?: string;

  // Status
  status: SessionStatus;

  // Collection config
  collectionLevel: CollectionLevel;
  captureLogs: boolean;
  captureNetwork: boolean;
  captureScreenshots: boolean;
  screenshotOnError: boolean;
  maxDurationMinutes: number;

  // Timestamps
  createdAt: string;
  updatedAt: string;
  startedAt?: string;
  endedAt?: string;
  expiresAt: string;

  // Stats (denormalized)
  logCount: number;
  traceCount: number;
  screenshotCount: number;

  // Audit
  createdBy: string;
  updatedBy?: string;

  // Consent tracking
  userConsent?: {
    consentedAt: string;
    consentMethod: 'prompt' | 'pre_consent' | 'auto';
  };
}

// ─────────────────────────────────────────────────────────────────────────────
// DebugTraceDoc (OpenTelemetry-compatible)
// ─────────────────────────────────────────────────────────────────────────────

export interface DebugTraceDoc {
  id: string;
  pk: string; // Composite: ${productId}:${sessionId}
  sessionId: string;
  productId: string;

  // OTel context
  traceId: string;
  parentId?: string;
  spanId: string;
  name: string;
  kind?: SpanKind;

  // Timing
  startTime: string;
  endTime?: string;
  durationMs?: number;

  // Context
  attributes: Record<string, unknown>;
  status: SpanStatus;
  statusMessage?: string;

  // Events within span
  events?: Array<{
    name: string;
    timestamp: string;
    attributes?: Record<string, unknown>;
  }>;

  // Links to other traces
  links?: Array<{
    traceId: string;
    spanId: string;
    attributes?: Record<string, unknown>;
  }>;
}

// ─────────────────────────────────────────────────────────────────────────────
// DebugLogEntryDoc
// ─────────────────────────────────────────────────────────────────────────────

export interface DebugLogEntryDoc {
  id: string;
  pk: string; // Composite: ${productId}:${sessionId}
  sessionId: string;
  productId: string;

  level: LogLevel;
  message: string;
  messageHash?: string;

  // Timestamps
  timestamp: string;
  receivedAt?: string;

  // Source context
  module: string;
  file?: string;
  line?: number;
  function?: string;

  // Thread/task context
  threadId?: string;
  correlationId?: string;

  // Context (PII-scanned)
  context: Record<string, unknown>;

  // PII redaction metadata
  redaction?: {
    fieldsRedacted: string[];
    patternsMatched: string[];
  };
}

// ─────────────────────────────────────────────────────────────────────────────
// DebugScreenshotDoc (metadata only)
// ─────────────────────────────────────────────────────────────────────────────

export interface DebugScreenshotDoc {
  id: string;
  sessionId: string;
  productId: string;

  // Blob storage reference
  blobUrl: string;
  blobPath: string;
  containerName: string;

  // Metadata
  capturedAt: string;
  trigger: ScreenshotTrigger;

  // Dimensions
  width: number;
  height: number;
  format: 'png' | 'jpeg' | 'webp';
  sizeBytes: number;

  // Privacy
  sensitiveViewsBlurred: boolean;
  blurRegions?: Array<{ x: number; y: number; w: number; h: number }>;

  // Context
  screenName?: string;
  breadcrumbAtCapture?: string;
}

// ─────────────────────────────────────────────────────────────────────────────
// Input Schemas
// ─────────────────────────────────────────────────────────────────────────────

export const CreateDebugSessionSchema = z.object({
  productId: z.string().min(1),
  targetUserId: z.string().optional(),
  targetAnonymousId: z.string().optional(),
  targetDeviceId: z.string().optional(),
  targetSessionId: z.string().optional(),
  collectionLevel: CollectionLevelEnum.default('debug'),
  captureLogs: z.boolean().default(true),
  captureNetwork: z.boolean().default(true),
  captureScreenshots: z.boolean().default(false),
  screenshotOnError: z.boolean().default(true),
  maxDurationMinutes: z.number().min(5).max(1440).default(60),
});

export const UpdateDebugSessionSchema = z.object({
  status: SessionStatusEnum.optional(),
  collectionLevel: CollectionLevelEnum.optional(),
  captureLogs: z.boolean().optional(),
  captureNetwork: z.boolean().optional(),
  captureScreenshots: z.boolean().optional(),
  screenshotOnError: z.boolean().optional(),
  maxDurationMinutes: z.number().min(5).max(1440).optional(),
});

export const ListDebugSessionsQuerySchema = z.object({
  productId: z.string().optional(),
  status: SessionStatusEnum.optional(),
  targetUserId: z.string().optional(),
  from: z.string().datetime().optional(),
  to: z.string().datetime().optional(),
  limit: z.coerce.number().min(1).max(100).default(20),
  offset: z.coerce.number().min(0).default(0),
});

export const IngestTracesSchema = z.object({
  sessionId: z.string().min(1),
  traces: z
    .array(
      z.object({
        traceId: z.string().min(1),
        spanId: z.string().min(1),
        parentId: z.string().optional(),
        name: z.string().min(1),
        kind: SpanKindEnum.optional(),
        startTime: z.string().datetime(),
        endTime: z.string().datetime().optional(),
        durationMs: z.number().optional(),
        attributes: z.record(z.unknown()).default({}),
        status: SpanStatusEnum,
        statusMessage: z.string().optional(),
        events: z
          .array(
            z.object({
              name: z.string(),
              timestamp: z.string().datetime(),
              attributes: z.record(z.unknown()).optional(),
            })
          )
          .optional(),
        links: z
          .array(
            z.object({
              traceId: z.string(),
              spanId: z.string(),
              attributes: z.record(z.unknown()).optional(),
            })
          )
          .optional(),
      })
    )
    .min(1)
    .max(50),
});

export const IngestLogsSchema = z.object({
  sessionId: z.string().min(1),
  logs: z
    .array(
      z.object({
        level: LogLevelEnum,
        message: z.string().max(4096),
        timestamp: z.string().datetime(),
        module: z.string().min(1),
        file: z.string().optional(),
        line: z.number().optional(),
        function: z.string().optional(),
        threadId: z.string().optional(),
        correlationId: z.string().optional(),
        context: z.record(z.unknown()).default({}),
      })
    )
    .min(1)
    .max(50),
});

export const CreateScreenshotMetadataSchema = z.object({
  sessionId: z.string().min(1),
  capturedAt: z.string().datetime(),
  trigger: ScreenshotTriggerEnum,
  width: z.number().positive(),
  height: z.number().positive(),
  format: z.enum(['png', 'jpeg', 'webp']),
  sizeBytes: z.number().positive(),
  sensitiveViewsBlurred: z.boolean(),
  blurRegions: z
    .array(z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() }))
    .optional(),
  screenName: z.string().optional(),
  breadcrumbAtCapture: z.string().optional(),
});

export const QueryTracesSchema = z.object({
  limit: z.coerce.number().min(1).max(200).default(50),
  continuationToken: z.string().optional(),
});

export const QueryLogsSchema = z.object({
  level: LogLevelEnum.optional(),
  from: z.string().datetime().optional(),
  to: z.string().datetime().optional(),
  search: z.string().max(256).optional(),
  limit: z.coerce.number().min(1).max(200).default(50),
  continuationToken: z.string().optional(),
});

// ─────────────────────────────────────────────────────────────────────────────
// Inferred Types
// ─────────────────────────────────────────────────────────────────────────────

export type CreateDebugSessionInput = z.infer<typeof CreateDebugSessionSchema>;
export type UpdateDebugSessionInput = z.infer<typeof UpdateDebugSessionSchema>;
export type ListDebugSessionsQuery = z.infer<typeof ListDebugSessionsQuerySchema>;
export type IngestTracesInput = z.infer<typeof IngestTracesSchema>;
export type IngestLogsInput = z.infer<typeof IngestLogsSchema>;
export type CreateScreenshotMetadataInput = z.infer<typeof CreateScreenshotMetadataSchema>;
export type QueryTracesInput = z.infer<typeof QueryTracesSchema>;
export type QueryLogsInput = z.infer<typeof QueryLogsSchema>;

// ─────────────────────────────────────────────────────────────────────────────
// Event Bus Event Types
// ─────────────────────────────────────────────────────────────────────────────

export interface DiagnosticsSessionCreatedEvent {
  sessionId: string;
  productId: string;
  targetUserId?: string;
  createdBy: string;
}

export interface DiagnosticsSessionUpdatedEvent {
  sessionId: string;
  productId: string;
  changes: Partial<DebugSessionDoc>;
  updatedBy: string;
}

export interface DiagnosticsSessionCancelledEvent {
  sessionId: string;
  productId: string;
  reason?: string;
  cancelledBy: string;
}

export interface DiagnosticsSessionCompletedEvent {
  sessionId: string;
  productId: string;
  stats: {
    logCount: number;
    traceCount: number;
    screenshotCount: number;
  };
  endedAt: string;
}

export interface DiagnosticsIngestFatalEvent {
  sessionId: string;
  productId: string;
  logEntry: DebugLogEntryDoc;
  timestamp: string;
}
