import { createLogbook } from './logbook.js'
import { db } from './db.js'
import { getActiveMasterKey } from './auth.js'
import { getLogbookKey } from './logbookKeys.js'
import { encryptJson } from './crypto.js'
import { syncLogbook } from './sync.js'
import { putEntryRecord } from '../utils/entryListCache.js'
import { syncPersonPool } from './personPoolSync.js'
import i18n from '../i18n/index.js'
import type { PersonData } from '../types/person.js'
import { buildLogbookCrewSelection } from '../utils/personSnapshots.js'
import {
  buildDemoPersonPool,
  buildDemoEntryPayloads,
  buildDemoYachtData
} from './demoLogbookData.js'

export const SEED_DEMO_FLAG = 'seed_demo_logbook'

export function getDemoLogbookStorageKey(userId: string): string {
  return `demo_logbook_id_${userId}`
}

export function getDemoFirstEntryStorageKey(userId: string): string {
  return `demo_first_entry_id_${userId}`
}

async function putEncryptedRecord(
  logbookId: string,
  key: ArrayBuffer,
  type: 'entry' | 'yacht' | 'gpsTrack' | 'logbookCrew',
  payloadId: string,
  data: unknown,
  now: string
): Promise<void> {
  const encrypted = await encryptJson(data, key)

  if (type === 'entry') {
    await putEntryRecord(
      {
        payloadId,
        logbookId,
        encryptedData: encrypted.ciphertext,
        iv: encrypted.iv,
        tag: encrypted.tag,
        updatedAt: now
      },
      data as Record<string, unknown>
    )
  } else if (type === 'yacht') {
    await db.yachts.put({
      logbookId,
      encryptedData: encrypted.ciphertext,
      iv: encrypted.iv,
      tag: encrypted.tag,
      updatedAt: now
    })
  } else if (type === 'gpsTrack') {
    await db.gpsTracks.put({
      entryId: payloadId,
      logbookId,
      encryptedData: encrypted.ciphertext,
      iv: encrypted.iv,
      tag: encrypted.tag,
      updatedAt: now
    })
  } else if (type === 'logbookCrew') {
    await db.logbookCrewSelections.put({
      logbookId,
      encryptedData: encrypted.ciphertext,
      iv: encrypted.iv,
      tag: encrypted.tag,
      updatedAt: now
    })
  }

  await db.syncQueue.put({
    action: type === 'yacht' || type === 'logbookCrew' ? 'update' : 'create',
    type,
    payloadId: type === 'yacht' || type === 'logbookCrew' ? logbookId : payloadId,
    logbookId,
    data: JSON.stringify(encrypted),
    updatedAt: now
  })
}

async function seedPersonPool(masterKey: ArrayBuffer, now: string): Promise<Map<string, PersonData>> {
  const poolMap = new Map<string, PersonData>()
  for (const person of buildDemoPersonPool()) {
    poolMap.set(person.payloadId, person.data)
    const encrypted = await encryptJson(person.data, masterKey)
    await db.personPool.put({
      payloadId: person.payloadId,
      encryptedData: encrypted.ciphertext,
      iv: encrypted.iv,
      tag: encrypted.tag,
      updatedAt: now
    })
    await db.userSyncQueue.put({
      action: 'create',
      type: 'person',
      payloadId: person.payloadId,
      data: JSON.stringify(encrypted),
      updatedAt: now
    })
  }
  syncPersonPool().catch((err) => console.warn('Demo person pool sync failed:', err))
  return poolMap
}

async function seedYachtAndCrew(logbookId: string, key: ArrayBuffer, now: string): Promise<void> {
  const masterKey = getActiveMasterKey()
  if (!masterKey) throw new Error('Encryption key not available')

  const yachtData = buildDemoYachtData()
  await putEncryptedRecord(logbookId, key, 'yacht', logbookId, yachtData, now)

  const poolMap = await seedPersonPool(masterKey, now)
  const skipperId = [...poolMap.entries()].find(([, d]) => d.role === 'skipper')?.[0] ?? null
  const crewIds = [...poolMap.entries()].filter(([, d]) => d.role === 'crew').map(([id]) => id)
  const selection = buildLogbookCrewSelection(skipperId, crewIds, poolMap)
  await putEncryptedRecord(logbookId, key, 'logbookCrew', logbookId, selection, now)
}

export interface DemoSeedResult {
  logbookId: string
  title: string
  firstEntryId: string
}

export async function seedDemoLogbookIfNeeded(): Promise<DemoSeedResult | null> {
  const userId = localStorage.getItem('active_userid')
  if (!userId || !getActiveMasterKey()) return null

  const shouldSeed = sessionStorage.getItem(SEED_DEMO_FLAG) === '1'
  const existingId = localStorage.getItem(getDemoLogbookStorageKey(userId))

  if (existingId) {
    const existing = await db.logbooks.get(existingId)
    if (existing) {
      if (shouldSeed) sessionStorage.removeItem(SEED_DEMO_FLAG)
      const firstEntryId = localStorage.getItem(getDemoFirstEntryStorageKey(userId)) || ''
      const title = i18n.t('demo.logbook_title')
      return { logbookId: existingId, title, firstEntryId }
    }
    clearDemoLogbookRefs(userId, existingId)
  }

  if (!shouldSeed) return null
  sessionStorage.removeItem(SEED_DEMO_FLAG)

  const title = i18n.t('demo.logbook_title')
  const logbook = await createLogbook(title)
  const logbookId = logbook.id

  await db.logbooks.update(logbookId, { isDemo: 1 })
  localStorage.setItem(getDemoLogbookStorageKey(userId), logbookId)

  const key = (await getLogbookKey(logbookId)) || getActiveMasterKey()
  if (!key) throw new Error('Encryption key not available for demo seed')

  const now = new Date().toISOString()
  await seedYachtAndCrew(logbookId, key, now)

  const entryPayloads = buildDemoEntryPayloads()
  let firstEntryId = ''

  for (const { entryId, entryPayload, trackData } of entryPayloads) {
    if (!firstEntryId) firstEntryId = entryId
    await putEncryptedRecord(logbookId, key, 'entry', entryId, entryPayload, now)
    await putEncryptedRecord(logbookId, key, 'gpsTrack', entryId, trackData, now)
  }

  localStorage.setItem(getDemoFirstEntryStorageKey(userId), firstEntryId)
  syncLogbook(logbookId).catch((err) => console.warn('Demo logbook sync failed:', err))

  return { logbookId, title, firstEntryId }
}

export function getStoredDemoLogbookId(): string | null {
  const userId = localStorage.getItem('active_userid')
  if (!userId) return null
  return localStorage.getItem(getDemoLogbookStorageKey(userId))
}

export function getStoredDemoFirstEntryId(): string | null {
  const userId = localStorage.getItem('active_userid')
  if (!userId) return null
  return localStorage.getItem(getDemoFirstEntryStorageKey(userId))
}

/** Remove persisted demo logbook pointers when the logbook no longer exists. */
export function clearDemoLogbookRefs(userId: string, logbookId?: string): void {
  const storedId = localStorage.getItem(getDemoLogbookStorageKey(userId))
  if (logbookId && storedId && storedId !== logbookId) return
  localStorage.removeItem(getDemoLogbookStorageKey(userId))
  localStorage.removeItem(getDemoFirstEntryStorageKey(userId))
}

export async function entryExistsInLogbook(logbookId: string, entryId: string): Promise<boolean> {
  const entry = await db.entries.get(entryId)
  return entry?.logbookId === logbookId
}

export interface TourLogbookContext {
  logbookId: string
  title: string
  firstEntryId: string | null
}

/** Pick a logbook + first entry for the onboarding tour (handles deleted demo data). */
export async function resolveTourLogbookContext(
  preferLogbookId?: string | null
): Promise<TourLogbookContext | null> {
  const userId = localStorage.getItem('active_userid')
  if (!userId || !getActiveMasterKey()) return null

  const demoId = localStorage.getItem(getDemoLogbookStorageKey(userId))
  if (demoId && !(await db.logbooks.get(demoId))) {
    clearDemoLogbookRefs(userId, demoId)
  }

  const { fetchLogbooks } = await import('./logbook.js')
  const books = await fetchLogbooks()
  if (books.length === 0) return null

  const activeId = localStorage.getItem('active_logbook_id')
  const pick =
    (preferLogbookId ? books.find((b) => b.id === preferLogbookId) : undefined) ??
    (activeId ? books.find((b) => b.id === activeId) : undefined) ??
    (demoId ? books.find((b) => b.id === demoId) : undefined) ??
    books[0]

  const firstEntryId = await resolveTourFirstEntryId(pick.id, userId)
  return { logbookId: pick.id, title: pick.title, firstEntryId }
}

async function resolveTourFirstEntryId(logbookId: string, userId: string): Promise<string | null> {
  const stored = localStorage.getItem(getDemoFirstEntryStorageKey(userId))
  if (stored && (await entryExistsInLogbook(logbookId, stored))) {
    return stored
  }

  if (stored) {
    localStorage.removeItem(getDemoFirstEntryStorageKey(userId))
  }

  const localEntries = await db.entries.where({ logbookId }).toArray()
  if (localEntries.length === 0) return null

  localEntries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
  return localEntries[0]?.payloadId ?? null
}
