import { User, UserSession } from '@prisma/client'
import { prisma } from '@/lib/db/prisma'
import { hashPassword, verifyPassword } from '@/lib/utils/password'
import {
  generateAccessToken,
  generateRefreshToken,
  getRefreshTokenExpirationTime
} from '@/lib/utils/jwt'
import { AuthTokens, RegisterData, LoginCredentials } from '@/types/auth'
import { randomBytes } from 'crypto'

export class AuthService {
  async register(data: RegisterData): Promise<{
    user: Omit<User, 'passwordHash'>
    tokens: AuthTokens
  }> {
    // 检查用户是否已存在
    const existingUser = await prisma.user.findUnique({
      where: { email: data.email }
    })

    if (existingUser) {
      throw new Error('该邮箱已被注册')
    }

    // 创建用户
    const passwordHash = await hashPassword(data.password)
    const user = await prisma.user.create({
      data: {
        email: data.email,
        passwordHash,
        name: data.name,
        status: 'PENDING'
      }
    })

    // 生成令牌
    const tokens = await this.generateTokens(user)

    // 发送验证邮件
    await this.sendVerificationEmail(user.id, user.email)

    return {
      user: this.sanitizeUser(user),
      tokens
    }
  }

  async login(credentials: LoginCredentials, deviceInfo?: {
    ipAddress?: string
    userAgent?: string
    deviceInfo?: string
  }): Promise<{
    user: Omit<User, 'passwordHash'>
    tokens: AuthTokens
  }> {
    // 查找用户
    const user = await prisma.user.findUnique({
      where: { email: credentials.email }
    })

    if (!user) {
      throw new Error('邮箱或密码错误')
    }

    // 验证密码
    const isPasswordValid = await verifyPassword(credentials.password, user.passwordHash)
    if (!isPasswordValid) {
      throw new Error('邮箱或密码错误')
    }

    // 检查用户状态
    if (user.status === 'SUSPENDED') {
      throw new Error('账户已被暂停，请联系管理员')
    }

    if (user.status === 'INACTIVE') {
      throw new Error('账户已被停用')
    }

    // 更新最后活跃时间
    await prisma.user.update({
      where: { id: user.id },
      data: { lastActiveAt: new Date() }
    })

    // 生成令牌
    const tokens = await this.generateTokens(user, deviceInfo)

    return {
      user: this.sanitizeUser(user),
      tokens
    }
  }

  async refreshTokens(refreshToken: string): Promise<AuthTokens> {
    // 查找会话
    const session = await prisma.userSession.findUnique({
      where: { refreshToken },
      include: { user: true }
    })

    if (!session) {
      throw new Error('无效的刷新令牌')
    }

    // 检查令牌是否过期
    if (session.expiresAt < new Date()) {
      // 删除过期的会话
      await prisma.userSession.delete({
        where: { id: session.id }
      })
      throw new Error('刷新令牌已过期')
    }

    // 检查用户状态
    if (session.user.status !== 'ACTIVE') {
      throw new Error('用户账户状态异常')
    }

    // 生成新的令牌
    const accessToken = generateAccessToken(
      session.user.id,
      session.user.email,
      session.user.role
    )
    const newRefreshToken = generateRefreshToken()
    const expiresAt = getRefreshTokenExpirationTime()

    // 更新会话
    await prisma.userSession.update({
      where: { id: session.id },
      data: {
        refreshToken: newRefreshToken,
        expiresAt,
        lastUsedAt: new Date()
      }
    })

    return {
      accessToken,
      refreshToken: newRefreshToken,
      expiresIn: 3600 // 1小时
    }
  }

  async logout(refreshToken: string): Promise<void> {
    // 删除会话
    await prisma.userSession.deleteMany({
      where: { refreshToken }
    })
  }

  async logoutAllDevices(userId: string): Promise<void> {
    // 删除用户的所有会话
    await prisma.userSession.deleteMany({
      where: { userId }
    })
  }

  async generateTokens(user: User, deviceInfo?: {
    ipAddress?: string
    userAgent?: string
    deviceInfo?: string
  }): Promise<AuthTokens> {
    const accessToken = generateAccessToken(user.id, user.email, user.role)
    const refreshToken = generateRefreshToken()
    const expiresAt = getRefreshTokenExpirationTime()

    // 创建会话记录
    await prisma.userSession.create({
      data: {
        userId: user.id,
        refreshToken,
        expiresAt,
        ipAddress: deviceInfo?.ipAddress,
        userAgent: deviceInfo?.userAgent,
        deviceInfo: deviceInfo?.deviceInfo
      }
    })

    return {
      accessToken,
      refreshToken,
      expiresIn: 3600 // 1小时
    }
  }

  async sendVerificationEmail(userId: string, email: string): Promise<void> {
    // 生成验证令牌
    const token = randomBytes(32).toString('hex')
    const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000) // 24小时

    // 保存验证记录
    await prisma.emailVerification.create({
      data: {
        userId,
        token,
        expiresAt
      }
    })

    // TODO: 发送邮件
    console.log(`Email verification token for ${email}: ${token}`)
  }

  async verifyEmail(token: string): Promise<void> {
    // 查找验证记录
    const verification = await prisma.emailVerification.findUnique({
      where: { token },
      include: { user: true }
    })

    if (!verification) {
      throw new Error('无效的验证令牌')
    }

    if (verification.expiresAt < new Date()) {
      throw new Error('验证令牌已过期')
    }

    if (verification.verifiedAt) {
      throw new Error('邮箱已经验证过了')
    }

    // 更新用户状态
    await prisma.$transaction([
      prisma.user.update({
        where: { id: verification.userId },
        data: {
          status: 'ACTIVE',
          emailVerified: new Date()
        }
      }),
      prisma.emailVerification.update({
        where: { id: verification.id },
        data: { verifiedAt: new Date() }
      })
    ])
  }

  async requestPasswordReset(email: string): Promise<void> {
    // 查找用户
    const user = await prisma.user.findUnique({
      where: { email }
    })

    if (!user) {
      // 为了安全，不暴露用户是否存在
      return
    }

    // 生成重置令牌
    const token = randomBytes(32).toString('hex')
    const expiresAt = new Date(Date.now() + 1 * 60 * 60 * 1000) // 1小时

    // 保存重置记录
    await prisma.passwordReset.create({
      data: {
        userId: user.id,
        token,
        expiresAt
      }
    })

    // TODO: 发送重置邮件
    console.log(`Password reset token for ${email}: ${token}`)
  }

  async resetPassword(token: string, newPassword: string): Promise<void> {
    // 查找重置记录
    const reset = await prisma.passwordReset.findUnique({
      where: { token }
    })

    if (!reset) {
      throw new Error('无效的重置令牌')
    }

    if (reset.expiresAt < new Date()) {
      throw new Error('重置令牌已过期')
    }

    if (reset.usedAt) {
      throw new Error('重置令牌已使用过')
    }

    // 更新密码
    const passwordHash = await hashPassword(newPassword)
    
    await prisma.$transaction([
      prisma.user.update({
        where: { id: reset.userId },
        data: { passwordHash }
      }),
      prisma.passwordReset.update({
        where: { id: reset.id },
        data: { usedAt: new Date() }
      }),
      // 删除用户的所有会话，强制重新登录
      prisma.userSession.deleteMany({
        where: { userId: reset.userId }
      })
    ])
  }

  async updateProfile(userId: string, data: { name?: string; avatar?: string }): Promise<User> {
    const user = await prisma.user.update({
      where: { id: userId },
      data
    })

    return this.sanitizeUser(user)
  }

  async changePassword(userId: string, currentPassword: string, newPassword: string): Promise<void> {
    // 获取用户信息
    const user = await prisma.user.findUnique({
      where: { id: userId }
    })

    if (!user) {
      throw new Error('用户不存在')
    }

    // 验证当前密码
    const isCurrentPasswordValid = await verifyPassword(currentPassword, user.passwordHash)
    if (!isCurrentPasswordValid) {
      throw new Error('当前密码错误')
    }

    // 更新密码
    const passwordHash = await hashPassword(newPassword)
    await prisma.user.update({
      where: { id: userId },
      data: { passwordHash }
    })

    // 删除除当前会话外的所有会话
    // 注意：在实际应用中，你需要传入当前的refreshToken来保留当前会话
  }

  async getUserSessions(userId: string): Promise<UserSession[]> {
    return prisma.userSession.findMany({
      where: { userId },
      orderBy: { lastUsedAt: 'desc' }
    })
  }

  async revokeSession(sessionId: string, userId: string): Promise<void> {
    await prisma.userSession.deleteMany({
      where: {
        id: sessionId,
        userId
      }
    })
  }

  private sanitizeUser(user: User): Omit<User, 'passwordHash'> {
    const { passwordHash, ...sanitizedUser } = user
    return sanitizedUser
  }

  async getUserById(userId: string): Promise<Omit<User, 'passwordHash'> | null> {
    const user = await prisma.user.findUnique({
      where: { id: userId }
    })

    if (!user) {
      return null
    }

    return this.sanitizeUser(user)
  }

  async getUserByEmail(email: string): Promise<Omit<User, 'passwordHash'> | null> {
    const user = await prisma.user.findUnique({
      where: { email }
    })

    if (!user) {
      return null
    }

    return this.sanitizeUser(user)
  }
}

export const authService = new AuthService()