import type { FastifyPluginAsync } from 'fastify'
import { z } from 'zod'
import type { Container } from '../../container'
import { LoginStaff } from '../../../application/use-cases/auth/login-staff'
import { RefreshTokenStaff } from '../../../application/use-cases/auth/refresh-token-staff'
import { LogoutStaff } from '../../../application/use-cases/auth/logout-staff'
import { RegisterStaff } from '../../../application/use-cases/auth/register-staff'
import { createAuthGuard, requireCurrentUser } from '../plugins/auth-guard'
import { USER_ROLES } from '../../../domain/entities/user'
import { AppError } from '../../../domain/errors/app-error'

const REFRESH_TOKEN_COOKIE = 'rt'
const COOKIE_PATH = '/auth'
const COOKIE_MAX_AGE_S = 7 * 24 * 60 * 60

const loginBodySchema = z.object({
  email: z.string().email('Email inválido'),
  password: z.string().min(1, 'La contraseña es obligatoria'),
})

const registerBodySchema = z.object({
  fullName: z.string().min(2, 'El nombre debe tener al menos 2 caracteres'),
  email: z.string().email('Email inválido'),
  password: z
    .string()
    .min(8, 'La contraseña debe tener al menos 8 caracteres')
    .regex(/[A-Z]/, 'Debe contener al menos una mayúscula')
    .regex(/[0-9]/, 'Debe contener al menos un número'),
  role: z.enum(USER_ROLES),
  discountLimitPct: z.number().min(0).max(100).default(0),
})

export const authRoutes: FastifyPluginAsync<{ container: Container }> = async (fastify, opts) => {
  const { container } = opts
  const { tokenService, userRepo, passwordHasher, refreshTokenRepo } = container

  const cookieOptions = (isProduction: boolean) => ({
    httpOnly: true,
    secure: isProduction,
    sameSite: 'strict' as const,
    path: COOKIE_PATH,
    maxAge: COOKIE_MAX_AGE_S,
  })

  const isProduction = process.env['NODE_ENV'] === 'production'

  // ── POST /auth/login ─────────────────────────────────────────────────────
  fastify.post(
    '/login',
    {
      config: {
        rateLimit: { max: 10, timeWindow: '15 minutes' },
      },
    },
    async (request, reply) => {
      const body = loginBodySchema.parse(request.body)
      const useCase = new LoginStaff(userRepo, passwordHasher, tokenService, refreshTokenRepo)
      const result = await useCase.execute(body)

      return reply
        .status(200)
        .setCookie(REFRESH_TOKEN_COOKIE, result.refreshToken, cookieOptions(isProduction))
        .send({ data: { accessToken: result.accessToken, user: result.user } })
    },
  )

  // ── POST /auth/refresh ───────────────────────────────────────────────────
  fastify.post(
    '/refresh',
    {
      config: {
        rateLimit: { max: 20, timeWindow: '15 minutes' },
      },
    },
    async (request, reply) => {
      const rawToken = request.cookies[REFRESH_TOKEN_COOKIE]
      if (!rawToken) {
        throw new AppError('INVALID_TOKEN', 401, 'Refresh token no encontrado')
      }
      const useCase = new RefreshTokenStaff(userRepo, tokenService, refreshTokenRepo)
      const result = await useCase.execute(rawToken)

      return reply
        .status(200)
        .setCookie(REFRESH_TOKEN_COOKIE, result.refreshToken, cookieOptions(isProduction))
        .send({ data: { accessToken: result.accessToken } })
    },
  )

  // ── POST /auth/logout ────────────────────────────────────────────────────
  fastify.post('/logout', async (request, reply) => {
    const rawToken = request.cookies[REFRESH_TOKEN_COOKIE]
    if (rawToken) {
      const useCase = new LogoutStaff(tokenService, refreshTokenRepo)
      await useCase.execute(rawToken)
    }
    return reply
      .status(200)
      .clearCookie(REFRESH_TOKEN_COOKIE, { path: COOKIE_PATH })
      .send({ data: { ok: true } })
  })

  // ── POST /auth/register (solo admin) ─────────────────────────────────────
  fastify.post(
    '/register',
    {
      preHandler: createAuthGuard(tokenService, ['admin']),
    },
    async (request, reply) => {
      const body = registerBodySchema.parse(request.body)
      const useCase = new RegisterStaff(userRepo, passwordHasher)
      const result = await useCase.execute(body)
      return reply.status(201).send({ data: result })
    },
  )

  // ── GET /auth/me (cualquier rol autenticado) ──────────────────────────────
  fastify.get(
    '/me',
    {
      preHandler: createAuthGuard(tokenService, [...USER_ROLES]),
    },
    async (request, reply) => {
      const current = requireCurrentUser(request)
      const user = await userRepo.findById(current.id)
      if (!user) {
        throw new AppError('NOT_FOUND', 404, 'Usuario no encontrado')
      }
      return reply.status(200).send({
        data: {
          id: user.id,
          fullName: user.fullName,
          email: user.email,
          role: user.role,
          discountLimitPct: user.discountLimitPct,
        },
      })
    },
  )
}
