import {
  cmdGetUserInfo,
  cmdListMatterStatuses,
  cmdListMatterTypes,
  cmdListMatters,
  cmdListProjects,
  cmdUpdateMatter,
  extractMatterList,
} from './geelib-api.mjs'

/** 轮询仅拉取「未开始」任务。 */
const POLL_TARGET_STATUS_NAME = '未开始'

/** dispatch 前将任务流转到该状态，表示 agent 已接手。 */
const IN_PROGRESS_STATUS_NAME = '处理中'

/** agent 处理完成后流转到该状态。 */
const SELF_TEST_STATUS_NAME = '自测&联调中'

function extractMatterId(row) {
  const id = row.id ?? row.matter_id
  if (typeof id === 'number' && Number.isFinite(id)) {
    return id
  }
  return null
}

function extractStatusName(row) {
  return (row.status_name ?? row.matterStatus?.status_name ?? '').trim()
}

function extractStatusId(row) {
  const sid = row.matter_status ?? row.status ?? row.matterStatus?.id
  if (typeof sid === 'number' && Number.isFinite(sid)) {
    return sid
  }
  return null
}

/** 判断状态名是否为轮询目标（未开始）。 */
function isUnstartedStatusName(name) {
  return (name ?? '').trim() === POLL_TARGET_STATUS_NAME
}

/** 判断状态名是否为 dispatch 目标（处理中）。 */
function isInProgressStatusName(name) {
  return (name ?? '').trim() === IN_PROGRESS_STATUS_NAME
}

/** 判断状态名是否为完成后目标（自测&联调中）。 */
function isSelfTestStatusName(name) {
  return (name ?? '').trim() === SELF_TEST_STATUS_NAME
}

/**
 * 解析 GEELIB_SUB_ID：支持逗号、分号、空格分隔的多个项目 ID。
 * 未配置时返回空数组，由调用方决定是否回退到首个可访问项目。
 */
export function parseGeelibSubIds(raw = process.env.GEELIB_SUB_ID) {
  if (raw === undefined || raw === null) {
    return []
  }
  const text = String(raw).trim()
  if (text.length === 0) {
    return []
  }
  const ids = []
  const seen = new Set()
  for (const part of text.split(/[,;\s]+/)) {
    const n = Number(part.trim())
    if (!Number.isFinite(n) || n <= 0 || seen.has(n)) {
      continue
    }
    seen.add(n)
    ids.push(n)
  }
  return ids
}

/** 两个 sub_id 列表是否一致（顺序无关）。 */
function sameSubIdList(a, b) {
  if (a.length !== b.length) {
    return false
  }
  const sa = [...a].sort((x, y) => x - y)
  const sb = [...b].sort((x, y) => x - y)
  return sa.every((v, i) => v === sb[i])
}

/**
 * 为单个项目发现 type_id、未开始状态 ID、完成后状态 ID。
 */
async function discoverProjectConfig(subId) {
  const types = await cmdListMatterTypes(subId)
  if (types.errno !== 2000 || !types.data?.length) {
    throw new Error(`list-matter-types(sub_id=${subId}) 无数据`)
  }
  const taskType =
    types.data.find(t => (t.type_name ?? t.name ?? '').includes('任务')) ??
    types.data[0]
  const typeId = taskType.id ?? taskType.type_id ?? null
  if (typeId === null) {
    throw new Error(`无法从类型列表解析 type_id，sub_id=${subId}`)
  }

  const statuses = await cmdListMatterStatuses(subId, typeId)
  if (statuses.errno !== 2000 || !statuses.data?.length) {
    throw new Error(`list-matter-statuses 无数据，sub_id=${subId}`)
  }

  const pollStatusIds = []
  let inProgressStatusId = null
  let selfTestStatusId = null
  for (const s of statuses.data) {
    const sid = s.status_id ?? s.matterStatus?.id ?? s.id
    const name = s.matterStatus?.status_name ?? s.name ?? ''
    if (sid === undefined) {
      continue
    }
    if (isUnstartedStatusName(name)) {
      pollStatusIds.push(sid)
    }
    if (isInProgressStatusName(name) && inProgressStatusId === null) {
      inProgressStatusId = sid
    }
    if (isSelfTestStatusName(name) && selfTestStatusId === null) {
      selfTestStatusId = sid
    }
  }

  if (pollStatusIds.length === 0) {
    throw new Error(
      `未找到轮询目标状态「${POLL_TARGET_STATUS_NAME}」，sub_id=${subId} type_id=${typeId}`,
    )
  }
  if (inProgressStatusId === null) {
    throw new Error(
      `未找到 dispatch 目标状态「${IN_PROGRESS_STATUS_NAME}」，sub_id=${subId} type_id=${typeId}`,
    )
  }
  if (selfTestStatusId === null) {
    throw new Error(
      `未找到完成后目标状态「${SELF_TEST_STATUS_NAME}」，sub_id=${subId} type_id=${typeId}`,
    )
  }

  return {
    sub_id: subId,
    type_id: typeId,
    poll_status_ids: pollStatusIds,
    in_progress_status_id: inProgressStatusId,
    self_test_status_id: selfTestStatusId,
  }
}

/**
 * 解析本轮 poll 要扫描的项目列表。
 * 优先 GEELIB_SUB_ID；未配置时仅取第一个可访问项目。
 */
async function resolvePollSubIds(configuredSubIds) {
  if (configuredSubIds.length > 0) {
    return configuredSubIds
  }
  const projects = await cmdListProjects('')
  const projectList = Array.isArray(projects.data)
    ? projects.data
    : projects.data?.list
  if (projects.errno !== 2000 || !projectList?.length) {
    throw new Error('list-projects 未返回可用项目')
  }
  const first = projectList[0]
  const subId = first.id ?? first.sub_id ?? null
  if (subId === null) {
    throw new Error('无法从项目列表解析 sub_id')
  }
  return [subId]
}

/**
 * 发现/刷新全部轮询项目配置。
 */
async function discoverAllProjects(state) {
  const configuredSubIds = parseGeelibSubIds()
  const pollSubIds = await resolvePollSubIds(configuredSubIds)
  const cachedProjects = Array.isArray(state.discovered.projects)
    ? state.discovered.projects
    : []
  const cacheValid =
    sameSubIdList(
      state.discovered.configured_sub_ids ?? [],
      configuredSubIds,
    ) &&
    pollSubIds.every(subId =>
      cachedProjects.some(
        p =>
          p.sub_id === subId &&
          p.type_id !== null &&
          p.in_progress_status_id !== null &&
          p.self_test_status_id !== null,
      ),
    )

  if (cacheValid && cachedProjects.length === pollSubIds.length) {
    return {
      configured_sub_ids: configuredSubIds,
      projects: cachedProjects.filter(p => pollSubIds.includes(p.sub_id)),
    }
  }

  const projects = []
  for (const subId of pollSubIds) {
    projects.push(await discoverProjectConfig(subId))
  }

  return {
    configured_sub_ids: configuredSubIds,
    projects,
  }
}

/**
 * 按 executor + 未开始状态拉取工作项。
 */
async function listUnstartedMatters(subId, typeId, assigneeId, pollStatusIds) {
  const pollStatusSet = new Set(pollStatusIds)
  const byId = new Map()
  for (const statusId of pollStatusIds) {
    const listResp = await cmdListMatters({
      subId,
      typeId,
      page: 1,
      pageSize: 50,
      assigneeId,
      statusId,
    })
    if (listResp.errno !== 2000) {
      continue
    }
    for (const row of extractMatterList(listResp.data)) {
      const mid = extractMatterId(row)
      if (mid === null || byId.has(mid)) {
        continue
      }
      const rowStatusId = extractStatusId(row)
      const rowStatusName = extractStatusName(row)
      if (rowStatusId !== null && !pollStatusSet.has(rowStatusId)) {
        continue
      }
      if (rowStatusId === null && !isUnstartedStatusName(rowStatusName)) {
        continue
      }
      byId.set(mid, row)
    }
  }
  return [...byId.values()]
}

/** 任务是否已在队列、执行中或已完成，下次 poll 应跳过。 */
function isMatterTracked(state, matterId) {
  const id = Number(matterId)
  if (state.processedMatterIds.includes(id)) {
    return true
  }
  if (state.notifiedMatterIds.includes(id)) {
    return true
  }
  if (state.activeMatterId === id) {
    return true
  }
  return (state.pendingQueue ?? []).some(q => Number(q.matterId) === id)
}

/**
 * 根据 sub_id 从 poller state 取该项目「处理中」状态 ID。
 */
export function resolveInProgressStatusId(state, subId) {
  const projects = state.discovered?.projects ?? []
  const project = projects.find(p => p.sub_id === Number(subId))
  return project?.in_progress_status_id ?? null
}

/**
 * 根据 sub_id 从 poller state 取该项目完成后状态 ID。
 */
export function resolveSelfTestStatusId(state, subId) {
  const projects = state.discovered?.projects ?? []
  const project = projects.find(p => p.sub_id === Number(subId))
  return project?.self_test_status_id ?? null
}

/**
 * dispatch 前将工作项状态改为「处理中」。
 * 业务语义：GeeLib 侧先可见 agent 已接手，再推送 channel 通知启动执行。
 */
export async function markMatterInProgress(store, item) {
  const matterId = Number(item.matterId)
  const subId = Number(item.subId)
  const state = store.loadPollerState()
  const inProgressStatusId = resolveInProgressStatusId(state, subId)
  if (inProgressStatusId === null) {
    throw new Error(
      `未找到 sub_id=${subId} 的「处理中」状态 ID，请确认 poll 已完成项目发现`,
    )
  }
  const result = await cmdUpdateMatter({
    subId,
    matterId,
    statusId: inProgressStatusId,
  })
  if (result?.errno !== 2000) {
    throw new Error(
      `matter_id=${matterId} 状态改为「处理中」失败: ${JSON.stringify(result)}`,
    )
  }
  return { matterId, subId, inProgressStatusId, result }
}

/**
 * dispatch 失败时回滚：释放 active 槽位，任务重新入队队首。
 */
export function rollbackDispatch(store, item) {
  const state = store.loadPollerState()
  const matterId = Number(item.matterId)
  if (state.activeMatterId === matterId) {
    state.activeMatterId = null
  }
  state.notifiedMatterIds = state.notifiedMatterIds.filter(x => x !== matterId)
  const pendingQueue = Array.isArray(state.pendingQueue) ? state.pendingQueue : []
  if (!pendingQueue.some(q => Number(q.matterId) === matterId)) {
    pendingQueue.unshift(item)
  }
  state.pendingQueue = pendingQueue
  store.savePollerState(state)
}

/**
 * 一次拉取全部「未开始」任务并入队；已在执行/队列/已完成的任务会被忽略。
 */
export async function syncUnstartedTaskQueue(store) {
  const state = store.loadPollerState()
  const discovered = await discoverAllProjects(state)
  state.discovered = discovered

  const userInfo = await cmdGetUserInfo()
  if (userInfo.errno !== 2000) {
    throw new Error(`get-user-info 失败: ${JSON.stringify(userInfo)}`)
  }
  const assigneeId = userInfo.data?.id ?? userInfo.data?.user_id
  if (assigneeId === undefined) {
    throw new Error('无法从 get-user-info 解析当前用户 ID')
  }

  const pendingQueue = Array.isArray(state.pendingQueue) ? [...state.pendingQueue] : []
  const pendingIds = new Set(pendingQueue.map(q => Number(q.matterId)))
  const newCandidates = []

  for (const project of discovered.projects) {
    const rows = await listUnstartedMatters(
      project.sub_id,
      project.type_id,
      assigneeId,
      project.poll_status_ids,
    )
    for (const row of rows) {
      const mid = extractMatterId(row)
      if (mid === null || isMatterTracked(state, mid) || pendingIds.has(mid)) {
        continue
      }
      newCandidates.push({
        row,
        project,
      })
    }
  }

  newCandidates.sort((a, b) => {
    const ta = a.row.create_time ?? a.row.updated_at ?? ''
    const tb = b.row.create_time ?? b.row.updated_at ?? ''
    return ta.localeCompare(tb)
  })

  for (const { row, project } of newCandidates) {
    const mid = extractMatterId(row)
    pendingQueue.push({
      matterId: mid,
      subId: project.sub_id,
      typeId: project.type_id,
      title: extractMatterTitle(row),
      matter: row,
      enqueuedAt: Date.now(),
    })
    pendingIds.add(mid)
  }

  state.pendingQueue = pendingQueue
  state.lastPollAt = Date.now()
  store.savePollerState(state)

  return {
    state,
    discovered,
    newlyEnqueued: newCandidates.length,
    queueLength: pendingQueue.length,
    activeMatterId: state.activeMatterId,
  }
}

/**
 * 当前无执行任务时，从队列头取一条准备 NOTIFY。
 * 业务语义：同一时刻仅 dispatch 一条，保证按顺序串行执行。
 */
export function takeNextForDispatch(store) {
  const state = store.loadPollerState()
  if (state.activeMatterId !== null) {
    return null
  }
  const pendingQueue = Array.isArray(state.pendingQueue) ? state.pendingQueue : []
  if (pendingQueue.length === 0) {
    return null
  }
  const item = pendingQueue.shift()
  const matterId = Number(item.matterId)
  state.pendingQueue = pendingQueue
  state.activeMatterId = matterId
  if (!state.notifiedMatterIds.includes(matterId)) {
    state.notifiedMatterIds.push(matterId)
  }
  store.savePollerState(state)
  return item
}

/** 将 matter_id 记入 processedMatterIds，释放 active 槽位。 */
export function markMatterProcessed(store, matterId) {
  const state = store.loadPollerState()
  const id = Number(matterId)
  if (!state.processedMatterIds.includes(id)) {
    state.processedMatterIds.push(id)
  }
  state.notifiedMatterIds = state.notifiedMatterIds.filter(x => x !== id)
  if (state.activeMatterId === id) {
    state.activeMatterId = null
  }
  state.pendingQueue = (state.pendingQueue ?? []).filter(
    q => Number(q.matterId) !== id,
  )
  store.savePollerState(state)
  return state
}

export function extractMatterTitle(row) {
  return (
    row.title ??
    row.matter_title ??
    row.name ??
    `matter-${extractMatterId(row) ?? '?'}`
  )
}
