import * as aiAttachmentAssociationModel from '../../utils/aiAttachmentAssociationModel.js'
import {
  buildAttachmentAssociationCandidateActions,
  isAttachmentAssociationConfirmationResult,
  resolveAttachmentAssociationTarget
} from '../../utils/attachmentAssociationJobModel.js'
import {
  fetchAttachmentAssociationJob,
  normalizeAttachmentAssociationJob
} from '../../services/attachmentAssociationJobs.js'

const ATTACHMENT_ASSOCIATION_JOB_POLL_INTERVAL_MS = 1200
const ATTACHMENT_ASSOCIATION_JOB_MAX_POLLS = 90
const ATTACHMENT_ASSOCIATION_JOB_PENDING_STATUSES = new Set(['queued', 'running'])

export function useWorkbenchAiAttachmentAssociationJobs({
  conversationMessages,
  createInlineMessage,
  persistCurrentConversation,
  replaceInlineMessage,
  streamOrSetInlineAssistantContent,
  notifyRequestUpdated,
  toast,
  buildDetailActions,
  buildThinkingEvents
}) {
  const activeJobPolls = new Set()

  function delay(milliseconds) {
    return new Promise((resolve) => {
      globalThis.setTimeout(resolve, milliseconds)
    })
  }

  function normalizeJob(job = {}) {
    return normalizeAttachmentAssociationJob(job)
  }

  function isPending(job = {}) {
    return ATTACHMENT_ASSOCIATION_JOB_PENDING_STATUSES.has(String(job?.status || '').trim())
  }

  function extractReceiptIdsFromOcrDocuments(documents = []) {
    return Array.from(new Set(
      (Array.isArray(documents) ? documents : [])
        .map((document) => String(document?.receipt_id || document?.receiptId || '').trim())
        .filter(Boolean)
    ))
  }

  function buildRunningMessage(job = {}, fileNames = []) {
    const names = fileNames.filter(Boolean)
    const attachmentLabel = names.length
      ? `${names.length} 份：${names.slice(0, 2).join('、')}${names.length > 2 ? ' 等' : ''}`
      : '已识别票据附件'
    const statusText = String(job?.message || '').trim() || '正在后台匹配并归集票据附件。'
    return [
      '我已收到附件关联请求，正在后台继续处理。',
      '',
      `本次附件：${attachmentLabel}`,
      `处理状态：${statusText}`,
      '',
      '您可以先离开当前会话，回来后我会继续查询任务结果。'
    ].join('\n')
  }

  function buildFailedMessage(job = {}) {
    return String(job?.message || job?.error || '').trim() || '自动归集失败，请补充说明或重新上传附件后再试。'
  }

  function findMessage(messageId) {
    return conversationMessages.value.find((message) => message.id === messageId) || null
  }

  function replaceJobMessage(messageId, content, options = {}) {
    const sourceMessage = findMessage(messageId)
    if (!sourceMessage) {
      return false
    }
    replaceInlineMessage(
      messageId,
      createInlineMessage('assistant', content, {
        id: messageId,
        attachmentAssociationJob: options.attachmentAssociationJob || sourceMessage?.attachmentAssociationJob || null,
        attachmentOcrDetails: options.attachmentOcrDetails || sourceMessage?.attachmentOcrDetails || null,
        pending: Boolean(options.pending),
        stewardPlan: options.stewardPlan || null,
        suggestedActions: options.suggestedActions || []
      })
    )
    return true
  }

  async function updateJobMessage({
    job,
    messageId,
    fileNames = [],
    attachmentOcrDetails = null
  }) {
    const normalizedJob = normalizeJob(job)
    if (!normalizedJob) {
      return false
    }
    if (!findMessage(messageId)) {
      return true
    }
    if (isAttachmentAssociationConfirmationResult(normalizedJob)) {
      const confirmationMessage = aiAttachmentAssociationModel.buildAiAttachmentAssociationConfirmationResultMessage({
        job: normalizedJob,
        fileNames
      })
      await streamOrSetInlineAssistantContent(messageId, confirmationMessage)
      replaceJobMessage(messageId, confirmationMessage, {
        attachmentAssociationJob: normalizedJob,
        attachmentOcrDetails,
        stewardPlan: {
          streamStatus: 'completed',
          thinkingEvents: buildThinkingEvents('completed')
        },
        suggestedActions: buildAttachmentAssociationCandidateActions(normalizedJob)
      })
      persistCurrentConversation()
      return true
    }
    if (normalizedJob.status === 'succeeded') {
      const target = resolveAttachmentAssociationTarget(normalizedJob)
      const finalMessageText = aiAttachmentAssociationModel.buildAiAttachmentAssociationResultMessage({
        claimNo: target.claimNo,
        fileNames,
        uploadedCount: normalizedJob.uploadedCount,
        skippedCount: normalizedJob.skippedCount,
        applicationClaimNo: normalizedJob.applicationClaimNo,
        confidence: normalizedJob.confidence,
        matchReasons: normalizedJob.matchReasons,
        riskItems: normalizedJob.riskItems,
        confirmationRequired: Boolean(
          normalizedJob.draftPayload?.confirmationRequired
          ?? normalizedJob.draftPayload?.confirmation_required
        )
      })
      await streamOrSetInlineAssistantContent(messageId, finalMessageText)
      replaceJobMessage(messageId, finalMessageText, {
        attachmentAssociationJob: normalizedJob,
        attachmentOcrDetails,
        stewardPlan: {
          streamStatus: 'completed',
          thinkingEvents: buildThinkingEvents('completed')
        },
        suggestedActions: buildDetailActions({
          claimId: target.claimId,
          claimNo: target.claimNo
        })
      })
      notifyRequestUpdated?.({
        claimId: target.claimId,
        claimNo: target.claimNo,
        source: 'ai-workbench-attachment-association-job',
        uploadedCount: normalizedJob.uploadedCount,
        skippedCount: normalizedJob.skippedCount
      })
      persistCurrentConversation()
      return true
    }
    if (normalizedJob.status === 'failed') {
      replaceJobMessage(messageId, buildFailedMessage(normalizedJob), {
        attachmentAssociationJob: normalizedJob,
        attachmentOcrDetails,
        stewardPlan: {
          streamStatus: 'failed',
          thinkingEvents: buildThinkingEvents('failed')
        }
      })
      persistCurrentConversation()
      return true
    }

    replaceJobMessage(messageId, buildRunningMessage(normalizedJob, fileNames), {
      attachmentAssociationJob: normalizedJob,
      attachmentOcrDetails,
      pending: true,
      stewardPlan: {
        streamStatus: 'streaming',
        thinkingEvents: buildThinkingEvents('running')
      }
    })
    persistCurrentConversation()
    return false
  }

  async function pollJob({
    jobId,
    messageId,
    fileNames = [],
    attachmentOcrDetails = null,
    initialJob = null
  } = {}) {
    const normalizedJobId = String(jobId || '').trim()
    if (!normalizedJobId || activeJobPolls.has(normalizedJobId)) {
      return
    }
    activeJobPolls.add(normalizedJobId)
    try {
      let currentJob = initialJob ? normalizeJob(initialJob) : null
      if (currentJob) {
        const done = await updateJobMessage({ job: currentJob, messageId, fileNames, attachmentOcrDetails })
        if (done) {
          return
        }
      }

      for (let index = 0; index < ATTACHMENT_ASSOCIATION_JOB_MAX_POLLS; index += 1) {
        await delay(ATTACHMENT_ASSOCIATION_JOB_POLL_INTERVAL_MS)
        currentJob = normalizeJob(await fetchAttachmentAssociationJob(normalizedJobId))
        if (!currentJob) {
          throw new Error('附件关联任务不存在或已失效。')
        }
        const done = await updateJobMessage({ job: currentJob, messageId, fileNames, attachmentOcrDetails })
        if (done) {
          return
        }
      }
      throw new Error('附件关联任务仍在后台处理中，稍后回到会话会继续刷新结果。')
    } catch (error) {
      const message = error?.message || '自动归集状态查询失败，请稍后回到会话查看。'
      replaceJobMessage(messageId, message, {
        attachmentAssociationJob: {
          jobId: normalizedJobId,
          status: 'failed',
          message,
          error: message
        },
        attachmentOcrDetails,
        stewardPlan: {
          streamStatus: 'failed',
          thinkingEvents: buildThinkingEvents('failed')
        }
      })
      toast(message)
      persistCurrentConversation()
    } finally {
      activeJobPolls.delete(normalizedJobId)
    }
  }

  function resumePendingJobs() {
    conversationMessages.value.forEach((message) => {
      const job = normalizeJob(message.attachmentAssociationJob || null)
      if (!job || !isPending(job)) {
        return
      }
      void pollJob({
        jobId: job.jobId,
        messageId: message.id,
        fileNames: message.attachmentOcrDetails?.fileNames || [],
        attachmentOcrDetails: message.attachmentOcrDetails || null,
        initialJob: job
      })
    })
  }

  return {
    buildRunningMessage,
    extractReceiptIdsFromOcrDocuments,
    normalizeJob,
    pollJob,
    resumePendingJobs,
    updateJobMessage
  }
}
