const TOKEN_KEY = 'archnest_token'

export function getToken(): string | null {
  return localStorage.getItem(TOKEN_KEY)
}

export function setToken(token: string | null) {
  if (token) localStorage.setItem(TOKEN_KEY, token)
  else localStorage.removeItem(TOKEN_KEY)
}

export class ApiError extends Error {
  status: number
  constructor(status: number, message: string) {
    super(message)
    this.status = status
  }
}

export async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
  const token = getToken()
  const headers: Record<string, string> = {
    ...(options.body ? { 'Content-Type': 'application/json' } : {}),
    ...(options.headers as Record<string, string> | undefined),
  }
  if (token) headers.Authorization = `Bearer ${token}`

  const res = await fetch(`/api${path}`, { ...options, headers })

  if (!res.ok) {
    let message = res.statusText
    try {
      const body = await res.json()
      message = body.error ?? message
    } catch {
      // ignore non-JSON error bodies
    }
    throw new ApiError(res.status, message)
  }

  if (res.status === 204) return undefined as T
  return res.json() as Promise<T>
}

export const api = {
  getSetupStatus: () => apiFetch<{ needsSetup: boolean }>('/system/setup-status'),
  setup: (username: string, password: string) =>
    apiFetch<{ token: string }>('/setup', { method: 'POST', body: JSON.stringify({ username, password }) }),
  login: (username: string, password: string) =>
    apiFetch<{ token: string }>('/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) }),
  me: () => apiFetch<{ user: AuthUser }>('/auth/me'),
  updateMe: (data: Partial<{ displayName: string | null; email: string | null; avatarDataUrl: string | null }>) =>
    apiFetch<{ user: AuthUser }>('/auth/me', { method: 'PUT', body: JSON.stringify(data) }),

  listIntegrations: () => apiFetch<{ integrations: Integration[] }>('/integrations'),
  createIntegration: (data: { type: string; name: string; config?: Record<string, string>; secrets?: Record<string, string> }) =>
    apiFetch<{ integration: Integration }>('/integrations', { method: 'POST', body: JSON.stringify(data) }),
  updateIntegration: (id: number, data: Partial<{ name: string; config: Record<string, string>; secrets: Record<string, string> }>) =>
    apiFetch<{ integration: Integration }>(`/integrations/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteIntegration: (id: number) => apiFetch<void>(`/integrations/${id}`, { method: 'DELETE' }),
  testIntegration: (id: number) => apiFetch<{ ok: boolean; message: string }>(`/integrations/${id}/test`, { method: 'POST' }),

  listBookmarks: () => apiFetch<{ bookmarks: Bookmark[] }>('/bookmarks'),
  listBookmarkCategories: () => apiFetch<{ categories: BookmarkCategory[] }>('/bookmarks/categories'),
  createBookmarkCategory: (data: { name: string; icon?: string; sortOrder?: number }) =>
    apiFetch<{ id: number }>('/bookmarks/categories', { method: 'POST', body: JSON.stringify(data) }),
  createBookmark: (data: { categoryId?: number | null; title: string; url: string; icon?: string; favorite?: boolean }) =>
    apiFetch<{ id: number }>('/bookmarks', { method: 'POST', body: JSON.stringify(data) }),
  updateBookmark: (id: number, data: Partial<{ categoryId: number | null; title: string; url: string; icon: string; favorite: boolean }>) =>
    apiFetch<{ ok: boolean }>(`/bookmarks/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  deleteBookmark: (id: number) => apiFetch<void>(`/bookmarks/${id}`, { method: 'DELETE' }),

  listEvents: (limit = 20) => apiFetch<{ events: Event[] }>(`/events?limit=${limit}`),
  listResources: () => apiFetch<{ resources: Resource[] }>('/integrations/resources'),

  listTunnels: () => apiFetch<{ tunnels: Tunnel[] }>('/tunnels'),
  createTunnel: (data: {
    name: string
    integrationId: number
    mode: 'local' | 'remote' | 'dynamic'
    sourcePort: number
    endpointHost?: string
    endpointPort?: number
    autoStart?: boolean
    maxRetries?: number
    retryIntervalMs?: number
  }) => apiFetch<{ tunnel: Tunnel }>('/tunnels', { method: 'POST', body: JSON.stringify(data) }),
  deleteTunnel: (id: number) => apiFetch<void>(`/tunnels/${id}`, { method: 'DELETE' }),
  connectTunnel: (id: number) =>
    apiFetch<{ ok: boolean; status: string; error: string | null; retryCount: number }>(`/tunnels/${id}/connect`, { method: 'POST' }),
  disconnectTunnel: (id: number) =>
    apiFetch<{ ok: boolean; status: string; error: string | null; retryCount: number }>(`/tunnels/${id}/disconnect`, { method: 'POST' }),

  listFiles: (integrationId: number, path: string) =>
    apiFetch<{ path: string; entries: FileEntry[] }>(`/files/${integrationId}/list?path=${encodeURIComponent(path)}`),
  readFile: (integrationId: number, path: string) =>
    apiFetch<{ content: string; encoding: 'utf8' | 'base64'; size: number; mode: number }>(
      `/files/${integrationId}/content?path=${encodeURIComponent(path)}`,
    ),
  writeFile: (integrationId: number, path: string, content: string, encoding: 'utf8' | 'base64' = 'utf8') =>
    apiFetch<{ ok: boolean }>(`/files/${integrationId}/content`, { method: 'PUT', body: JSON.stringify({ path, content, encoding }) }),
  mkdir: (integrationId: number, path: string) =>
    apiFetch<{ ok: boolean }>(`/files/${integrationId}/mkdir`, { method: 'POST', body: JSON.stringify({ path }) }),
  renameFile: (integrationId: number, from: string, to: string) =>
    apiFetch<{ ok: boolean }>(`/files/${integrationId}/rename`, { method: 'POST', body: JSON.stringify({ from, to }) }),
  deleteFile: (integrationId: number, path: string, isDirectory = false) =>
    apiFetch<{ ok: boolean }>(`/files/${integrationId}/delete`, { method: 'POST', body: JSON.stringify({ path, isDirectory }) }),
  chmodFile: (integrationId: number, path: string, mode: string) =>
    apiFetch<{ ok: boolean }>(`/files/${integrationId}/chmod`, { method: 'POST', body: JSON.stringify({ path, mode }) }),
  downloadFileUrl: (integrationId: number, path: string) =>
    `/api/files/${integrationId}/download?path=${encodeURIComponent(path)}`,
  uploadFile: async (integrationId: number, path: string, file: File) => {
    const form = new FormData()
    form.append('path', path)
    form.append('file', file)
    const token = getToken()
    const res = await fetch(`/api/files/${integrationId}/upload`, {
      method: 'POST',
      headers: token ? { Authorization: `Bearer ${token}` } : undefined,
      body: form,
    })
    if (!res.ok) {
      let message = res.statusText
      try {
        message = (await res.json()).error ?? message
      } catch {
        // ignore non-JSON error bodies
      }
      throw new ApiError(res.status, message)
    }
    return res.json() as Promise<{ ok: boolean; path: string }>
  },

  listContainers: (integrationId: number) =>
    apiFetch<{ containers: Container[] }>(`/docker/${integrationId}/containers`),
  containerStats: (integrationId: number, id: string) =>
    apiFetch<ContainerStats>(`/docker/${integrationId}/containers/${encodeURIComponent(id)}/stats`),
  containerLogs: (integrationId: number, id: string, tail = 200) =>
    apiFetch<{ logs: string }>(`/docker/${integrationId}/containers/${encodeURIComponent(id)}/logs?tail=${tail}`),
  containerAction: (integrationId: number, id: string, action: 'start' | 'stop' | 'restart' | 'pause' | 'unpause') =>
    apiFetch<{ ok: boolean }>(`/docker/${integrationId}/containers/${encodeURIComponent(id)}/${action}`, { method: 'POST' }),
  removeContainer: (integrationId: number, id: string, force = false) =>
    apiFetch<{ ok: boolean }>(`/docker/${integrationId}/containers/${encodeURIComponent(id)}/remove`, {
      method: 'POST',
      body: JSON.stringify({ force }),
    }),

  getHostMetrics: (integrationId: number) => apiFetch<HostMetrics>(`/integrations/${integrationId}/metrics`),

  startTransfer: (data: { sourceIntegrationId: number; destIntegrationId: number; sourcePaths: string[]; destPath: string; move?: boolean }) =>
    apiFetch<{ transferId: string }>('/transfers', { method: 'POST', body: JSON.stringify(data) }),
  listTransfers: () => apiFetch<{ transfers: TransferProgress[] }>('/transfers'),
  getTransfer: (id: string) => apiFetch<TransferProgress>(`/transfers/${id}`),
  cancelTransfer: (id: string) => apiFetch<{ ok: boolean }>(`/transfers/${id}/cancel`, { method: 'POST' }),

  exportData: () => apiFetch<DataExport>('/data/export'),
  importData: (data: DataExport) =>
    apiFetch<{ ok: boolean; imported: { integrations: number; bookmarkCategories: number; bookmarks: number; tunnels: number } }>('/data/import', {
      method: 'POST',
      body: JSON.stringify(data),
    }),
}

export interface DataExport {
  version: number
  exportedAt?: string
  integrations: Array<{ id: number; type: string; name: string; enabled: boolean; config: Record<string, string>; secrets: Record<string, string> }>
  bookmarkCategories: Array<{ id: number; name: string; icon: string | null; sortOrder: number }>
  bookmarks: Array<{ categoryId: number | null; title: string; url: string; icon: string | null; favorite: boolean }>
  tunnels: Array<{ name: string; integrationId: number; mode: string; sourcePort: number; endpointHost: string; endpointPort: number; autoStart: boolean; maxRetries: number; retryIntervalMs: number }>
}

export interface AuthUser {
  id: number
  username: string
  display_name: string | null
  email: string | null
  avatar_data_url: string | null
}

export interface Integration {
  id: number
  type: string
  name: string
  enabled: boolean
  status: string
  config: Record<string, string>
  lastCheckedAt: string | null
  createdAt: string
}

export interface Tunnel {
  id: number
  name: string
  integrationId: number
  mode: 'local' | 'remote' | 'dynamic'
  sourcePort: number
  endpointHost: string
  endpointPort: number
  autoStart: boolean
  maxRetries: number
  retryIntervalMs: number
  createdAt: string
  status: 'stopped' | 'connecting' | 'connected' | 'retrying' | 'error'
  error: string | null
  retryCount: number
}

export interface Bookmark {
  id: number
  category_id: number | null
  title: string
  url: string
  icon: string | null
  favorite: number
  status: string
  last_checked_at: string | null
  created_at: string
}

export interface BookmarkCategory {
  id: number
  name: string
  icon: string | null
  sort_order: number
}

export interface Event {
  id: number
  type: string
  title: string
  source: string | null
  created_at: string
}

export interface FileEntry {
  name: string
  isDirectory: boolean
  isSymlink: boolean
  size: number
  mode: number
  mtime: number
}

export interface Container {
  id: string
  name: string
  image: string
  state: string
  status: string
  ports: { privatePort: number; publicPort?: number; type: string }[]
}

export interface ContainerStats {
  cpuPercent: number
  memUsage: number
  memLimit: number
  netRx: number
  netTx: number
}

export interface Resource {
  name: string
  status: 'healthy' | 'warning' | 'critical' | 'unknown'
  detail?: string
  integration: string
}

export interface TransferProgress {
  transferId: string
  status: 'running' | 'completed' | 'failed' | 'cancelled'
  sourceIntegrationId: number
  destIntegrationId: number
  sourcePaths: string[]
  destPath: string
  move: boolean
  totalFiles: number
  totalBytes: number
  filesTransferred: number
  bytesTransferred: number
  currentFile: string | null
  error: string | null
  startedAt: number
  finishedAt: number | null
}

export interface HostMetrics {
  cpu: { percent: number | null; cores: number | null; load: [number, number, number] | null }
  memory: { percent: number | null; usedGiB: number | null; totalGiB: number | null }
  disk: { percent: number | null; usedHuman: string | null; totalHuman: string | null; availableHuman: string | null }
  uptime: { seconds: number | null; formatted: string | null }
  network: { interfaces: Array<{ name: string; ip: string; state: string }> }
  system: { hostname: string | null; kernel: string | null; os: string | null }
  processes: {
    total: number | null
    running: number | null
    top: Array<{ pid: string; user: string; cpu: string; mem: string; command: string }>
  }
  ports: { source: 'ss' | 'netstat' | 'none'; ports: Array<{ protocol: string; localAddress: string; localPort: number; state?: string; process?: string }> }
  firewall: { type: 'iptables' | 'none'; status: 'active' | 'inactive' | 'unknown'; chains: Array<{ name: string; policy: string; rules: unknown[] }> }
  loginStats: {
    recentLogins: Array<{ user: string; ip: string; time: string; status: string }>
    failedLogins: Array<{ user: string; ip: string; time: string; status: string }>
    totalLogins: number
    uniqueIPs: number
  }
}
