import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'
import {
  comparePassword,
  hashPassword,
  isAdminEmail,
  issueEmailCode,
  normalizeEmail,
  validatePassword,
  verifyEmailCode,
} from '../lib/auth.js'
import { generateAvatar } from '../lib/generate-avatar.js'
import { prisma } from '../lib/prisma.js'
import { checkCodeRequestRateLimit, checkCodeVerifyRateLimit, checkLoginRateLimit } from '../lib/rate-limit.js'

export function mapUserForClient(user) {
  const adminEmail = normalizeEmail(process.env.ADMIN_EMAIL)
  const userEmail = normalizeEmail(user.email)
  return {
    id: user.id,
    email: user.email,
    displayName: user.displayName,
    avatar: user.avatar,
    avatarStyle: user.avatarStyle,
    isAdmin: Boolean(adminEmail) && userEmail === adminEmail,
  }
}

export async function registerAuthRoutes(fastify) {
  fastify.post('/api/auth/request-code', async (request, reply) => {
    const email = normalizeEmail(request.body?.email)
    if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })

    const ip = request.ip
    const rate = checkCodeRequestRateLimit(ip)
    if (!rate.allowed) {
      return reply
        .code(429)
        .header('Retry-After', String(rate.retryAfter))
        .send({ error: `Слишком много запросов. Попробуйте через ${rate.retryAfter} сек.` })
    }

    const code = await issueEmailCode({ email, purpose: 'login' })

    const adminEmail = process.env.ADMIN_EMAIL?.trim().toLowerCase()
    const isAdmin = email === adminEmail

    request.server.eventBus.emit(NOTIFICATION_EVENTS.AUTH_CODE_REQUESTED, {
      email,
      code,
      isAdmin,
    })

    return { ok: true }
  })

  fastify.post('/api/auth/verify-code', async (request, reply) => {
    const email = normalizeEmail(request.body?.email)
    const code = String(request.body?.code || '').trim()
    if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })
    if (!code || code.length !== 6) return reply.code(400).send({ error: 'Код должен быть из 6 цифр' })

    const ip = request.ip
    const rate = checkCodeVerifyRateLimit(ip)
    if (!rate.allowed) {
      return reply
        .code(429)
        .header('Retry-After', String(rate.retryAfter))
        .send({ error: `Слишком много попыток. Попробуйте через ${rate.retryAfter} сек.` })
    }

    const ok = await verifyEmailCode({ email, purpose: 'login', code })
    if (!ok) return reply.code(401).send({ error: 'Неверный или истёкший код' })

    const avatarUri = await generateAvatar(email)
    const user = await prisma.user.upsert({
      where: { email },
      update: {},
      create: { email, avatar: avatarUri, avatarStyle: 'avataaars' },
    })

    // Ensure notification preference exists
    await prisma.notificationPreference.upsert({
      where: { userId: user.id },
      create: { userId: user.id, globalEnabled: true },
      update: {},
    })

    const token = fastify.jwt.sign({ sub: user.id, email: user.email })
    return { token, user: mapUserForClient(user) }
  })

  fastify.post('/api/auth/register', async (request, reply) => {
    const email = normalizeEmail(request.body?.email)
    const password = String(request.body?.password || '')
    const displayNameRaw = request.body?.displayName
    const displayName = displayNameRaw ? String(displayNameRaw).trim().slice(0, 100) : email.split('@')[0]

    if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })
    if (isAdminEmail(email)) return reply.code(403).send({ error: 'Администратор не может регистрироваться с паролем' })

    const passwordErr = validatePassword(password)
    if (passwordErr) return reply.code(400).send({ error: passwordErr })

    const exists = await prisma.user.findUnique({ where: { email } })
    if (exists) return reply.code(409).send({ error: 'Эта почта уже зарегистрирована' })

    const passwordHash = await hashPassword(password)
    const avatarUri = await generateAvatar(email)
    const user = await prisma.user.create({
      data: {
        email,
        passwordHash,
        displayName: displayName || null,
        avatar: avatarUri,
        avatarStyle: 'initials',
      },
    })

    await prisma.notificationPreference.upsert({
      where: { userId: user.id },
      create: { userId: user.id, globalEnabled: true },
      update: {},
    })

    const token = fastify.jwt.sign({ sub: user.id, email: user.email })
    return reply.code(201).send({ token, user: mapUserForClient(user) })
  })

  fastify.post('/api/auth/login', async (request, reply) => {
    const email = normalizeEmail(request.body?.email)
    const password = String(request.body?.password || '')
    const ip = request.ip

    if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })
    if (isAdminEmail(email)) return reply.code(403).send({ error: 'Администратор входит только по коду' })

    const rate = checkLoginRateLimit(ip)
    if (!rate.allowed) {
      return reply
        .code(429)
        .header('Retry-After', String(rate.retryAfter))
        .send({ error: `Слишком много попыток. Попробуйте через ${rate.retryAfter} сек.` })
    }

    const user = await prisma.user.findUnique({ where: { email } })
    if (!user || !user.passwordHash) {
      return reply.code(401).send({ error: 'Неверная почта или пароль' })
    }

    const valid = await comparePassword(password, user.passwordHash)
    if (!valid) {
      return reply.code(401).send({ error: 'Неверная почта или пароль' })
    }

    const token = fastify.jwt.sign({ sub: user.id, email: user.email })
    return { token, user: mapUserForClient(user) }
  })

  fastify.post('/api/auth/forgot-password', async (request) => {
    const email = normalizeEmail(request.body?.email)
    if (!email || !email.includes('@')) return { ok: true }

    if (isAdminEmail(email)) return { ok: true }

    const user = await prisma.user.findUnique({ where: { email } })
    if (!user || !user.passwordHash) return { ok: true }

    await issueEmailCode({ email, purpose: 'reset_password' })
    return { ok: true }
  })

  fastify.post('/api/auth/reset-password', async (request, reply) => {
    const email = normalizeEmail(request.body?.email)
    const code = String(request.body?.code || '').trim()
    const newPassword = String(request.body?.newPassword || '')

    if (!email || !email.includes('@')) return reply.code(400).send({ error: 'Некорректная почта' })
    if (!code || code.length !== 6) return reply.code(400).send({ error: 'Код должен быть из 6 цифр' })

    const ok = await verifyEmailCode({ email, purpose: 'reset_password', code })
    if (!ok) return reply.code(401).send({ error: 'Неверный или истёкший код' })

    const passwordErr = validatePassword(newPassword)
    if (passwordErr) return reply.code(400).send({ error: passwordErr })

    const passwordHash = await hashPassword(newPassword)
    await prisma.user.update({ where: { email }, data: { passwordHash } })

    return { ok: true }
  })

  fastify.patch('/api/me/profile', { preHandler: [fastify.authenticate] }, async (request, reply) => {
    const userId = request.user.sub
    const nameRaw = request.body?.displayName
    const displayName = nameRaw === null || nameRaw === undefined ? null : String(nameRaw).trim()
    const avatarRaw = request.body?.avatar
    const avatar = avatarRaw === null || avatarRaw === undefined ? undefined : String(avatarRaw).trim()
    const avatarStyleRaw = request.body?.avatarStyle
    const avatarStyle =
      avatarStyleRaw === null || avatarStyleRaw === undefined ? undefined : String(avatarStyleRaw).trim()

    if (displayName !== null && displayName.length > 40)
      return reply.code(400).send({ error: 'Имя/ник максимум 40 символов' })
    if (avatar !== undefined && avatar.length > 200000) return reply.code(400).send({ error: 'Аватар слишком большой' })
    if (avatarStyle !== undefined && avatarStyle !== '' && avatarStyle.length > 30) {
      return reply.code(400).send({ error: 'Стиль аватара слишком длинный' })
    }

    const data = {
      displayName: displayName && displayName.length ? displayName : null,
    }

    if (avatar !== undefined) {
      data.avatar = avatar === '' ? null : avatar
    }
    if (avatarStyle !== undefined) {
      data.avatarStyle = avatarStyle === '' ? null : avatarStyle
    }
    const updated = await prisma.user.update({
      where: { id: userId },
      data,
    })
    return { user: mapUserForClient(updated) }
  })

  fastify.delete('/api/me', { preHandler: [fastify.authenticate] }, async (request, reply) => {
    const userId = request.user.sub

    const ACTIVE_STATUSES = ['DRAFT', 'PENDING_PAYMENT', 'PAID', 'IN_PROGRESS', 'SHIPPED', 'READY_FOR_PICKUP']

    const activeOrders = await prisma.order.findMany({
      where: { userId, status: { in: ACTIVE_STATUSES } },
      select: { id: true },
    })

    await prisma.user.delete({ where: { id: userId } })
    return { ok: true, activeOrderIds: activeOrders.map((o) => o.id) }
  })
}
