import crypto from 'node:crypto'

import {
  consumeWorkerSmsCode,
  createWorkerUser,
  getLatestWorkerSmsCode,
  getWorkerUserByDisplayName,
  getWorkerUserByInviteCode,
  getWorkerUserByPhone,
  getWorkerUserByUsername,
  recordWorkerSmsCodeFailure,
  revokeAllWorkerSessions,
  updateWorkerPassword,
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import {
  ensureWorkerAuthConfigured,
  hashWorkerPassword,
  mapWorkerUser,
  normalizePassword,
  normalizeSessionVersion,
  validateWorkerPassword,
} from './mappers.js'
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
import { hashSmsCode, normalizeWorkerPhone } from './worker-sms-service.js'

const INVITE_CODE_LENGTH = 8
const INVITE_CODE_RETRY_LIMIT = 5
const WORKER_SMS_MAX_VERIFY_ATTEMPTS = 5

export async function resetWorkerPassword(payload: JsonObject = {}) {
  const phone = normalizeWorkerPhone(payload.phone)
  const code = String(payload.code || payload.smsCode || '').trim()
  const password = normalizePassword(payload.password || payload.newPassword)
  validateWorkerPassword(password)
  if (!code) {
    throw createHttpError('请输入短信验证码', {
      statusCode: 400,
      errorCode: 'worker_sms_code_required',
    })
  }

  const worker = await getWorkerUserByPhone(phone)
  if (!worker) {
    throw createHttpError('该手机号未注册', {
      statusCode: 404,
      errorCode: 'worker_phone_not_registered',
    })
  }
  const latest = await getLatestWorkerSmsCode(phone, 'password_reset')
  const codeValid = isWorkerSmsCodeValid(latest, code)
  if (!codeValid) {
    await recordInvalidWorkerSmsCode(latest)
    throw createHttpError('短信验证码错误或已过期', {
      statusCode: 400,
      errorCode: 'worker_sms_code_invalid',
    })
  }
  if (!(await consumeWorkerSmsCode(latest!.id, nowIso()))) {
    throw createHttpError('验证码已失效，请重新获取', {
      statusCode: 400,
      errorCode: 'worker_sms_code_invalid',
    })
  }
  const now = nowIso()
  const updated = await updateWorkerPassword({
    workerId: worker.id,
    passwordHash: hashWorkerPassword(password),
    sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
    now,
  })
  await revokeAllWorkerSessions(worker.id, now)
  return { worker: mapWorkerUser(updated || worker), reloginRequired: true }
}

export async function registerWorker(payload: JsonObject = {}) {
  ensureWorkerAuthConfigured()
  const phone = normalizeWorkerPhone(payload.phone)
  const code = String(payload.code || payload.smsCode || '').trim()
  const password = normalizePassword(payload.password)
  const displayName = String(payload.displayName || payload.nickname || '').trim()
  const inviteCode = String(payload.inviteCode || '')
    .trim()
    .toUpperCase()

  validateWorkerPassword(password)
  if (!displayName) {
    throw createHttpError('请输入昵称', {
      statusCode: 400,
      errorCode: 'worker_display_name_required',
    })
  }
  if (!code) {
    throw createHttpError('请输入短信验证码', {
      statusCode: 400,
      errorCode: 'worker_sms_code_required',
    })
  }

  const latest = await getLatestWorkerSmsCode(phone, 'register')
  if (!isWorkerSmsCodeValid(latest, code)) {
    await recordInvalidWorkerSmsCode(latest)
    throw createHttpError('短信验证码错误或已过期', {
      statusCode: 400,
      errorCode: 'worker_sms_code_invalid',
    })
  }
  if (await getWorkerUserByUsername(phone)) {
    throw createHttpError('该手机号已注册，请直接登录', {
      statusCode: 409,
      errorCode: 'worker_user_exists',
    })
  }
  if (await getWorkerUserByPhone(phone)) {
    throw createHttpError('该手机号已注册，请直接登录', {
      statusCode: 409,
      errorCode: 'worker_phone_exists',
    })
  }

  const inviter = inviteCode ? await getWorkerUserByInviteCode(inviteCode) : null
  if (inviteCode && !inviter) {
    throw createHttpError('邀请码无效', {
      statusCode: 400,
      errorCode: 'worker_invite_code_invalid',
    })
  }
  if (inviter && inviter.status !== 'active') {
    throw createHttpError('邀请人账号不可用', {
      statusCode: 400,
      errorCode: 'worker_inviter_invalid',
    })
  }
  if (await getWorkerUserByDisplayName(displayName)) {
    throw createHttpError('该昵称已被使用，请更换昵称', {
      statusCode: 409,
      errorCode: 'worker_display_name_taken',
    })
  }

  const defaults = await ensureWorkerPlatformDefaults()
  const now = nowIso()
  const created = await createWorkerUser({
    username: phone,
    passwordHash: hashWorkerPassword(password),
    displayName,
    phone,
    levelId: defaults.level?.id || null,
    status: 'active',
    inviterId: inviter ? Number(inviter.id) : null,
    inviteCode: await generateInviteCode(),
    smsCodeId: latest!.id,
    now,
  })
  if (!created) {
    throw createHttpError('接单账号注册失败', {
      statusCode: 500,
      errorCode: 'worker_register_failed',
    })
  }
  return { worker: mapWorkerUser(created), reviewRequired: false }
}

function isWorkerSmsCodeValid(
  latest: { consumed_at: string | null; expires_at: string; code_hash: string } | null,
  code: string,
) {
  return Boolean(
    latest &&
    !latest.consumed_at &&
    new Date(latest.expires_at).getTime() > Date.now() &&
    latest.code_hash === hashSmsCode(code),
  )
}

async function recordInvalidWorkerSmsCode(
  latest: { id: number; consumed_at: string | null } | null,
) {
  if (!latest || latest.consumed_at) return
  const failed = await recordWorkerSmsCodeFailure(
    latest.id,
    nowIso(),
    WORKER_SMS_MAX_VERIFY_ATTEMPTS,
  )
  if (failed?.consumed_at) {
    throw createHttpError('验证码错误次数过多，请重新获取验证码', {
      statusCode: 400,
      errorCode: 'worker_sms_code_locked',
    })
  }
}

async function generateInviteCode(): Promise<string> {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
  for (let attempt = 0; attempt < INVITE_CODE_RETRY_LIMIT; attempt += 1) {
    const candidate = Array.from(
      { length: INVITE_CODE_LENGTH },
      () => chars[Math.floor(crypto.randomInt(chars.length))],
    ).join('')
    if (!(await getWorkerUserByInviteCode(candidate))) return candidate
  }
  throw createHttpError('邀请码生成失败，请重试', {
    statusCode: 500,
    errorCode: 'worker_invite_code_generation_failed',
  })
}
