/**
 * Sync Engine Types
 *
 * @module @bytelyst/sync/types
 */

import type { ApiClient } from '@bytelyst/api-client';
import type { TelemetryClient } from '@bytelyst/telemetry-client';

// ─────────────────────────────────────────────────────────────────────────────
// Core Types
// ─────────────────────────────────────────────────────────────────────────────

export type EntityName = string;

export type ConflictStrategy = 'server-wins' | 'client-wins' | 'last-write-wins' | 'manual';

export type SyncStatus = 'idle' | 'syncing' | 'offline' | 'error';

export type SyncOperation = 'create' | 'update' | 'delete';

export interface EntityConfig {
  /** REST endpoint path, e.g. '/api/timers' */
  endpoint: string;
  /** Cosmos partition key field name (for reference) */
  partitionKey: string;
  /** Conflict resolution strategy */
  conflictStrategy: ConflictStrategy;
}

/**
 * Callback invoked when items are pulled from the server.
 * Consumer is responsible for merging pulled data into their local store.
 */
export type PullHandler = (entity: EntityName, items: unknown[]) => void | Promise<void>;

export interface SyncEngineConfig {
  productId: string;
  entities: Record<EntityName, EntityConfig>;
  storage: StorageAdapter;
  apiClient: ApiClient;
  telemetryClient?: TelemetryClient;
  /** Called when items are pulled from server — consumer merges into local store */
  onPull?: PullHandler;
  /** Called for 'manual' conflict strategy. Return the winning data. */
  onConflict?: (conflict: Conflict) => Promise<unknown> | unknown;
  /** Max retry attempts before dropping an item. Default: 5. */
  maxRetries?: number;
  /** Base delay in ms for exponential backoff. Default: 1000. */
  retryBaseDelayMs?: number;
  /** Maximum backoff delay in ms. Default: 30000. */
  retryMaxDelayMs?: number;
}

export interface SyncItem {
  id: string;
  entity: EntityName;
  operation: SyncOperation;
  data: unknown;
  timestamp: string;
  retryCount: number;
  lastError?: string;
}

export interface SyncResult {
  success: boolean;
  pushed: number;
  pulled: number;
  conflicts: number;
  errors: number;
  timestamp: string;
}

export interface SyncStatusInfo {
  status: SyncStatus;
  queueLength: number;
  lastSyncAt?: string;
  lastError?: string;
}

export type SyncStatusCallback = (status: SyncStatusInfo) => void;

export interface Conflict {
  entity: EntityName;
  localItem: SyncItem;
  remoteData: unknown;
}

// ─────────────────────────────────────────────────────────────────────────────
// Storage Adapter Interface
// ─────────────────────────────────────────────────────────────────────────────

export interface StorageAdapter {
  getItem<T>(key: string): Promise<T | null> | T | null;
  setItem<T>(key: string, value: T): Promise<void> | void;
  removeItem(key: string): Promise<void> | void;
  keys(): Promise<string[]> | string[];
}

// ─────────────────────────────────────────────────────────────────────────────
// Sync Engine Interface
// ─────────────────────────────────────────────────────────────────────────────

export interface SyncEngine {
  /** Queue a create/update for later push. Deduplicates by entity + data.id. */
  push(entity: EntityName, data: unknown, operation?: SyncOperation): Promise<void>;
  /** Queue a delete for later push. */
  delete(entity: EntityName, id: string): Promise<void>;
  /** Pull remote changes for all entities. Invokes onPull callback. */
  pull(): Promise<SyncResult>;
  /** Push queued items, then pull remote changes. */
  fullSync(): Promise<SyncResult>;

  /** Current number of items in the offline queue. */
  getQueueLength(): number;
  /** Current sync status snapshot. */
  getStatus(): SyncStatusInfo;
  /** Subscribe to status changes. Returns unsubscribe function. */
  onStatusChange(callback: SyncStatusCallback): () => void;

  /** Remove all items from the offline queue. */
  clearQueue(): Promise<void>;
  /** Reset retry counts on all failed items and re-flush. */
  reprocessFailed(): Promise<void>;
  /** Manually trigger a flush of the push queue. */
  flush(): Promise<void>;
  /** Tear down connectivity listeners. */
  destroy(): void;
}
