import bcrypt from 'bcryptjs'
import jwt, { Secret, SignOptions } from 'jsonwebtoken'
import prisma from '../../config/database'
import { config } from '../../config'
import { AppError } from '../../shared/middleware/errorHandler'

class AuthService {
  async register(data: {
    email: string
    username: string
    password: string
    employeeId?: string
  }) {
    const hashedPassword = await bcrypt.hash(
      data.password,
      config.security.bcryptRounds
    )

    const user = await prisma.user.create({
      data: {
        email: data.email,
        username: data.username,
        password: hashedPassword,
        employeeId: data.employeeId
      },
      select: {
        id: true,
        email: true,
        username: true,
        employeeId: true,
        isActive: true,
        createdAt: true
      }
    })

    const tokens = this.generateTokens(user.id, user.email)

    await prisma.user.update({
      where: { id: user.id },
      data: { refreshToken: tokens.refreshToken }
    })

    return {
      user,
      ...tokens
    }
  }

  
  async login(emailOrUsername: string, password: string) {
    const identifier = (emailOrUsername || '').trim()
    const isEmail = identifier.includes('@')

    let user: any = null

    if (isEmail) {
      const matches = await prisma.user.findMany({
        where: { email: identifier },
        include: {
          employee: {
            include: {
              position: { include: { permissions: true } },
              department: true
            }
          }
        }
      })

      if (matches.length === 0) {
        throw new AppError(401, 'بيانات الدخول غير صحيحة - Invalid credentials')
      }

      if (matches.length > 1) {
        throw new AppError(
          400,
          'هذا البريد مستخدم لأكثر من حساب. الرجاء تسجيل الدخول باسم المستخدم - Use username'
        )
      }

      user = matches[0]
    } else {
      user = await prisma.user.findUnique({
        where: { username: identifier },
        include: {
          employee: {
            include: {
              position: { include: { permissions: true } },
              department: true
            }
          }
        }
      })

      if (!user) {
        throw new AppError(401, 'بيانات الدخول غير صحيحة - Invalid credentials')
      }
    }

    if (!user.isActive) {
      throw new AppError(403, 'الحساب غير مفعل - Account is inactive')
    }

    if (user.lockedUntil && user.lockedUntil > new Date()) {
      throw new AppError(403, 'الحساب مقفل مؤقتاً - Account is temporarily locked')
    }

    const isPasswordValid = await bcrypt.compare(password, user.password)

    if (!isPasswordValid) {
      const failedAttempts = (user.failedLoginAttempts || 0) + 1
      const updateData: any = { failedLoginAttempts: failedAttempts }

      if (failedAttempts >= 5) {
        updateData.lockedUntil = new Date(Date.now() + 30 * 60 * 1000)
      }

      await prisma.user.update({
        where: { id: user.id },
        data: updateData
      })

      throw new AppError(401, 'بيانات الدخول غير صحيحة - Invalid credentials')
    }

    if (!user.employee || user.employee.status !== 'ACTIVE') {
      throw new AppError(
        403,
        'الوصول مرفوض - Access denied. Active employee record required.'
      )
    }

    await prisma.user.update({
      where: { id: user.id },
      data: {
        failedLoginAttempts: 0,
        lockedUntil: null,
        lastLogin: new Date()
      }
    })

    const tokens = this.generateTokens(user.id, user.email)

    await prisma.user.update({
      where: { id: user.id },
      data: { refreshToken: tokens.refreshToken }
    })

    const { password: _pw, ...userWithoutPassword } = user

    const role = user.employee?.position
      ? {
          id: user.employee.position.id,
          name: user.employee.position.titleAr || user.employee.position.title,
          nameEn: user.employee.position.title,
          permissions: user.employee.position.permissions || []
        }
      : null

    return {
      user: {
        ...userWithoutPassword,
        role
      },
      ...tokens
    }
  }

  async getUserById(userId: string) {
    const user = await prisma.user.findUnique({
      where: { id: userId },
      include: {
        employee: {
          include: {
            position: { include: { permissions: true } },
            department: true
          }
        }
      }
    })

    if (!user) throw new AppError(404, 'المستخدم غير موجود - User not found')
    if (!user.isActive) throw new AppError(403, 'الحساب غير مفعل - Account is inactive')

    const { password: _pw, ...userWithoutPassword } = user

    const role = user.employee?.position
      ? {
          id: user.employee.position.id,
          name: user.employee.position.titleAr || user.employee.position.title,
          nameEn: user.employee.position.title,
          permissions: user.employee.position.permissions || []
        }
      : null

    return { ...userWithoutPassword, role }
  }

  async refreshToken(refreshToken: string) {
    try {
      const decoded = jwt.verify(refreshToken, config.jwt.secret) as { id: string; email: string }

      const user = await prisma.user.findUnique({ where: { id: decoded.id } })
      if (!user || user.refreshToken !== refreshToken || !user.isActive) {
        throw new AppError(401, 'رمز غير صالح - Invalid token')
      }

      const tokens = this.generateTokens(user.id, user.email)

      await prisma.user.update({
        where: { id: user.id },
        data: { refreshToken: tokens.refreshToken }
      })

      return tokens
    } catch {
      throw new AppError(401, 'رمز غير صالح - Invalid token')
    }
  }

  async logout(userId: string) {
    await prisma.user.update({
      where: { id: userId },
      data: { refreshToken: null }
    })
  }

  async getUserProfile(userId: string) {
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        email: true,
        username: true,
        isActive: true,
        lastLogin: true,
        employee: {
          include: {
            position: { include: { permissions: true } },
            department: true
          }
        }
      }
    })

    if (!user) throw new AppError(404, 'المستخدم غير موجود - User not found')
    return user
  }

  private generateTokens(userId: string, email: string) {
    const payload = { id: userId, email }
    const secret = config.jwt.secret as Secret

    const accessToken = jwt.sign(payload, secret, {
      expiresIn: config.jwt.expiresIn
    } as SignOptions)

    const refreshToken = jwt.sign(payload, secret, {
      expiresIn: config.jwt.refreshExpiresIn
    } as SignOptions)

    return {
      accessToken,
      refreshToken,
      expiresIn: config.jwt.expiresIn
    }
  }
}

export const authService = new AuthService()