// BackendAdapter — abstraction layer for Tauri and Electrobun backends

import type { AgentStartOptions, AgentMessage, AgentStatus, ProviderId } from './agent';
import type { FileEntry, FileContent, PtyCreateOptions, SearchResult } from './protocol';
import type { SettingsMap } from './settings';
import type { GroupsFile } from './project';
import type { AgentId, GroupId, SessionId, ProjectId } from './ids';
import type {
  BtmsgAgent, BtmsgMessage, BtmsgChannel, BtmsgChannelMessage, DeadLetterEntry, AuditEntry,
} from './btmsg';
import type { Task, TaskComment } from './bttask';

// ── Backend capabilities ─────────────────────────────────────────────────────

export interface BackendCapabilities {
  /** Can multiplex multiple PTY sessions (Tauri: yes, Electrobun: via daemon) */
  readonly supportsPtyMultiplexing: boolean;
  /** Web Worker plugin sandbox available */
  readonly supportsPluginSandbox: boolean;
  /** Native OS menu bar integration */
  readonly supportsNativeMenus: boolean;
  /** OS keychain for secrets (libsecret on Linux) */
  readonly supportsOsKeychain: boolean;
  /** Native file open/save dialogs */
  readonly supportsFileDialogs: boolean;
  /** In-app auto-updater (Tauri updater / GitHub Releases) */
  readonly supportsAutoUpdater: boolean;
  /** Desktop notifications (notify-rust / OS notification daemon) */
  readonly supportsDesktopNotifications: boolean;
  /** OpenTelemetry tracing export */
  readonly supportsTelemetry: boolean;
}

// ── Unsubscribe function ─────────────────────────────────────────────────────

/** Call to remove an event listener */
export type UnsubscribeFn = () => void;

// ── Domain-specific sub-interfaces ──────────────────────────────────────────

export interface SessionPersistenceAdapter {
  listSessions(): Promise<PersistedSession[]>;
  saveSession(session: PersistedSession): Promise<void>;
  deleteSession(id: string): Promise<void>;
  updateSessionTitle(id: string, title: string): Promise<void>;
  touchSession(id: string): Promise<void>;
  updateSessionGroup(id: string, groupName: string): Promise<void>;
  saveLayout(layout: PersistedLayout): Promise<void>;
  loadLayout(): Promise<PersistedLayout>;
}

export interface AgentPersistenceAdapter {
  saveAgentMessages(
    sessionId: SessionId, projectId: ProjectId, sdkSessionId: string | undefined,
    messages: AgentMessageRecord[],
  ): Promise<void>;
  loadAgentMessages(projectId: ProjectId): Promise<AgentMessageRecord[]>;
  saveProjectAgentState(state: ProjectAgentState): Promise<void>;
  loadProjectAgentState(projectId: ProjectId): Promise<ProjectAgentState | null>;
  saveSessionMetric(metric: Omit<SessionMetricRecord, 'id'>): Promise<void>;
  loadSessionMetrics(projectId: ProjectId, limit?: number): Promise<SessionMetricRecord[]>;
  getCliGroup(): Promise<string | null>;
  discoverMarkdownFiles(cwd: string): Promise<MdFileEntry[]>;
}

export interface BtmsgAdapter {
  btmsgGetAgents(groupId: GroupId): Promise<BtmsgAgent[]>;
  btmsgUnreadCount(agentId: AgentId): Promise<number>;
  btmsgUnreadMessages(agentId: AgentId): Promise<BtmsgMessage[]>;
  btmsgHistory(agentId: AgentId, otherId: AgentId, limit?: number): Promise<BtmsgMessage[]>;
  btmsgSend(fromAgent: AgentId, toAgent: AgentId, content: string): Promise<string>;
  btmsgSetStatus(agentId: AgentId, status: string): Promise<void>;
  btmsgEnsureAdmin(groupId: GroupId): Promise<void>;
  btmsgAllFeed(groupId: GroupId, limit?: number): Promise<BtmsgFeedMessage[]>;
  btmsgMarkRead(readerId: AgentId, senderId: AgentId): Promise<void>;
  btmsgGetChannels(groupId: GroupId): Promise<BtmsgChannel[]>;
  btmsgChannelMessages(channelId: string, limit?: number): Promise<BtmsgChannelMessage[]>;
  btmsgChannelSend(channelId: string, fromAgent: AgentId, content: string): Promise<string>;
  btmsgCreateChannel(name: string, groupId: GroupId, createdBy: AgentId): Promise<string>;
  btmsgAddChannelMember(channelId: string, agentId: AgentId): Promise<void>;
  btmsgRegisterAgents(config: GroupsFile): Promise<void>;
  btmsgUnseenMessages(agentId: AgentId, sessionId: string): Promise<BtmsgMessage[]>;
  btmsgMarkSeen(sessionId: string, messageIds: string[]): Promise<void>;
  btmsgPruneSeen(): Promise<number>;
  btmsgRecordHeartbeat(agentId: AgentId): Promise<void>;
  btmsgGetStaleAgents(groupId: GroupId, thresholdSecs?: number): Promise<string[]>;
  btmsgGetDeadLetters(groupId: GroupId, limit?: number): Promise<DeadLetterEntry[]>;
  btmsgClearDeadLetters(groupId: GroupId): Promise<void>;
  btmsgClearAllComms(groupId: GroupId): Promise<void>;
}

export interface BttaskAdapter {
  bttaskList(groupId: GroupId): Promise<Task[]>;
  bttaskComments(taskId: string): Promise<TaskComment[]>;
  bttaskUpdateStatus(taskId: string, status: string, version: number): Promise<number>;
  bttaskAddComment(taskId: string, agentId: AgentId, content: string): Promise<string>;
  bttaskCreate(
    title: string, description: string, priority: string,
    groupId: GroupId, createdBy: AgentId, assignedTo?: AgentId,
  ): Promise<string>;
  bttaskDelete(taskId: string): Promise<void>;
  bttaskReviewQueueCount(groupId: GroupId): Promise<number>;
}

export interface AnchorsAdapter {
  saveSessionAnchors(anchors: SessionAnchorRecord[]): Promise<void>;
  loadSessionAnchors(projectId: string): Promise<SessionAnchorRecord[]>;
  deleteSessionAnchor(id: string): Promise<void>;
  clearProjectAnchors(projectId: string): Promise<void>;
  updateAnchorType(id: string, anchorType: string): Promise<void>;
}

export interface SearchAdapter {
  searchInit(): Promise<void>;
  searchAll(query: string, limit?: number): Promise<SearchResult[]>;
  searchRebuild(): Promise<void>;
  searchIndexMessage(sessionId: string, role: string, content: string): Promise<void>;
}

export interface AuditAdapter {
  logAuditEvent(agentId: AgentId, eventType: AuditEventType, detail: string): Promise<void>;
  getAuditLog(groupId: GroupId, limit?: number, offset?: number): Promise<AuditEntry[]>;
  getAuditLogForAgent(agentId: AgentId, limit?: number): Promise<AuditEntry[]>;
}

export interface NotificationsAdapter {
  sendDesktopNotification(title: string, body: string, urgency?: NotificationUrgency): void;
}

export interface TelemetryAdapter {
  telemetryLog(level: TelemetryLevel, message: string, context?: Record<string, unknown>): void;
}

export interface SecretsAdapter {
  storeSecret(key: string, value: string): Promise<void>;
  getSecret(key: string): Promise<string | null>;
  deleteSecret(key: string): Promise<void>;
  listSecrets(): Promise<string[]>;
  hasKeyring(): Promise<boolean>;
  knownSecretKeys(): Promise<string[]>;
}

export interface FsWatcherAdapter {
  fsWatchProject(projectId: string, cwd: string): Promise<void>;
  fsUnwatchProject(projectId: string): Promise<void>;
  onFsWriteDetected(callback: (event: FsWriteEvent) => void): UnsubscribeFn;
  fsWatcherStatus(): Promise<FsWatcherStatus>;
}

export interface CtxAdapter {
  ctxInitDb(): Promise<void>;
  ctxRegisterProject(name: string, description: string, workDir?: string): Promise<void>;
  ctxGetContext(project: string): Promise<CtxEntry[]>;
  ctxGetShared(): Promise<CtxEntry[]>;
  ctxGetSummaries(project: string, limit?: number): Promise<CtxSummary[]>;
  ctxSearch(query: string): Promise<CtxEntry[]>;
}

export interface MemoraAdapter {
  memoraAvailable(): Promise<boolean>;
  memoraList(options?: { tags?: string[]; limit?: number; offset?: number }): Promise<MemoraSearchResult>;
  memoraSearch(query: string, options?: { tags?: string[]; limit?: number }): Promise<MemoraSearchResult>;
  memoraGet(id: number): Promise<MemoraNode | null>;
}

export interface SshAdapter {
  listSshSessions(): Promise<SshSessionRecord[]>;
  saveSshSession(session: SshSessionRecord): Promise<void>;
  deleteSshSession(id: string): Promise<void>;
}

export interface PluginsAdapter {
  discoverPlugins(): Promise<PluginMeta[]>;
  readPluginFile(pluginId: string, filename: string): Promise<string>;
}

export interface ClaudeProviderAdapter {
  listProfiles(): Promise<ClaudeProfile[]>;
  listSkills(): Promise<ClaudeSkill[]>;
  readSkill(path: string): Promise<string>;
}

export interface RemoteMachineAdapter {
  listRemoteMachines(): Promise<RemoteMachineInfo[]>;
  addRemoteMachine(config: RemoteMachineConfig): Promise<string>;
  removeRemoteMachine(machineId: string): Promise<void>;
  connectRemoteMachine(machineId: string): Promise<void>;
  disconnectRemoteMachine(machineId: string): Promise<void>;
  probeSpki(url: string): Promise<string>;
  addSpkiPin(machineId: string, pin: string): Promise<void>;
  removeSpkiPin(machineId: string, pin: string): Promise<void>;
  onRemoteSidecarMessage(callback: (msg: RemoteSidecarMessage) => void): UnsubscribeFn;
  onRemotePtyData(callback: (msg: RemotePtyData) => void): UnsubscribeFn;
  onRemotePtyExit(callback: (msg: RemotePtyExit) => void): UnsubscribeFn;
  onRemoteMachineReady(callback: (msg: RemoteMachineEvent) => void): UnsubscribeFn;
  onRemoteMachineDisconnected(callback: (msg: RemoteMachineEvent) => void): UnsubscribeFn;
  onRemoteStateSync(callback: (msg: RemoteMachineEvent) => void): UnsubscribeFn;
  onRemoteError(callback: (msg: RemoteMachineEvent) => void): UnsubscribeFn;
  onRemoteMachineReconnecting(callback: (msg: RemoteReconnectingEvent) => void): UnsubscribeFn;
  onRemoteMachineReconnectReady(callback: (msg: RemoteMachineEvent) => void): UnsubscribeFn;
  onRemoteSpkiTofu(callback: (msg: RemoteSpkiTofuEvent) => void): UnsubscribeFn;
}

export interface FileWatcherAdapter {
  watchFile(paneId: string, path: string): Promise<string>;
  unwatchFile(paneId: string): Promise<void>;
  readWatchedFile(path: string): Promise<string>;
  onFileChanged(callback: (payload: FileChangedPayload) => void): UnsubscribeFn;
}

export interface AgentBridgeAdapter {
  /** Direct agent IPC — supports remote machines and resume */
  queryAgent(options: AgentQueryOptions): Promise<void>;
  stopAgentDirect(sessionId: string, remoteMachineId?: string): Promise<void>;
  isAgentReady(): Promise<boolean>;
  restartAgentSidecar(): Promise<void>;
  setSandbox(projectCwds: string[], worktreeRoots: string[], enabled: boolean): Promise<void>;
  onSidecarMessage(callback: (msg: SidecarMessagePayload) => void): UnsubscribeFn;
  onSidecarExited(callback: () => void): UnsubscribeFn;
}

export interface PtyBridgeAdapter {
  /** Per-session PTY — supports remote machines */
  spawnPty(options: PtySpawnOptions): Promise<string>;
  writePtyDirect(id: string, data: string, remoteMachineId?: string): Promise<void>;
  resizePtyDirect(id: string, cols: number, rows: number, remoteMachineId?: string): Promise<void>;
  killPty(id: string, remoteMachineId?: string): Promise<void>;
  onPtyData(id: string, callback: (data: string) => void): UnsubscribeFn;
  onPtyExit(id: string, callback: () => void): UnsubscribeFn;
}

// ── Backend adapter interface ────────────────────────────────────────────────

export interface BackendAdapter extends
  SessionPersistenceAdapter,
  AgentPersistenceAdapter,
  BtmsgAdapter,
  BttaskAdapter,
  AnchorsAdapter,
  SearchAdapter,
  AuditAdapter,
  NotificationsAdapter,
  TelemetryAdapter,
  SecretsAdapter,
  FsWatcherAdapter,
  CtxAdapter,
  MemoraAdapter,
  SshAdapter,
  PluginsAdapter,
  ClaudeProviderAdapter,
  RemoteMachineAdapter,
  FileWatcherAdapter,
  AgentBridgeAdapter,
  PtyBridgeAdapter {

  readonly capabilities: BackendCapabilities;

  // ── Lifecycle ────────────────────────────────────────────────────────────

  /** Initialize the backend (connect to IPC, register listeners, etc.) */
  init(): Promise<void>;

  /** Tear down the backend (disconnect, clean up listeners) */
  destroy(): Promise<void>;

  // ── Settings ─────────────────────────────────────────────────────────────

  getSetting(key: string): Promise<string | null>;
  setSetting(key: string, value: string): Promise<void>;
  getAllSettings(): Promise<SettingsMap>;

  // ── Groups ───────────────────────────────────────────────────────────────

  loadGroups(): Promise<GroupsFile>;
  saveGroups(groups: GroupsFile): Promise<void>;

  // ── Agent (simplified) ────────────────────────────────────────────────────

  startAgent(options: AgentStartOptions): Promise<{ ok: boolean; error?: string }>;
  stopAgent(sessionId: string): Promise<{ ok: boolean; error?: string }>;
  sendPrompt(sessionId: string, prompt: string): Promise<{ ok: boolean; error?: string }>;

  // ── PTY (simplified) ──────────────────────────────────────────────────────

  createPty(options: PtyCreateOptions): Promise<string>;
  writePty(sessionId: string, data: string): Promise<void>;
  resizePty(sessionId: string, cols: number, rows: number): Promise<void>;
  closePty(sessionId: string): Promise<void>;

  // ── Files ────────────────────────────────────────────────────────────────

  listDirectory(path: string): Promise<FileEntry[]>;
  readFile(path: string): Promise<FileContent>;
  writeFile(path: string, content: string): Promise<void>;

  // ── Events (backend -> frontend) ─────────────────────────────────────────

  onAgentMessage(callback: (sessionId: string, messages: AgentMessage[]) => void): UnsubscribeFn;
  onAgentStatus(callback: (sessionId: string, status: AgentStatus, error?: string) => void): UnsubscribeFn;
  onAgentCost(callback: (sessionId: string, costUsd: number, inputTokens: number, outputTokens: number) => void): UnsubscribeFn;
  onPtyOutput(callback: (sessionId: string, data: string) => void): UnsubscribeFn;
  onPtyClosed(callback: (sessionId: string, exitCode: number | null) => void): UnsubscribeFn;
}

// ── Shared record types ─────────────────────────────────────────────────────

export interface PersistedSession {
  id: string;
  type: string;
  title: string;
  shell?: string;
  cwd?: string;
  args?: string[];
  group_name?: string;
  created_at: number;
  last_used_at: number;
}

export interface PersistedLayout {
  preset: string;
  pane_ids: string[];
}

export interface AgentMessageRecord {
  id: number;
  session_id: SessionId;
  project_id: ProjectId;
  sdk_session_id: string | null;
  message_type: string;
  content: string;
  parent_id: string | null;
  created_at: number;
}

export interface ProjectAgentState {
  project_id: ProjectId;
  last_session_id: SessionId;
  sdk_session_id: string | null;
  status: string;
  cost_usd: number;
  input_tokens: number;
  output_tokens: number;
  last_prompt: string | null;
  updated_at: number;
}

export interface SessionMetricRecord {
  id: number;
  project_id: ProjectId;
  session_id: SessionId;
  start_time: number;
  end_time: number;
  peak_tokens: number;
  turn_count: number;
  tool_call_count: number;
  cost_usd: number;
  model: string | null;
  status: string;
  error_message: string | null;
}

export interface MdFileEntry {
  name: string;
  path: string;
  priority: boolean;
}

export interface SessionAnchorRecord {
  id: string;
  project_id: string;
  message_id: string;
  anchor_type: string;
  content: string;
  estimated_tokens: number;
  turn_index: number;
  created_at: number;
}

export type AuditEventType =
  | 'prompt_injection'
  | 'wake_event'
  | 'btmsg_sent'
  | 'btmsg_received'
  | 'status_change'
  | 'heartbeat_missed'
  | 'dead_letter';

export type NotificationUrgency = 'low' | 'normal' | 'critical';
export type TelemetryLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace';

export interface FsWriteEvent {
  project_id: string;
  file_path: string;
  timestamp_ms: number;
}

export interface FsWatcherStatus {
  max_watches: number;
  estimated_watches: number;
  usage_ratio: number;
  active_projects: number;
  warning: string | null;
}

export interface CtxEntry {
  project: string;
  key: string;
  value: string;
  updated_at: string;
}

export interface CtxSummary {
  project: string;
  summary: string;
  created_at: string;
}

export interface MemoraNode {
  id: number;
  content: string;
  tags: string[];
  metadata?: Record<string, unknown>;
  created_at?: string;
  updated_at?: string;
}

export interface MemoraSearchResult {
  nodes: MemoraNode[];
  total: number;
}

export interface SshSessionRecord {
  id: string;
  name: string;
  host: string;
  port: number;
  username: string;
  key_file: string;
  folder: string;
  color: string;
  created_at: number;
  last_used_at: number;
}

export interface PluginMeta {
  id: string;
  name: string;
  version: string;
  description: string;
  main: string;
  permissions: string[];
}

export interface ClaudeProfile {
  name: string;
  email: string | null;
  subscription_type: string | null;
  display_name: string | null;
  config_dir: string;
}

export interface ClaudeSkill {
  name: string;
  description: string;
  source_path: string;
}

export interface RemoteMachineConfig {
  label: string;
  url: string;
  token: string;
  auto_connect: boolean;
  spki_pins?: string[];
}

export interface RemoteMachineInfo {
  id: string;
  label: string;
  url: string;
  status: string;
  auto_connect: boolean;
  spki_pins: string[];
}

export interface RemoteSidecarMessage {
  machineId: string;
  sessionId?: string;
  event?: Record<string, unknown>;
}

export interface RemotePtyData {
  machineId: string;
  sessionId?: string;
  data?: string;
}

export interface RemotePtyExit {
  machineId: string;
  sessionId?: string;
}

export interface RemoteMachineEvent {
  machineId: string;
  payload?: unknown;
  error?: unknown;
}

export interface RemoteReconnectingEvent {
  machineId: string;
  backoffSecs: number;
}

export interface RemoteSpkiTofuEvent {
  machineId: string;
  hash: string;
}

export interface FileChangedPayload {
  pane_id: string;
  path: string;
  content: string;
}

export interface BtmsgFeedMessage {
  id: string;
  fromAgent: AgentId;
  toAgent: AgentId;
  content: string;
  createdAt: string;
  replyTo: string | null;
  senderName: string;
  senderRole: string;
  recipientName: string;
  recipientRole: string;
}

export interface AgentQueryOptions {
  provider?: ProviderId;
  session_id: string;
  prompt: string;
  cwd?: string;
  max_turns?: number;
  max_budget_usd?: number;
  resume_session_id?: string;
  permission_mode?: string;
  setting_sources?: string[];
  system_prompt?: string;
  model?: string;
  claude_config_dir?: string;
  additional_directories?: string[];
  worktree_name?: string;
  provider_config?: Record<string, unknown>;
  extra_env?: Record<string, string>;
  remote_machine_id?: string;
}

export interface SidecarMessagePayload {
  type: string;
  sessionId?: string;
  event?: Record<string, unknown>;
  message?: string;
  exitCode?: number | null;
  signal?: string | null;
}

export interface PtySpawnOptions {
  shell?: string;
  cwd?: string;
  args?: string[];
  cols?: number;
  rows?: number;
  remote_machine_id?: string;
}
