import {
  consumeWorkerSmsCode,
  createWorkerSessionRecord,
  getLatestWorkerSmsCode,
  getWorkerUserByDisplayName,
  getWorkerUserByPhone,
  getWorkerUserByUsername,
  recordWorkerSmsCodeFailure,
  type WorkerUserRow,
} from '../../repositories/worker-platform/index.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import {
  createWorkerSession,
  ensureWorkerAuthConfigured,
  isWorkerPasswordStrong,
  normalizePassword,
  normalizeSessionVersion,
  normalizeUsername,
  verifyWorkerPassword,
} from './mappers.js'
import {
  assertWorkerLoginAllowed,
  normalizeWorkerDeviceId,
  normalizeWorkerDeviceName,
  normalizeWorkerDeviceType,
} from './worker-auth-policy.js'
import { deleteWorkerSession, getWorkerSessions } from './worker-session-auth-service.js'
import type { WorkerSession } from './worker-session-context-service.js'
import { hashSmsCode, normalizeWorkerPhone } from './worker-sms-service.js'

const WORKER_MAX_DEVICES = 3
const WORKER_SMS_MAX_VERIFY_ATTEMPTS = 5

type WorkerLoginDeviceInfo = {
  deviceId?: unknown
  deviceType?: unknown
  deviceName?: unknown
  userAgent?: unknown
  ipAddress?: unknown
}

export async function loginWorker(
  username: unknown,
  password: unknown,
  deviceInfo: WorkerLoginDeviceInfo = {},
) {
  ensureWorkerAuthConfigured()
  const normalizedPassword = normalizePassword(password)
  const worker = await authenticateWorkerCredentials(username, password)
  return createWorkerLoginSession(worker, deviceInfo, !isWorkerPasswordStrong(normalizedPassword))
}

export async function loginWorkerBySmsCode(
  phoneValue: unknown,
  codeValue: unknown,
  deviceInfo: WorkerLoginDeviceInfo = {},
) {
  ensureWorkerAuthConfigured()
  const phone = normalizeWorkerPhone(phoneValue)
  const code = String(codeValue || '').trim()
  if (!code) {
    throw createHttpError('请输入短信验证码', {
      statusCode: 400,
      errorCode: 'worker_sms_code_required',
    })
  }
  const worker = await getWorkerUserByPhone(phone)
  const latest = await getLatestWorkerSmsCode(phone, 'login')
  const codeValid = Boolean(
    latest &&
    !latest.consumed_at &&
    new Date(latest.expires_at).getTime() > Date.now() &&
    latest.code_hash === hashSmsCode(code),
  )
  if (!worker || !codeValid) {
    if (latest && !latest.consumed_at) {
      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',
        })
      }
    }
    throw createHttpError('手机号或验证码错误', {
      statusCode: 401,
      errorCode: 'worker_sms_code_invalid',
    })
  }
  if (!(await consumeWorkerSmsCode(latest!.id, nowIso()))) {
    throw createHttpError('验证码已失效，请重新获取', {
      statusCode: 400,
      errorCode: 'worker_sms_code_invalid',
    })
  }
  assertWorkerLoginAllowed(worker)
  return createWorkerLoginSession(worker, deviceInfo, false)
}

export async function manageWorkerSessions(username: unknown, password: unknown) {
  const worker = await authenticateWorkerCredentials(username, password)
  return getWorkerSessions(createCredentialSession(worker))
}

export async function deleteWorkerSessionWithCredentials(
  username: unknown,
  password: unknown,
  targetSessionId: unknown,
) {
  const worker = await authenticateWorkerCredentials(username, password)
  return deleteWorkerSession(createCredentialSession(worker), targetSessionId)
}

async function createWorkerLoginSession(
  worker: WorkerUserRow,
  deviceInfo: WorkerLoginDeviceInfo,
  passwordWeak: boolean,
) {
  const session = createWorkerSession(worker)
  const deviceType = normalizeWorkerDeviceType(deviceInfo.deviceType)
  const sessionRecord = await createWorkerSessionRecord({
    sessionId: session.sessionId,
    workerId: Number(worker.id),
    deviceId: normalizeWorkerDeviceId(deviceInfo.deviceId, session.sessionId),
    deviceType,
    deviceName: normalizeWorkerDeviceName(deviceInfo.deviceName, deviceType),
    userAgent: String(deviceInfo.userAgent || '')
      .trim()
      .slice(0, 500),
    ipAddress: String(deviceInfo.ipAddress || '')
      .trim()
      .slice(0, 100),
    issuedAt: new Date().toISOString(),
    expiresAt: session.expiresAt,
    now: nowIso(),
    maxDevices: WORKER_MAX_DEVICES,
  })
  if (!sessionRecord.created) {
    const deviceLabel = deviceType === 'pc' ? 'PC' : '手机'
    throw createHttpError(
      `${deviceLabel}设备已达到 ${WORKER_MAX_DEVICES} 台，请先删除旧的登录设备`,
      {
        statusCode: 409,
        errorCode: 'worker_device_limit',
      },
    )
  }
  return { ...session, passwordWeak }
}

async function authenticateWorkerCredentials(
  username: unknown,
  password: unknown,
): Promise<WorkerUserRow> {
  const normalizedUsername = normalizeUsername(username)
  const normalizedPassword = normalizePassword(password)
  if (!normalizedUsername || !normalizedPassword) {
    throw createHttpError('缺少接单账号或密码', {
      statusCode: 400,
      errorCode: 'worker_credentials_required',
    })
  }
  const worker =
    (await getWorkerUserByUsername(normalizedUsername)) ||
    (await getWorkerUserByPhone(normalizedUsername)) ||
    (await getWorkerUserByDisplayName(normalizedUsername))
  if (!worker || !verifyWorkerPassword(normalizedPassword, worker.password_hash)) {
    throw createHttpError('账号或密码错误', {
      statusCode: 401,
      errorCode: 'worker_login_failed',
    })
  }
  assertWorkerLoginAllowed(worker)
  return worker
}

function createCredentialSession(worker: WorkerUserRow): WorkerSession {
  return {
    sessionId: '',
    workerId: Number(worker.id),
    username: worker.username,
    status: worker.status,
    expiresAt: '',
    sessionVersion: normalizeSessionVersion(worker.session_version),
  }
}
