import { computed, ref } from 'vue'

import { fetchApprovalExpenseClaims, fetchArchivedExpenseClaims, fetchExpenseClaims } from '../services/reimbursements.js'
import {
  DOCUMENT_VIEWED_KEYS_CHANGE_EVENT,
  countNewDocuments,
  readViewedDocumentKeys,
  resolveDocumentNewKey
} from '../utils/documentCenterNewState.js'
import { mapExpenseClaimToRequest } from './useRequests.js'

const SOURCE_PRIORITY = {
  owned: 1,
  approval: 2,
  archive: 3
}

const documentRows = ref([])
const viewedDocumentKeys = ref(readViewedDocumentKeys())
const loading = ref(false)
let refreshTimer = null
let viewedKeysListenerAttached = false

function normalizeClaimText(...values) {
  for (const value of values) {
    const normalized = String(value || '').trim()
    if (normalized) {
      return normalized
    }
  }

  return ''
}

function buildDocumentInboxRow(claim, source) {
  const request = mapExpenseClaimToRequest(claim)
  const claimId = normalizeClaimText(request.claimId, request.id, claim?.id, claim?.claim_id)
  const documentNo = normalizeClaimText(request.documentNo, request.claimNo, request.id, claim?.claim_no)
  const documentKey = normalizeClaimText(claimId, documentNo)

  return documentKey
    ? {
        source,
        claimId: claimId || documentKey,
        documentNo,
        documentKey: `${source}:${documentKey}`
      }
    : null
}

function sourcePriority(row) {
  return SOURCE_PRIORITY[row?.source] || 0
}

function mergeNonArchivedRows(rows) {
  const rowMap = new Map()

  rows.filter(Boolean).forEach((row) => {
    const key = normalizeClaimText(row.claimId, row.documentNo, row.documentKey)
    if (!key) {
      return
    }

    const current = rowMap.get(key)
    if (!current || sourcePriority(row) >= sourcePriority(current)) {
      rowMap.set(key, row)
    }
  })

  return Array.from(rowMap.values())
}

function uniqueRowsByNewKey(rows) {
  const seenKeys = new Set()

  return rows.filter((row) => {
    const key = resolveDocumentNewKey(row)
    if (!key || seenKeys.has(key)) {
      return false
    }

    seenKeys.add(key)
    return true
  })
}

function mapClaimsToRows(claims, source) {
  return Array.isArray(claims)
    ? claims.map((claim) => buildDocumentInboxRow(claim, source)).filter(Boolean)
    : []
}

export function buildDocumentInboxRows({ ownedClaims = [], approvalClaims = [], archivedClaims = [] } = {}) {
  const ownedRows = mapClaimsToRows(ownedClaims, 'owned')
  const approvalRows = mapClaimsToRows(approvalClaims, 'approval')
  const archiveRows = mapClaimsToRows(archivedClaims, 'archive')

  return uniqueRowsByNewKey([
    ...mergeNonArchivedRows([...ownedRows, ...approvalRows]),
    ...archiveRows
  ])
}

function refreshViewedDocumentKeys() {
  viewedDocumentKeys.value = readViewedDocumentKeys()
}

function attachViewedKeysListener() {
  if (typeof window === 'undefined' || viewedKeysListenerAttached) {
    return
  }

  window.addEventListener(DOCUMENT_VIEWED_KEYS_CHANGE_EVENT, refreshViewedDocumentKeys)
  viewedKeysListenerAttached = true
}

async function readClaimList(fetcher) {
  const result = await fetcher()
  return Array.isArray(result) ? result : []
}

export function useDocumentCenterInbox() {
  attachViewedKeysListener()

  const unreadCount = computed(() => countNewDocuments(documentRows.value, viewedDocumentKeys.value))
  const hasUnread = computed(() => unreadCount.value > 0)

  async function refreshDocumentInbox() {
    loading.value = true

    try {
      const [ownedResult, approvalResult, archiveResult] = await Promise.allSettled([
        readClaimList(fetchExpenseClaims),
        readClaimList(fetchApprovalExpenseClaims),
        readClaimList(fetchArchivedExpenseClaims)
      ])

      documentRows.value = buildDocumentInboxRows({
        ownedClaims: ownedResult.status === 'fulfilled' ? ownedResult.value : [],
        approvalClaims: approvalResult.status === 'fulfilled' ? approvalResult.value : [],
        archivedClaims: archiveResult.status === 'fulfilled' ? archiveResult.value : []
      })
      refreshViewedDocumentKeys()
    } finally {
      loading.value = false
    }
  }

  function startDocumentInboxPolling(intervalMs = 45000) {
    stopDocumentInboxPolling()

    if (typeof window === 'undefined') {
      return
    }

    refreshTimer = window.setInterval(() => {
      void refreshDocumentInbox()
    }, intervalMs)
  }

  function stopDocumentInboxPolling() {
    if (refreshTimer && typeof window !== 'undefined') {
      window.clearInterval(refreshTimer)
      refreshTimer = null
    }
  }

  return {
    hasUnread,
    loading,
    refreshDocumentInbox,
    startDocumentInboxPolling,
    stopDocumentInboxPolling,
    unreadCount
  }
}
