import { openDB, type IDBPDatabase } from "idb"
import { apiClient } from "./client"

export interface PendingMutation {
  id: string
  timestamp: number
  type:
    | "send_message"
    | "update_flags"
    | "update_labels"
    | "delete_message"
    | "create_draft"
    | "update_draft"
    | "schedule_send"
    | "create_contact"
    | "update_contact"
    | "delete_draft"
    | "delete_contact"
  payload: unknown
  retries: number
}

const DB_NAME = "ultimail-offline-queue"
const STORE_NAME = "mutations"

let dbPromise: Promise<IDBPDatabase> | null = null

function getDb() {
  if (!dbPromise) {
    dbPromise = openDB(DB_NAME, 1, {
      upgrade(db) {
        db.createObjectStore(STORE_NAME, { keyPath: "id" })
      },
    })
  }
  return dbPromise
}

export async function enqueue(mutation: PendingMutation): Promise<void> {
  const db = await getDb()
  await db.put(STORE_NAME, mutation)
}

export async function getAll(): Promise<PendingMutation[]> {
  const db = await getDb()
  return db.getAll(STORE_NAME)
}

export async function remove(id: string): Promise<void> {
  const db = await getDb()
  await db.delete(STORE_NAME, id)
}

export async function getPendingCount(): Promise<number> {
  const db = await getDb()
  return db.count(STORE_NAME)
}

const MUTATION_ENDPOINTS: Record<PendingMutation["type"], { method: "post" | "put" | "delete"; path: (p: any) => string }> = {
  send_message: { method: "post", path: () => "/outbox" },
  update_flags: { method: "put", path: (p) => `/messages/${p.message_id}/flags` },
  update_labels: { method: "put", path: (p) => `/messages/${p.message_id}/labels` },
  delete_message: { method: "delete", path: (p) => `/messages/${p.message_id}` },
  create_draft: { method: "post", path: () => "/drafts" },
  update_draft: { method: "put", path: (p) => `/drafts/${p.draft_id}` },
  schedule_send: { method: "post", path: () => "/outbox/schedule" },
  delete_draft: { method: "delete", path: (p) => `/drafts/${p.draft_id}` },
  create_contact: { method: "post", path: () => "/contacts" },
  update_contact: { method: "put", path: (p) => `/contacts/${p.uid}` },
  delete_contact: { method: "delete", path: (p) => `/contacts/${p.uid}` },
}

export async function flush(): Promise<void> {
  const mutations = await getAll()
  const sorted = mutations.sort((a, b) => a.timestamp - b.timestamp)

  for (const mutation of sorted) {
    try {
      const endpoint = MUTATION_ENDPOINTS[mutation.type]
      const path = endpoint.path(mutation.payload)

      switch (endpoint.method) {
        case "post":
          await apiClient.post(path, mutation.payload)
          break
        case "put":
          await apiClient.put(path, mutation.payload)
          break
        case "delete":
          await apiClient.delete(path)
          break
      }

      await remove(mutation.id)
    } catch {
      const db = await getDb()
      await db.put(STORE_NAME, { ...mutation, retries: mutation.retries + 1 })
    }
  }
}

if (typeof window !== "undefined") {
  window.addEventListener("online", () => {
    flush()
  })
}
