import { computed, ref } from 'vue'

import {
  REIMBURSEMENT_LIST_PREVIEW_PARAMS,
  extractExpenseClaimItems,
  fetchApprovalExpenseClaims,
  fetchArchivedExpenseClaims,
  fetchExpenseClaims
} from '../services/reimbursements.js'
import { fetchNotificationStates } from '../services/notificationStates.js'
import {
  DOCUMENT_VIEWED_KEYS_CHANGE_EVENT,
  countNewDocuments,
  isNewDocument,
  markDocumentViewed,
  markDocumentsViewed,
  mergeNotificationStatesIntoViewedDocumentKeys,
  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)
const INBOX_CACHE_TTL_MS = 30000
let refreshTimer = null
let refreshPromise = null
let lastRefreshAt = 0
let viewedKeysListenerAttached = false

const SOURCE_LABELS = {
  owned: '我的单据',
  approval: '待我处理',
  archive: '归档单据'
}

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

  return ''
}

function resolveSortTime(...values) {
  for (const value of values) {
    const time = Date.parse(String(value || '').trim())
    if (Number.isFinite(time)) {
      return time
    }
  }

  return 0
}

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)
  const createdAt = normalizeClaimText(request.createdAt, request.submittedAt, claim?.created_at, claim?.submitted_at)
  const updatedAt = normalizeClaimText(request.updatedAt, request.approvedAt, claim?.updated_at, claim?.approved_at)
  const documentTypeLabel = normalizeClaimText(request.documentTypeLabel, claim?.document_type_label) || '报销单'

  return documentKey
    ? {
        source,
        claimId: claimId || documentKey,
        documentNo,
        documentKey: `${source}:${documentKey}`,
        documentTypeLabel,
        sourceLabel: SOURCE_LABELS[source] || '单据中心',
        title: normalizeClaimText(claim?.title, request.title, request.sceneLabel, request.note) || documentTypeLabel,
        initiatorName: normalizeClaimText(request.initiatorName, request.person, claim?.employee_name, claim?.applicant_name),
        statusLabel: normalizeClaimText(request.approvalStatus, request.status, claim?.approval_status, claim?.status),
        createdAt,
        updatedAt,
        sortTime: resolveSortTime(updatedAt, createdAt),
        rawRequest: request
      }
    : 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()
}

async function refreshRemoteViewedDocumentKeys() {
  try {
    viewedDocumentKeys.value = mergeNotificationStatesIntoViewedDocumentKeys(
      await fetchNotificationStates(),
      readViewedDocumentKeys()
    )
  } catch {
    refreshViewedDocumentKeys()
  }
}

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(REIMBURSEMENT_LIST_PREVIEW_PARAMS)
  return extractExpenseClaimItems(result)
}

export function useDocumentCenterInbox() {
  attachViewedKeysListener()

  const unreadCount = computed(() => countNewDocuments(documentRows.value, viewedDocumentKeys.value))
  const hasUnread = computed(() => unreadCount.value > 0)
  const notificationRows = computed(() =>
    documentRows.value
      .filter((row) => String(row?.source || '').trim() !== 'archive')
      .map((row) => ({
        ...row,
        isUnread: isNewDocument(row, viewedDocumentKeys.value)
      }))
      .sort((left, right) => Number(right.sortTime || 0) - Number(left.sortTime || 0))
  )

  function markDocumentInboxRowRead(row) {
    viewedDocumentKeys.value = markDocumentViewed(row, viewedDocumentKeys.value)
    return viewedDocumentKeys.value
  }

  function markDocumentInboxRowsRead(rows = documentRows.value) {
    viewedDocumentKeys.value = markDocumentsViewed(rows, viewedDocumentKeys.value)
    return viewedDocumentKeys.value
  }

  async function refreshDocumentInbox(options = {}) {
    const force = Boolean(options.force)
    const now = Date.now()

    if (refreshPromise) {
      return refreshPromise
    }

    if (!force && lastRefreshAt && now - lastRefreshAt < INBOX_CACHE_TTL_MS) {
      refreshViewedDocumentKeys()
      return documentRows.value
    }

    loading.value = true

    refreshPromise = (async () => {
      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 : []
      })
      await refreshRemoteViewedDocumentKeys()
      lastRefreshAt = Date.now()

      return documentRows.value
    })()

    try {
      return await refreshPromise
    } finally {
      loading.value = false
      refreshPromise = null
    }
  }

  function startDocumentInboxPolling(intervalMs = 120000) {
    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,
    markDocumentInboxRowRead,
    markDocumentInboxRowsRead,
    notificationRows,
    refreshDocumentInbox,
    startDocumentInboxPolling,
    stopDocumentInboxPolling,
    unreadCount
  }
}
