import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import {
  listWorkCategories,
  listWorkerCompletionLeaderboard,
  listWorkOrderSharesByOrderIds,
  listWorkOrders,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { normalizePage, normalizePageSize } from '../admin/admin-query-utils.js'
import {
  mapWorkCategory,
  mapWorkOrderForWorker,
  mapWorkOrderShare,
  normalizeOptionalId,
  resolveWorkerPermissions,
} from './mappers.js'
import { getWorkerHallConfig } from './worker-hall-config-service.js'
import {
  getRequiredWorker,
  requireActiveWorkerSession,
  type WorkerSession,
} from './worker-session-context-service.js'
import { WorkerListCache } from './worker-list-cache.js'

const HALL_LIST_CACHE_TTL_MS = 2_000
const hallListCache = new WorkerListCache(HALL_LIST_CACHE_TTL_MS)

export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
  requireActiveWorkerSession(session)
  const page = normalizePage(query.page)
  const pageSize = normalizePageSize(query.pageSize)
  const categoryId = normalizeOptionalId(query.categoryId)
  const worker = await getRequiredWorker(session.workerId)
  const permissions = resolveWorkerPermissions(worker)
  const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
  const hallConfig = getWorkerHallConfig()
  const keyword = String(query.keyword || '').trim()
  const cacheKey = JSON.stringify([worker.id, page, pageSize, keyword, categoryId || 0])
  return hallListCache.getOrSet(cacheKey, async () => {
    const visibleAfterIso =
      visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
    const { items, total } = await listWorkOrders({
      page,
      pageSize,
      statuses: [WORK_ORDER_STATUS.OPEN],
      keyword,
      categoryId: categoryId || 0,
      hallCandidateLimit: hallConfig.maxVisibleOrders,
      hallCategoryLimits: hallConfig.categoryMaxVisibleOrders,
      visibleAfterIso,
      excludeFilledSharing: true,
      pinnedFirst: true,
    })
    const categories = (await listWorkCategories()).map(mapWorkCategory)
    const orderIds = items.map((item) => Number(item.id))
    const shares = await listWorkOrderSharesByOrderIds(orderIds)
    const myShareByOrderId = new Map(
      shares
        .filter(
          (share) => share.status !== 'cancelled' && Number(share.worker_id) === Number(worker.id),
        )
        .map((share) => [Number(share.work_order_id), share]),
    )
    const sharingProgressByOrderId = new Map<
      number,
      { joinedQuantity: number; pendingSubmissionCount: number }
    >()
    for (const share of shares) {
      if (share.status === 'cancelled') continue
      const orderId = Number(share.work_order_id)
      const progress = sharingProgressByOrderId.get(orderId) || {
        joinedQuantity: 0,
        pendingSubmissionCount: 0,
      }
      progress.joinedQuantity += Number(share.quantity || 0)
      if (share.status === 'joined') progress.pendingSubmissionCount += 1
      sharingProgressByOrderId.set(orderId, progress)
    }
    return {
      items: items.map((item) => {
        const myShare = myShareByOrderId.get(Number(item.id))
        return {
          ...mapWorkOrderForWorker(item, permissions),
          myShare: myShare ? mapWorkOrderShare(myShare) : null,
          sharingProgress: sharingProgressByOrderId.get(Number(item.id)) || {
            joinedQuantity: 0,
            pendingSubmissionCount: 0,
          },
        }
      }),
      pagination: { page, pageSize, total },
      categories,
      visibleDelaySeconds,
    }
  })
}

export async function getWorkerHallLeaderboard(session: WorkerSession, period: unknown = 'all') {
  requireActiveWorkerSession(session)
  const leaderboardPeriod = period === 'daily' ? 'daily' : 'all'
  const entries = await listWorkerCompletionLeaderboard({
    limit: 10,
    currentWorkerId: session.workerId,
    period: leaderboardPeriod,
  })
  const current = entries.find((entry) => entry.workerId === session.workerId)
  return {
    items: entries
      .filter((entry) => entry.rank <= 10)
      .map((entry) => ({
        ...entry,
        displayName: maskWorkerLeaderboardDisplayName(entry.displayName),
      })),
    current: {
      rank: current?.rank || null,
      acceptedOrderCount: current?.acceptedOrderCount || 0,
    },
  }
}

export function maskWorkerLeaderboardDisplayName(value: unknown): string {
  const firstCharacter = Array.from(String(value || '').trim())[0]
  return firstCharacter ? `${firstCharacter}****` : '****'
}
