/**
 * Global floating browser — persists tabs/history to MySQL via /api/browser/state.
 */
import { computed, ref } from 'vue'
import { http } from '@/api'
import { normalizeBrowserUrl } from '@/utils/browserUrl'
import type { FloatRect } from '@/composables/useFloatingPanel'

const LEGACY_HISTORY_KEY = 'nexus_browser_history_v1'
const SAVE_DEBOUNCE_MS = 700

export interface BrowserVisit {
  url: string
  title: string
  at: string
}

export interface BrowserTab {
  id: string
  url: string
  title: string
  stack: string[]
  stackIndex: number
}

export interface BrowserStateResponse {
  url_history: string[]
  visits: BrowserVisit[]
  tabs: Array<{
    id: string
    url: string
    title?: string
    stack?: string[]
    stack_index?: number
  }>
  active_tab_id: string | null
  minimized: boolean
  rect: FloatRect | null
  minimized_rect: FloatRect | null
}

const bootstrapped = ref(false)
const saving = ref(false)
const panelOpen = ref(false)
const minimized = ref(false)
const maximized = ref(false)
const lastError = ref<string | null>(null)
const addressDraft = ref('')
const urlHistory = ref<string[]>([])
const visits = ref<BrowserVisit[]>([])
const tabs = ref<BrowserTab[]>([])
const activeTabId = ref<string | null>(null)
const panelRect = ref<FloatRect | null>(null)
const minimizedRect = ref<FloatRect | null>(null)

let saveTimer: ReturnType<typeof setTimeout> | null = null

function tabLabel(tab: BrowserTab): string {
  try {
    return tab.title || new URL(tab.url).hostname
  } catch {
    return tab.title || tab.url
  }
}

function newTabId(): string {
  return crypto.randomUUID()
}

function serializeTabs(): BrowserStateResponse['tabs'] {
  return tabs.value.map((t) => ({
    id: t.id,
    url: t.url,
    title: t.title,
    stack: t.stack,
    stack_index: t.stackIndex,
  }))
}

function applyServerState(data: BrowserStateResponse) {
  urlHistory.value = data.url_history || []
  visits.value = (data.visits || []).map((v) => ({
    url: v.url,
    title: v.title || v.url,
    at: v.at,
  }))
  tabs.value = (data.tabs || []).map((t) => ({
    id: t.id,
    url: t.url,
    title: t.title || t.url,
    stack: t.stack?.length ? t.stack : (t.url ? [t.url] : []),
    stackIndex: typeof t.stack_index === 'number' ? t.stack_index : (t.stack?.length ?? 1) - 1,
  }))
  activeTabId.value = data.active_tab_id
  if (!activeTabId.value && tabs.value.length) {
    activeTabId.value = tabs.value[0]!.id
  }
  minimized.value = Boolean(data.minimized)
  panelRect.value = data.rect
  minimizedRect.value = data.minimized_rect
  panelOpen.value = tabs.value.length > 0 && !minimized.value
  syncAddressFromActive()
}

function syncAddressFromActive() {
  const tab = activeTab.value
  addressDraft.value = tab?.url ?? ''
}

const activeTab = computed(() => tabs.value.find((t) => t.id === activeTabId.value) ?? null)

const isActive = computed(() => tabs.value.length > 0 || panelOpen.value)

const canGoBack = computed(() => {
  const tab = activeTab.value
  return tab ? tab.stackIndex > 0 : false
})

const canGoForward = computed(() => {
  const tab = activeTab.value
  return tab ? tab.stackIndex < tab.stack.length - 1 : false
})

function scheduleSave(extra?: { recordUrl?: string; recordTitle?: string }) {
  if (saveTimer) clearTimeout(saveTimer)
  saveTimer = setTimeout(() => {
    void persistState(extra)
  }, SAVE_DEBOUNCE_MS)
}

async function persistState(extra?: { recordUrl?: string; recordTitle?: string }) {
  saving.value = true
  try {
    const body: Record<string, unknown> = {
      tabs: serializeTabs(),
      active_tab_id: activeTabId.value,
      minimized: minimized.value,
      rect: panelRect.value,
      minimized_rect: minimizedRect.value,
    }
    if (extra?.recordUrl) {
      body.record_url = extra.recordUrl
      if (extra.recordTitle) body.record_title = extra.recordTitle
    } else {
      body.url_history = urlHistory.value
      body.visits = visits.value
    }
    const res = await http.put<BrowserStateResponse>('/browser/state', body)
    applyServerState(res)
  } catch {
    /* keep local state */
  } finally {
    saving.value = false
  }
}

async function migrateLegacyHistory() {
  try {
    const raw = sessionStorage.getItem(LEGACY_HISTORY_KEY)
    if (!raw) return
    const parsed = JSON.parse(raw) as unknown
    if (!Array.isArray(parsed)) return
    const urls = parsed.filter((u): u is string => typeof u === 'string' && !!normalizeBrowserUrl(u))
    if (!urls.length) return
    for (const url of [...urls].reverse()) {
      const normalized = normalizeBrowserUrl(url)
      if (normalized) {
        await persistState({ recordUrl: normalized, recordTitle: normalized })
      }
    }
    sessionStorage.removeItem(LEGACY_HISTORY_KEY)
  } catch {
    /* ignore */
  }
}

async function bootstrap() {
  if (bootstrapped.value) return
  try {
    const res = await http.get<BrowserStateResponse>('/browser/state')
    applyServerState(res)
    if (!visits.value.length && !urlHistory.value.length) {
      await migrateLegacyHistory()
    }
  } catch {
    /* offline */
  } finally {
    bootstrapped.value = true
  }
}

function openPanel() {
  panelOpen.value = true
  minimized.value = false
  scheduleSave()
}

function minimizePanel() {
  minimized.value = true
  panelOpen.value = false
  scheduleSave()
}

function restartBrowser() {
  const tab: BrowserTab = {
    id: newTabId(),
    url: '',
    title: '新标签',
    stack: [],
    stackIndex: -1,
  }
  tabs.value = [tab]
  activeTabId.value = tab.id
  addressDraft.value = ''
  lastError.value = null
  openPanel()
  scheduleSave()
}

function openUrl(input: string, opts?: { title?: string; newTab?: boolean }) {
  const normalized = normalizeBrowserUrl(input)
  if (!normalized) {
    lastError.value = '请输入有效的 http 或 https 地址'
    return false
  }
  lastError.value = null

  if (opts?.newTab || !tabs.value.length || !activeTab.value) {
    const tab: BrowserTab = {
      id: newTabId(),
      url: normalized,
      title: opts?.title || normalized,
      stack: [normalized],
      stackIndex: 0,
    }
    tabs.value = [...tabs.value, tab].slice(-12)
    activeTabId.value = tab.id
  } else {
    const tab = activeTab.value
    const stack = tab.stack.slice(0, tab.stackIndex + 1)
    if (!stack.length || stack[stack.length - 1] !== normalized) stack.push(normalized)
    tab.url = normalized
    if (opts?.title) tab.title = opts.title
    tab.stack = stack
    tab.stackIndex = stack.length - 1
  }

  addressDraft.value = normalized
  openPanel()
  scheduleSave({ recordUrl: normalized, recordTitle: opts?.title })
  return true
}

function navigate(input: string) {
  return openUrl(input)
}

function updateActiveFromRemote(url: string, title: string) {
  const tab = activeTab.value
  if (!tab || !url) return
  tab.url = url
  if (title) tab.title = title
  addressDraft.value = url
  scheduleSave({ recordUrl: url, recordTitle: title })
}

function refreshActive() {
  /* remote reload handled in panel */
}

function goBack() {
  const tab = activeTab.value
  if (!tab || tab.stackIndex <= 0) return
  tab.stackIndex -= 1
  tab.url = tab.stack[tab.stackIndex]!
  addressDraft.value = tab.url
  scheduleSave()
}

function goForward() {
  const tab = activeTab.value
  if (!tab || tab.stackIndex >= tab.stack.length - 1) return
  tab.stackIndex += 1
  tab.url = tab.stack[tab.stackIndex]!
  addressDraft.value = tab.url
  scheduleSave()
}

function openExternal() {
  const tab = activeTab.value
  if (!tab?.url) return
  window.open(tab.url, '_blank', 'noopener,noreferrer')
}

function switchTab(id: string) {
  activeTabId.value = id
  syncAddressFromActive()
  scheduleSave()
}

function closeTab(id: string) {
  if (tabs.value.length <= 1) return
  const idx = tabs.value.findIndex((t) => t.id === id)
  if (idx < 0) return
  const next = tabs.value.filter((t) => t.id !== id)
  tabs.value = next
  if (activeTabId.value === id) {
    activeTabId.value = next[idx]?.id ?? next[idx - 1]?.id ?? null
  }
  syncAddressFromActive()
  scheduleSave()
}

function newEmptyTab() {
  const tab: BrowserTab = {
    id: newTabId(),
    url: '',
    title: '新标签',
    stack: [],
    stackIndex: -1,
  }
  tabs.value = [...tabs.value, tab].slice(-12)
  activeTabId.value = tab.id
  addressDraft.value = ''
  openPanel()
  scheduleSave()
}

function openFromHistory(url: string) {
  openUrl(url)
}

function savePanelRect(rect: FloatRect) {
  panelRect.value = rect
  scheduleSave()
}

function saveMinimizedRect(rect: FloatRect) {
  minimizedRect.value = rect
  scheduleSave()
}

export function useGlobalBrowser() {
  return {
    bootstrapped,
    saving,
    panelOpen,
    minimized,
    maximized,
    lastError,
    addressDraft,
    urlHistory,
    visits,
    tabs,
    activeTabId,
    activeTab,
    panelRect,
    minimizedRect,
    isActive,
    canGoBack,
    canGoForward,
    tabLabel,
    bootstrap,
    openPanel,
    minimizePanel,
    restartBrowser,
    openUrl,
    navigate,
    updateActiveFromRemote,
    refreshActive,
    goBack,
    goForward,
    openExternal,
    switchTab,
    closeTab,
    newEmptyTab,
    openFromHistory,
    savePanelRect,
    saveMinimizedRect,
    scheduleSave,
  }
}
