import {
  closeTencentBrowserSession,
  createTencentBrowserSession,
  reloadTencentBrowserSession,
} from '../../session/session.js'
import { updateTask } from '../../../repositories/task-repo.js'
import { nowIso } from '../../../utils/time.js'
import { getClaimContext, getClaimContextByTaskId } from './context.js'
import { clearTaskSession, loadTaskSession, syncTaskWithSession } from './runtime.js'
import {
  assertTaskCanProceed,
  buildClaimDetailPayload,
  isRecoverableSessionError,
  normalizeClaimLoginType,
} from './shared.js'

type JsonObject = Record<string, any>

export async function createClaimSession(token: unknown, payload: JsonObject = {}) {
  return createClaimSessionWithContextLoader(
    () => getClaimContext(token),
    payload,
  )
}

export async function createClaimSessionForAdminTask(taskId: unknown, payload: JsonObject = {}) {
  return createClaimSessionWithContextLoader(
    () => getClaimContextByTaskId(taskId),
    payload,
  )
}

export async function reloadClaimSession(token: unknown) {
  return reloadClaimSessionWithContextLoader(() => getClaimContext(token))
}

export async function reloadClaimSessionForAdminTask(taskId: unknown) {
  return reloadClaimSessionWithContextLoader(() => getClaimContextByTaskId(taskId))
}

export async function closeClaimSession(token: unknown) {
  return closeClaimSessionWithContextLoader(() => getClaimContext(token))
}

export async function closeClaimSessionForAdminTask(taskId: unknown) {
  return closeClaimSessionWithContextLoader(() => getClaimContextByTaskId(taskId))
}

async function createClaimSessionWithContextLoader(loadContext: () => Promise<JsonObject>, payload: JsonObject = {}) {
  const context = await loadContext()
  assertTaskCanProceed(context.task)
  const requestedLoginType = normalizeClaimLoginType(payload.loginType)
  const forceRecreate = Boolean(payload.forceRecreate)
  let task = context.task

  if (task.browser_session_id) {
    const existing = await loadTaskSession(task, { includeQrImage: true })
    task = existing.task

    if (existing.session) {
      const existingLoginType = normalizeClaimLoginType(existing.session.loginType || task.login_type)

      if (!forceRecreate && existingLoginType === requestedLoginType) {
        const syncedTask = await syncTaskWithSession(task, existing.session)

        return buildClaimDetailPayload({
          claimToken: context.claimToken,
          task: syncedTask,
          order: context.order,
          orderItem: context.orderItem,
          session: existing.session,
        })
      }

      try {
        await closeTencentBrowserSession(existing.session.sessionId)
      } catch (error) {
        if (!isRecoverableSessionError(error)) {
          throw error
        }
      }

      task = await clearTaskSession(task, {
        lastError: '',
      })
    }
  }

  const session = await createTencentBrowserSession({
    loginType: requestedLoginType,
  })
  const updatedTask = await updateTask(task.id, {
    task_status: 'claimed',
    browser_session_id: session.sessionId,
    login_type: session.loginType,
    user_action_status: 'claimed',
    claimed_at: task.claimed_at || nowIso(),
    updated_at: nowIso(),
    last_error: '',
  })

  return buildClaimDetailPayload({
    claimToken: context.claimToken,
    task: updatedTask,
    order: context.order,
    orderItem: context.orderItem,
    session,
  })
}

async function reloadClaimSessionWithContextLoader(loadContext: () => Promise<JsonObject>) {
  const context = await loadContext()
  assertTaskCanProceed(context.task)

  const active = await loadTaskSession(context.task, {
    includeQrImage: true,
  })

  if (!active.session) {
    return buildClaimDetailPayload({
      claimToken: context.claimToken,
      task: active.task,
      order: context.order,
      orderItem: context.orderItem,
      session: null,
    })
  }

  const session = await reloadTencentBrowserSession(active.session.sessionId)
  const syncedTask = await syncTaskWithSession(active.task, session)

  return buildClaimDetailPayload({
    claimToken: context.claimToken,
    task: syncedTask,
    order: context.order,
    orderItem: context.orderItem,
    session,
  })
}

async function closeClaimSessionWithContextLoader(loadContext: () => Promise<JsonObject>) {
  const context = await loadContext()
  assertTaskCanProceed(context.task)
  let task = context.task

  if (!task.browser_session_id) {
    return buildClaimDetailPayload({
      claimToken: context.claimToken,
      task,
      order: context.order,
      orderItem: context.orderItem,
      session: null,
    })
  }

  try {
    await closeTencentBrowserSession(task.browser_session_id)
  } catch (error) {
    if (!isRecoverableSessionError(error)) {
      throw error
    }
  }

  task = await clearTaskSession(task, {
    lastError: '',
  })

  return buildClaimDetailPayload({
    claimToken: context.claimToken,
    task,
    order: context.order,
    orderItem: context.orderItem,
    session: null,
  })
}
