import browser from 'webextension-polyfill'
import { normalizePageUrl, normalizeQuote, type ClipItem } from './clipTypes'

const KEY = 'clips'
const MAX_ITEMS = 500

function byTimeDesc(a: ClipItem, b: ClipItem): number {
  return Date.parse(b.createdAt || '') - Date.parse(a.createdAt || '')
}

function clipFingerprint(item: Pick<ClipItem, 'pageUrl' | 'quote'>): string {
  return `${normalizePageUrl(item.pageUrl)}::${normalizeQuote(item.quote).toLowerCase()}`
}

export async function listClips(): Promise<ClipItem[]> {
  const raw = (await browser.storage.local.get([KEY])) as Record<string, unknown>
  const value = raw[KEY]
  if (!Array.isArray(value)) return []
  return value as ClipItem[]
}

export async function setClips(items: ClipItem[]): Promise<void> {
  const next = [...items].sort(byTimeDesc).slice(0, MAX_ITEMS)
  await browser.storage.local.set({ [KEY]: next })
}

export async function listClipsByUrl(pageUrl: string): Promise<ClipItem[]> {
  const normalized = normalizePageUrl(pageUrl)
  const items = await listClips()
  return items.filter((item) => normalizePageUrl(item.pageUrl) === normalized).sort(byTimeDesc)
}

export async function getClipById(id: string): Promise<ClipItem | null> {
  const items = await listClips()
  const found = items.find((item) => item.id === id)
  return found || null
}

export async function insertClip(item: ClipItem): Promise<ClipItem> {
  const items = await listClips()
  const fp = clipFingerprint(item)
  const alwaysDuplicate = items.find((v) => clipFingerprint(v) === fp)
  if (alwaysDuplicate) {
    const idx = items.findIndex((v) => v.id === alwaysDuplicate.id)
    if (idx >= 0) {
      items[idx] = {
        ...items[idx],
        tabId: item.tabId ?? items[idx].tabId,
        pageTitle: item.pageTitle || items[idx].pageTitle,
        anchor: item.anchor || items[idx].anchor,
        resolveStatus: item.resolveStatus ?? items[idx].resolveStatus,
        resolveUpdatedAt: item.resolveUpdatedAt ?? items[idx].resolveUpdatedAt,
      }
      await setClips(items)
      return items[idx]
    }
    return alwaysDuplicate
  }

  const now = Date.now()
  const duplicate = items.find((v) => {
    const createdMs = Date.parse(v.createdAt || '')
    if (!Number.isFinite(createdMs) || Math.abs(now - createdMs) > 8000) return false
    return clipFingerprint(v) === fp
  })
  if (duplicate) return duplicate
  items.unshift(item)
  await setClips(items)
  return item
}

export async function deleteClip(id: string): Promise<boolean> {
  const items = await listClips()
  const next = items.filter((item) => item.id !== id)
  if (next.length === items.length) return false
  await setClips(next)
  return true
}

export async function updateClipResolveStatus(id: string, status: 'ok' | 'broken'): Promise<ClipItem | null> {
  const items = await listClips()
  const idx = items.findIndex((item) => item.id === id)
  if (idx < 0) return null
  const current = items[idx]
  if (current.resolveStatus === status) return current
  const nextItem: ClipItem = {
    ...current,
    resolveStatus: status,
    resolveUpdatedAt: new Date().toISOString(),
  }
  items[idx] = nextItem
  await setClips(items)
  return nextItem
}

function sanitizeImportedClip(raw: any): ClipItem | null {
  if (!raw || typeof raw !== 'object') return null
  const pageUrl = normalizePageUrl(String(raw.pageUrl || ''))
  const quote = String(raw.quote || raw?.anchor?.exact || '').trim()
  const exact = String(raw?.anchor?.exact || quote).trim()
  if (!pageUrl || !quote || !exact) return null

  const createdAt = String(raw.createdAt || new Date().toISOString())
  const id = String(raw.id || `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`)
  return {
    id,
    tabId: Number.isInteger(raw.tabId) ? Number(raw.tabId) : undefined,
    pageUrl,
    pageTitle: String(raw.pageTitle || pageUrl),
    quote,
    quoteHtml: raw.quoteHtml ? String(raw.quoteHtml) : undefined,
    createdAt,
    color: 'yellow',
    resolveStatus: raw.resolveStatus === 'broken' ? 'broken' : raw.resolveStatus === 'ok' ? 'ok' : undefined,
    resolveUpdatedAt: raw.resolveUpdatedAt ? String(raw.resolveUpdatedAt) : undefined,
    anchor: {
      exact,
      prefix: raw?.anchor?.prefix ? String(raw.anchor.prefix) : undefined,
      suffix: raw?.anchor?.suffix ? String(raw.anchor.suffix) : undefined,
      xpathStart: raw?.anchor?.xpathStart ? String(raw.anchor.xpathStart) : undefined,
      xpathEnd: raw?.anchor?.xpathEnd ? String(raw.anchor.xpathEnd) : undefined,
      startOffset: Number.isInteger(raw?.anchor?.startOffset) ? Number(raw.anchor.startOffset) : undefined,
      endOffset: Number.isInteger(raw?.anchor?.endOffset) ? Number(raw.anchor.endOffset) : undefined,
      startTextOffset: Number.isInteger(raw?.anchor?.startTextOffset) ? Number(raw.anchor.startTextOffset) : undefined,
      endTextOffset: Number.isInteger(raw?.anchor?.endTextOffset) ? Number(raw.anchor.endTextOffset) : undefined,
    },
  }
}

export async function importClips(rawItems: unknown[]): Promise<{ imported: number; total: number }> {
  const incoming = Array.isArray(rawItems) ? rawItems.map(sanitizeImportedClip).filter(Boolean) as ClipItem[] : []
  if (incoming.length === 0) return { imported: 0, total: 0 }

  const existing = await listClips()
  const merged = [...existing]
  let imported = 0

  for (const item of incoming) {
    const byId = merged.findIndex((v) => v.id === item.id)
    if (byId >= 0) {
      merged[byId] = { ...merged[byId], ...item }
      imported += 1
      continue
    }
    const fp = clipFingerprint(item)
    const dupe = merged.findIndex((v) => clipFingerprint(v) === fp)
    if (dupe >= 0) {
      merged[dupe] = { ...merged[dupe], ...item, id: merged[dupe].id }
      imported += 1
      continue
    }
    merged.unshift(item)
    imported += 1
  }

  await setClips(merged)
  return { imported, total: incoming.length }
}
