const STORAGE_KEY = 'x-financial.documents.viewed'
const SCOPE_STORAGE_KEY = 'x-financial.documents.scope'
export const DOCUMENT_VIEWED_KEYS_CHANGE_EVENT = 'x-financial.documents.viewed-change'

function getStorage() {
  return typeof window === 'undefined' ? null : window.localStorage
}

export function resolveDocumentNewKey(row) {
  const source = String(row?.source || 'document').trim()
  const id = String(row?.claimId || row?.documentNo || row?.documentKey || row?.id || '').trim()
  return id ? `${source}:${id}` : ''
}

export function resolveDocumentNotificationKey(row) {
  const documentKey = String(row?.documentKey || '').trim()
  return documentKey || resolveDocumentNewKey(row)
}

export function resolveDocumentNotificationId(row) {
  const key = resolveDocumentNotificationKey(row)
  return key ? `document:${key}` : ''
}

export function readViewedDocumentKeys(storage = getStorage()) {
  if (!storage) {
    return new Set()
  }

  try {
    const parsed = JSON.parse(storage.getItem(STORAGE_KEY) || '[]')
    return new Set(Array.isArray(parsed) ? parsed.map((item) => String(item || '').trim()).filter(Boolean) : [])
  } catch {
    return new Set()
  }
}

export function writeViewedDocumentKeys(keys, storage = getStorage()) {
  if (!storage) {
    return
  }

  storage.setItem(STORAGE_KEY, JSON.stringify(Array.from(keys).filter(Boolean)))

  if (typeof window !== 'undefined' && storage === window.localStorage) {
    window.dispatchEvent(new CustomEvent(DOCUMENT_VIEWED_KEYS_CHANGE_EVENT))
  }
}

export function readDocumentScope(fallback, allowedScopes = [], storage = getStorage()) {
  if (!storage) {
    return fallback
  }

  const storedScope = String(storage.getItem(SCOPE_STORAGE_KEY) || '').trim()
  return allowedScopes.includes(storedScope) ? storedScope : fallback
}

export function writeDocumentScope(scope, allowedScopes = [], storage = getStorage()) {
  if (!storage || !allowedScopes.includes(scope)) {
    return
  }

  storage.setItem(SCOPE_STORAGE_KEY, scope)
}

export function isNewDocument(row, viewedKeys) {
  if (row?.isNewDocument === false || row?.archived === true || String(row?.source || '').trim() === 'archive') {
    return false
  }
  const key = resolveDocumentNewKey(row)
  return Boolean(key) && !viewedKeys.has(key)
}

export function countNewDocuments(rows, viewedKeys) {
  return rows.filter((row) => isNewDocument(row, viewedKeys)).length
}

function resolveRemoteStateId(item) {
  return String(item?.notification_id || item?.notificationId || '').trim()
}

function isRemoteStateViewed(item) {
  return Boolean(item?.read_at || item?.readAt || item?.hidden_at || item?.hiddenAt)
}

export function mergeNotificationStatesIntoViewedDocumentKeys(states, viewedKeys, storage = getStorage()) {
  const nextKeys = new Set(viewedKeys)
  let changed = false

  ;(Array.isArray(states) ? states : []).forEach((item) => {
    if (!isRemoteStateViewed(item)) {
      return
    }

    const id = resolveRemoteStateId(item)
    if (!id.startsWith('document:')) {
      return
    }

    const key = id.slice('document:'.length).trim()
    if (key && !nextKeys.has(key)) {
      nextKeys.add(key)
      changed = true
    }
  })

  if (changed) {
    writeViewedDocumentKeys(nextKeys, storage)
  }

  return nextKeys
}

export function buildDocumentViewedStatePatch(row) {
  if (!isNewDocument(row, new Set())) {
    return null
  }

  const notificationId = resolveDocumentNotificationId(row)
  if (!notificationId) {
    return null
  }

  return {
    notification_id: notificationId,
    read: true,
    hidden: false,
    context_json: {
      kind: 'document',
      source: String(row?.source || '').trim(),
      target_type: 'documents-center'
    }
  }
}

export function buildDocumentsViewedStatePatches(rows, viewedKeys) {
  const seenIds = new Set()

  return (Array.isArray(rows) ? rows : [])
    .filter((row) => isNewDocument(row, viewedKeys))
    .map(buildDocumentViewedStatePatch)
    .filter((patch) => {
      if (!patch?.notification_id || seenIds.has(patch.notification_id)) {
        return false
      }

      seenIds.add(patch.notification_id)
      return true
    })
}

export function markDocumentViewed(row, viewedKeys, storage = getStorage()) {
  const key = resolveDocumentNewKey(row)
  if (!key) {
    return viewedKeys
  }

  const nextKeys = new Set(viewedKeys)
  nextKeys.add(key)
  writeViewedDocumentKeys(nextKeys, storage)
  return nextKeys
}

export function markDocumentsViewed(rows, viewedKeys, storage = getStorage()) {
  const nextKeys = new Set(viewedKeys)
  let changed = false

  ;(Array.isArray(rows) ? rows : []).forEach((row) => {
    if (!isNewDocument(row, nextKeys)) {
      return
    }

    const key = resolveDocumentNewKey(row)
    if (key) {
      nextKeys.add(key)
      changed = true
    }
  })

  if (changed) {
    writeViewedDocumentKeys(nextKeys, storage)
  }

  return nextKeys
}
