import {
  getLoginTypeLabel,
  normalizeLoginType,
} from './session-browser-config.js'
import { logBrowserSessionDebug } from './session-login-shared.js'
import {
  loadCookieMapFromContext,
} from './session-cookie-map.js'
import {
  ensureActivityInfoReady,
  ensureLoggedInPresentation,
  extractActivityInfo,
} from './session-activity.js'
import {
  buildSessionPayload as buildSessionPayloadBase,
} from './session-payload.js'
import {
  captureSessionQr,
  ensureLoginTab,
  extractHostState,
  extractQrState,
  reloadActivityPageForPresentation,
} from './session-page.js'
import {
  saveRedeemArtifacts,
} from './session-proof.js'
import {
  ensureReviewScreenshot,
} from './session-review.js'
import {
  capturePageCaptchaForOcr,
  isCaptchaRejectedResult,
  prepareRedeemCodeFill,
  refreshPageCaptcha,
  runTencentBrowserRedeem,
  submitRedeemInBrowser,
} from './session-redeem.js'
import {
  ensureWxLoginReady,
} from './session-wx.js'
import { recognizeTencentCaptcha } from './ocr.js'
import {
  hasRedeemCredential,
  resolveInitialQrSessionState,
  resolveSessionNotice,
  resolveSessionStatus,
  resolveTencentSessionExpiresAt,
} from './session-state.js'
import {
  ACTIVITY_URL,
  DEFAULT_VIEWPORT,
  REDEEM_SUCCESS_AUTO_CLOSE_MS,
  SESSION_DEBUG_ENABLED,
} from './session-service-config.js'
import {
  closeBrowserIfIdle,
  ensureBrowser,
  resolveBrowserLaunchOptions,
  SessionServiceError,
  warmupTencentBrowser,
} from './session-service-runtime.js'
import {
  applyInitialQrSessionState,
  armRedeemedSessionAutoClose,
  cleanupExpiredTencentBrowserSessions,
  closeSession as closeStoredSession,
  getRequiredSession,
  listActiveTencentBrowserSessions,
  persistSessionState as persistStoredSessionState,
  registerTencentBrowserSession,
  resetTencentSessionAutoClose,
} from './session-service-store.js'
import type { TencentBrowserSession } from './session-service-store.js'

type JsonObject = Record<string, any>

type IncludeQrImageOptions = {
  includeQrImage?: boolean
}

type CloseAllSessionsOptions = {
  markClosed?: boolean
}

type CloseManagedSessionOptions = {
  markClosed: boolean
}

export { resolveTencentSessionExpiresAt }
export { REDEEM_SUCCESS_AUTO_CLOSE_MS, warmupTencentBrowser }

export async function createTencentBrowserSession(payload: JsonObject = {}): Promise<JsonObject> {
  await cleanupExpiredTencentBrowserSessions({ closeSession: closeManagedSession })

  const loginType = normalizeLoginType(payload.loginType || payload.qrType)

  const browser = await ensureBrowser()
  const browserContext = await browser.newContext({
    viewport: DEFAULT_VIEWPORT,
    deviceScaleFactor: 1,
  })

  const page = await browserContext.newPage()
  page.setDefaultTimeout(20_000)

  const session = await registerTencentBrowserSession({
    loginType,
    browserContext,
    page,
  })
  const sessionId = session.sessionId

  try {
    await page.goto(ACTIVITY_URL, { waitUntil: 'domcontentloaded' })
    await ensureLoginTab(page, loginType)
    await captureSessionQr(session, { ensureLoginReady: false })
    applyInitialQrSessionState(session)
    await persistSessionState(session)
    void refreshTencentBrowserSession(sessionId).catch((error) => {
      logBrowserSessionDebug('createSession.backgroundRefresh.error', {
        sessionId,
        message: error instanceof Error ? error.message : String(error),
      })
    })
    return buildSessionPayload(session)
  } catch (error) {
    session.status = 'failed'
    session.notice = error instanceof Error ? error.message : `${getLoginTypeLabel(loginType)} 浏览器会话初始化失败`
    session.lastError = session.notice
    session.updatedAt = new Date().toISOString()
    await persistSessionState(session)
    throw error
  }
}

export async function getTencentBrowserSession(
  sessionId: unknown,
  options: IncludeQrImageOptions = {},
): Promise<JsonObject> {
  return refreshTencentBrowserSession(sessionId, options)
}

export async function getTencentBrowserSessionSummary(sessionId: unknown): Promise<JsonObject> {
  return refreshTencentBrowserSession(sessionId, { includeQrImage: false })
}

export async function reloadTencentBrowserSession(sessionId: unknown): Promise<JsonObject> {
  await cleanupExpiredTencentBrowserSessions({ closeSession: closeManagedSession })

  const session = await getRequiredSession(sessionId)

  if (session.browserContextClosed) {
    throw new SessionServiceError('浏览器会话已关闭', {
      statusCode: 410,
      errorCode: 'session_closed',
    })
  }

  try {
    await reloadActivityPageForPresentation(session.page, ACTIVITY_URL)

    const cookieMap = await loadCookieMapFromContext(session.browserContext)
    const credentialReady = hasRedeemCredential(cookieMap)

    if (!credentialReady) {
      session.qrImageBase64 = ''
      session.qrImagePath = ''
      session.qrUpdatedAt = ''
      await ensureLoginTab(session.page, session.loginType)
      await captureSessionQr(session)
    }

    session.notice = credentialReady
      ? '后端页面已刷新，正在同步最新状态'
      : `已刷新${getLoginTypeLabel(session.loginType)}登录页，准备重新抓取二维码`
    session.updatedAt = new Date().toISOString()

    return await refreshTencentBrowserSession(sessionId)
  } catch (error) {
    session.status = 'failed'
    session.notice = error instanceof Error ? error.message : '刷新后端页面失败'
    session.lastError = session.notice
    session.updatedAt = new Date().toISOString()
    await persistSessionState(session)
    throw error
  }
}

export async function refreshTencentBrowserSession(
  sessionId: unknown,
  { includeQrImage = true }: IncludeQrImageOptions = {},
): Promise<JsonObject> {
  await cleanupExpiredTencentBrowserSessions({ closeSession: closeManagedSession })

  const session = await getRequiredSession(sessionId)

  if (session.browserContextClosed) {
    throw new SessionServiceError('浏览器会话已关闭', {
      statusCode: 410,
      errorCode: 'session_closed',
    })
  }

  try {
    let hostState = await extractHostState(session.page)
    const cookieMap = await loadCookieMapFromContext(session.browserContext)
    const credentialReady = hasRedeemCredential(cookieMap)

    if (credentialReady) {
      const presentation = await ensureLoggedInPresentation(session, {
        hostState,
        credentialReady,
        activityUrl: ACTIVITY_URL,
      })
      hostState = presentation.hostState
    } else {
      session.presentationSyncAttempts = 0
    }

    if (!credentialReady && session.loginType === 'wx') {
      await ensureWxLoginReady(session.page)
    }

    const qrState = await extractQrState(session.page, session.loginType)
    const activityInfo = credentialReady
      ? await ensureActivityInfoReady(session.page, { triggerRender: true })
      : await extractActivityInfo(session.page)

    session.lastState = {
      hostState,
      qrState,
      credentialReady,
      activityInfo,
      cookieKeys: Object.keys(cookieMap),
    }
    session.status = resolveSessionStatus({ hostState, qrState, credentialReady, activityInfo })
    session.notice = resolveSessionNotice({
      hostState,
      qrState,
      credentialReady,
      activityInfo,
      loginType: session.loginType,
    })

    if (credentialReady && activityInfo?.role?.ready) {
      await ensureReviewScreenshot(session, activityInfo)
    }

    session.updatedAt = new Date().toISOString()
    session.expiresAt = resolveTencentSessionExpiresAt(session)

    if (!session.qrImageBase64 || session.status === 'expired') {
      if (session.status === 'expired') {
        await reloadActivityPageForPresentation(session.page, ACTIVITY_URL)
        await ensureLoginTab(session.page, session.loginType)
      }

      await captureSessionQr(session)
      if (session.status === 'expired') {
        session.notice = '二维码已刷新，请重新扫码'
        session.status = 'waiting_scan'
      }
    }

    await persistSessionState(session)
    return buildSessionPayload(session, { includeQrImage })
  } catch (error) {
    session.status = 'failed'
    session.notice = error instanceof Error ? error.message : `查询 ${getLoginTypeLabel(session.loginType)} 浏览器会话状态失败`
    session.lastError = session.notice
    session.updatedAt = new Date().toISOString()
    await persistSessionState(session)
    return buildSessionPayload(session, { includeQrImage })
  }
}

export async function redeemTencentBrowserSession(sessionId: unknown, payload: JsonObject = {}): Promise<JsonObject> {
  const session = await getRequiredSession(sessionId)
  const code = String(payload.code || payload.sCdKey || '').trim()
  const maxAttempts = Math.max(1, Math.min(Number(payload.maxAttempts || 6), 12))

  if (!code) {
    throw new SessionServiceError('缺少兑换码', {
      statusCode: 400,
      errorCode: 'missing_redeem_code',
    })
  }

  const refreshed = await refreshTencentBrowserSession(sessionId)

  if (!['logged_in', 'ready_to_redeem', 'redeemed'].includes(refreshed.status)) {
    throw new SessionServiceError(`当前会话还不能兑换，状态为 ${refreshed.status}`, {
      statusCode: 409,
      errorCode: 'session_not_ready',
    })
  }

  resetTencentSessionAutoClose(session)

  await runTencentBrowserRedeem({
    session,
    code,
    maxAttempts,
    ensureLoggedInPresentation: (redeemSession, options) =>
      ensureLoggedInPresentation(redeemSession as any, options as any),
    ensureActivityInfoReady,
    fillRedeemCodeInBrowser,
    capturePageCaptchaForOcr: (page, options) =>
      capturePageCaptchaForOcr(page, { ...options, ensureActivityInfoReady }),
    recognizeTencentCaptcha,
    submitRedeemInBrowser: (page, redeemPayload) =>
      submitRedeemInBrowser(page, redeemPayload, { ensureActivityInfoReady }),
    isCaptchaRejectedResult,
    refreshPageCaptcha,
    persistSessionState,
    buildSessionPayload,
    saveRedeemArtifacts,
    activityUrl: ACTIVITY_URL,
  })

  armRedeemedSessionAutoClose(session, { closeSession: closeManagedSession })
  await persistSessionState(session)

  return {
    ...buildSessionPayload(session),
    redeem: session.lastRedeem,
  }
}

export async function closeTencentBrowserSession(sessionId: unknown): Promise<JsonObject> {
  const session = await getRequiredSession(sessionId)
  await closeManagedSession(session, { markClosed: true })
  return {
    sessionId,
    closed: true,
  }
}

export async function closeAllTencentBrowserSessions(
  { markClosed = true }: CloseAllSessionsOptions = {},
): Promise<JsonObject> {
  const activeSessions = listActiveTencentBrowserSessions()

  await Promise.all(activeSessions.map((session) => closeManagedSession(session, { markClosed })))

  return {
    closedCount: activeSessions.length,
  }
}

export async function getTencentBrowserSessionScreenshotPath(sessionId: unknown): Promise<string> {
  const session = await getRequiredSession(sessionId)
  const screenshotPath = session.lastRedeem?.screenshotPath

  if (!screenshotPath) {
    throw new SessionServiceError('当前会话还没有兑换截图', {
      statusCode: 404,
      errorCode: 'screenshot_not_ready',
    })
  }

  return screenshotPath
}

export async function getTencentBrowserSessionReviewScreenshotPath(sessionId: unknown): Promise<string> {
  const session = await getRequiredSession(sessionId)
  const screenshotPath = session.lastReview?.screenshotPath

  if (!screenshotPath) {
    throw new SessionServiceError('当前会话还没有客服复核截图', {
      statusCode: 404,
      errorCode: 'review_screenshot_not_ready',
    })
  }

  return screenshotPath
}

async function persistSessionState(session: TencentBrowserSession): Promise<void> {
  await persistStoredSessionState(session, { buildSessionPayload })
}

function buildSessionPayload(
  session: TencentBrowserSession,
  { includeQrImage = true }: IncludeQrImageOptions = {},
): JsonObject {
  return buildSessionPayloadBase(session, {
    activityUrl: ACTIVITY_URL,
    includeQrImage,
    sessionDebugEnabled: SESSION_DEBUG_ENABLED,
    browserDebug: SESSION_DEBUG_ENABLED ? resolveBrowserLaunchOptions() : null,
  })
}

async function fillRedeemCodeInBrowser(page: any, code: string): Promise<void> {
  await prepareRedeemCodeFill(page, code, { ensureActivityInfoReady })
}

async function closeManagedSession(
  session: TencentBrowserSession,
  { markClosed }: CloseManagedSessionOptions,
): Promise<void> {
  await closeStoredSession(session, {
    markClosed,
    persistSessionState,
    closeBrowserIfIdle: () => closeBrowserIfIdle({
      activeSessionCount: listActiveTencentBrowserSessions().length,
    }),
  })
}
