import bcrypt from 'bcryptjs';
import prisma from '../../config/database';
import { AppError } from '../../shared/middleware/errorHandler';
import { AuditLogger } from '../../shared/utils/auditLogger';
import { config } from '../../config';
import { Prisma } from '@prisma/client';

export interface UserFilters {
  search?: string;
  isActive?: boolean;
  positionId?: string;
  page?: number;
  pageSize?: number;
}

export interface CreateUserData {
  email: string;
  username: string;
  password: string;
  employeeId: string;
  isActive?: boolean;
}

export interface UpdateUserData {
  email?: string;
  username?: string;
  password?: string;
  employeeId?: string;
  isActive?: boolean;
}

export interface AuditLogFilters {
  entityType?: string;
  action?: string;
  userId?: string;
  startDate?: string;
  endDate?: string;
  page?: number;
  pageSize?: number;
}

class AdminService {
  // ========== USERS ==========

  async findAllUsers(filters: UserFilters) {
    const page = filters.page || 1;
    const pageSize = Math.min(filters.pageSize || 20, 100);
    const skip = (page - 1) * pageSize;

    const where: Prisma.UserWhereInput = {};

    if (filters.isActive !== undefined) {
      where.isActive = filters.isActive;
    }

    if (filters.positionId) {
      where.employee = {
        positionId: filters.positionId,
      };
    }

    if (filters.search?.trim()) {
      const search = filters.search.trim().toLowerCase();
      where.OR = [
        { username: { contains: search, mode: 'insensitive' } },
        { email: { contains: search, mode: 'insensitive' } },
        {
          employee: {
            OR: [
              { firstName: { contains: search, mode: 'insensitive' } },
              { lastName: { contains: search, mode: 'insensitive' } },
            ],
          },
        },
      ];
    }

    const [users, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip,
        take: pageSize,
        include: {
          employee: {
            include: {
              position: { select: { id: true, title: true, titleAr: true } },
              department: { select: { name: true, nameAr: true } },
            },
          },
        },
        orderBy: { createdAt: 'desc' },
      }),
      prisma.user.count({ where }),
    ]);

    const sanitized = users.map((u) => {
      const { password: _, ...rest } = u;
      return rest;
    });

    return {
      users: sanitized,
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

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

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

    const { password: _, ...rest } = user;
    return rest;
  }

  async createUser(data: CreateUserData, createdById: string) {
    const employee = await prisma.employee.findUnique({
      where: { id: data.employeeId },
    });

    if (!employee) {
      throw new AppError(400, 'الموظف غير موجود - Employee not found');
    }

    if (employee.status !== 'ACTIVE') {
      throw new AppError(400, 'الموظف غير نشط - Employee must be ACTIVE');
    }

    const existingUser = await prisma.user.findFirst({
      where: { employeeId: data.employeeId },
    });
    if (existingUser) {
      throw new AppError(400, 'هذا الموظف مرتبط بحساب مستخدم بالفعل - Employee already has a user account');
    }

    const emailExists = await prisma.user.findFirst({
      where: { email: data.email },
    });
    if (emailExists) {
      throw new AppError(400, 'البريد الإلكتروني مستخدم بالفعل - Email already in use');
    }

    const usernameExists = await prisma.user.findUnique({
      where: { username: data.username },
    });
    if (usernameExists) {
      throw new AppError(400, 'اسم المستخدم مستخدم بالفعل - Username already in use');
    }

    const hashedPassword = await bcrypt.hash(data.password, config.security?.bcryptRounds || 10);

    const user = await prisma.user.create({
      data: {
        email: data.email,
        username: data.username,
        password: hashedPassword,
        employeeId: data.employeeId,
        isActive: data.isActive ?? true,
      },
      select: {
        id: true,
        email: true,
        username: true,
        employeeId: true,
        isActive: true,
        lastLogin: true,
        createdAt: true,
        employee: {
          include: {
            position: { select: { title: true, titleAr: true } },
          },
        },
      },
    });

    await AuditLogger.log({
      entityType: 'USER',
      entityId: user.id,
      action: 'CREATE',
      userId: createdById,
      changes: { created: { email: user.email, username: user.username } },
    });

    return user;
  }

  async updateUser(id: string, data: UpdateUserData, updatedById: string) {
    const existing = await prisma.user.findUnique({ where: { id } });
    if (!existing) {
      throw new AppError(404, 'المستخدم غير موجود - User not found');
    }

    if (data.email && data.email !== existing.email) {
      const emailExists = await prisma.user.findFirst({ where: { email: data.email } });
      if (emailExists) {
        throw new AppError(400, 'البريد الإلكتروني مستخدم بالفعل - Email already in use');
      }
    }

    if (data.username && data.username !== existing.username) {
      const usernameExists = await prisma.user.findUnique({ where: { username: data.username } });
      if (usernameExists) {
        throw new AppError(400, 'اسم المستخدم مستخدم بالفعل - Username already in use');
      }
    }

    const updateData: Prisma.UserUpdateInput = {
      ...(data.email && { email: data.email }),
      ...(data.username && { username: data.username }),
      ...(data.isActive !== undefined && { isActive: data.isActive }),
      ...(data.employeeId !== undefined && { employeeId: data.employeeId || null }),
    };

    if (data.password && data.password.length >= 8) {
      updateData.password = await bcrypt.hash(data.password, config.security?.bcryptRounds || 10);
    }

    const user = await prisma.user.update({
      where: { id },
      data: updateData,
      include: {
        employee: {
          include: {
            position: { select: { title: true, titleAr: true } },
          },
        },
      },
    });

    const { password: _, ...sanitized } = user;

    await AuditLogger.log({
      entityType: 'USER',
      entityId: id,
      action: 'UPDATE',
      userId: updatedById,
      changes: { before: existing, after: sanitized },
    });

    return sanitized;
  }

  async toggleUserActive(id: string, userId: string) {
    const user = await prisma.user.findUnique({ where: { id } });
    if (!user) {
      throw new AppError(404, 'المستخدم غير موجود - User not found');
    }

    const updated = await prisma.user.update({
      where: { id },
      data: { isActive: !user.isActive },
      include: {
        employee: {
          include: {
            position: { select: { title: true, titleAr: true } },
          },
        },
      },
    });

    const { password: _, ...sanitized } = updated;

    await AuditLogger.log({
      entityType: 'USER',
      entityId: id,
      action: user.isActive ? 'DEACTIVATE' : 'ACTIVATE',
      userId,
      changes: { isActive: updated.isActive },
    });

    return sanitized;
  }

  async deleteUser(id: string, deletedById: string) {
    const user = await prisma.user.findUnique({ where: { id } });
    if (!user) {
      throw new AppError(404, 'المستخدم غير موجود - User not found');
    }

    await prisma.user.update({
      where: { id },
      data: { isActive: false },
    });

    await AuditLogger.log({
      entityType: 'USER',
      entityId: id,
      action: 'DELETE',
      userId: deletedById,
      changes: { softDeleted: true, email: user.email },
    });

    return { success: true, message: 'User deactivated successfully' };
  }

  // ========== AUDIT LOGS ==========

  async getAuditLogs(filters: AuditLogFilters) {
    const page = filters.page || 1;
    const pageSize = Math.min(filters.pageSize || 20, 100);
    const skip = (page - 1) * pageSize;

    const where: Prisma.AuditLogWhereInput = {};

    if (filters.entityType) where.entityType = filters.entityType;
    if (filters.action) where.action = filters.action;
    if (filters.userId) where.userId = filters.userId;

    if (filters.startDate || filters.endDate) {
      where.createdAt = {};
      if (filters.startDate) where.createdAt.gte = new Date(filters.startDate);
      if (filters.endDate) where.createdAt.lte = new Date(filters.endDate);
    }

    const [logs, total] = await Promise.all([
      prisma.auditLog.findMany({
        where,
        skip,
        take: pageSize,
        include: {
          user: {
            select: { id: true, username: true, email: true },
          },
        },
        orderBy: { createdAt: 'desc' },
      }),
      prisma.auditLog.count({ where }),
    ]);

    return {
      logs,
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  // ========== STATS ==========

  async getStats() {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const [totalUsers, activeUsers, inactiveUsers, loginsToday] = await Promise.all([
      prisma.user.count(),
      prisma.user.count({ where: { isActive: true } }),
      prisma.user.count({ where: { isActive: false } }),
      prisma.user.count({
        where: { lastLogin: { gte: today } },
      }),
    ]);

    return {
      totalUsers,
      activeUsers,
      inactiveUsers,
      loginsToday,
    };
  }

  // ========== POSITIONS (ROLES) ==========

  async getPositions() {
    const positions = await prisma.position.findMany({
      where: { isActive: true },
      include: {
        department: { select: { name: true, nameAr: true } },
        permissions: true,
        _count: {
          select: {
            employees: true,
          },
        },
      },
      orderBy: { level: 'asc' },
    });

    const withUserCount = await Promise.all(
      positions.map(async (p) => {
        const usersCount = await prisma.user.count({
          where: {
            employee: { positionId: p.id },
            isActive: true,
          },
        });
        return { ...p, usersCount };
      })
    );

    return withUserCount;
  }

  async createPosition(data: {
    title: string;
    titleAr?: string;
    code: string;
    departmentId: string;
    level?: number;
    description?: string;
    isActive?: boolean;
  }) {
    const existing = await prisma.position.findUnique({
      where: { code: data.code },
    });
    if (existing) {
      throw new AppError(400, 'كود الدور مستخدم - Position code already exists');
    }

    const dept = await prisma.department.findUnique({
      where: { id: data.departmentId },
    });
    if (!dept) {
      throw new AppError(400, 'القسم غير موجود - Department not found');
    }

    return prisma.position.create({
      data: {
        title: data.title,
        titleAr: data.titleAr,
        code: data.code.trim().toUpperCase().replace(/\s+/g, '_'),
        departmentId: data.departmentId,
        level: data.level ?? 5,
        description: data.description,
        isActive: data.isActive ?? true,
      },
      include: {
        department: { select: { name: true, nameAr: true } },
        permissions: true,
      },
    });
  }

  async updatePosition(
    positionId: string,
    data: {
      title?: string;
      titleAr?: string;
      code?: string;
      departmentId?: string;
      level?: number;
      description?: string;
      isActive?: boolean;
    }
  ) {
    const position = await prisma.position.findUnique({
      where: { id: positionId },
    });
    if (!position) {
      throw new AppError(404, 'الدور غير موجود - Position not found');
    }

    if (data.code && data.code !== position.code) {
      const existing = await prisma.position.findUnique({
        where: { code: data.code },
      });
      if (existing) {
        throw new AppError(400, 'كود الدور مستخدم - Position code already exists');
      }
    }

    if (data.departmentId && data.departmentId !== position.departmentId) {
      const dept = await prisma.department.findUnique({
        where: { id: data.departmentId },
      });
      if (!dept) {
        throw new AppError(400, 'القسم غير موجود - Department not found');
      }
    }

    const updateData: Record<string, any> = {};
    if (data.title !== undefined) updateData.title = data.title;
    if (data.titleAr !== undefined) updateData.titleAr = data.titleAr;
    if (data.code !== undefined) updateData.code = data.code.trim().toUpperCase().replace(/\s+/g, '_');
    if (data.departmentId !== undefined) updateData.departmentId = data.departmentId;
    if (data.level !== undefined) updateData.level = data.level;
    if (data.description !== undefined) updateData.description = data.description;
    if (data.isActive !== undefined) updateData.isActive = data.isActive;

    return prisma.position.update({
      where: { id: positionId },
      data: updateData,
      include: {
        department: { select: { name: true, nameAr: true } },
        permissions: true,
      },
    });
  }

  async updatePositionPermissions(positionId: string, permissions: Array<{ module: string; resource: string; actions: string[] }>) {
    const position = await prisma.position.findUnique({ where: { id: positionId } });
    if (!position) {
      throw new AppError(404, 'الدور غير موجود - Position not found');
    }

    await prisma.positionPermission.deleteMany({
      where: { positionId },
    });

    if (permissions.length > 0) {
      await prisma.positionPermission.createMany({
        data: permissions.map((p) => ({
          positionId,
          module: p.module,
          resource: p.resource,
          actions: p.actions,
        })),
      });
    }

    return this.getPositions().then((pos) => pos.find((p) => p.id === positionId) || position);
  }

  // ========== PERMISSION GROUPS (Phase 3 - optional roles for multi-group) ==========

  async getPermissionGroups() {
    return prisma.role.findMany({
      where: { isActive: true },
      include: {
        permissions: true,
        _count: { select: { userRoles: true } },
      },
      orderBy: { name: 'asc' },
    });
  }

  async createPermissionGroup(data: { name: string; nameAr?: string; description?: string }) {
    const existing = await prisma.role.findUnique({ where: { name: data.name } });
    if (existing) {
      throw new AppError(400, 'اسم المجموعة مستخدم - Group name already exists');
    }
    return prisma.role.create({
      data: {
        name: data.name,
        nameAr: data.nameAr,
        description: data.description,
      },
      include: { permissions: true },
    });
  }

  async updatePermissionGroup(
    id: string,
    data: { name?: string; nameAr?: string; description?: string; isActive?: boolean }
  ) {
    const role = await prisma.role.findUnique({ where: { id } });
    if (!role) {
      throw new AppError(404, 'المجموعة غير موجودة - Group not found');
    }
    if (data.name && data.name !== role.name) {
      const existing = await prisma.role.findUnique({ where: { name: data.name } });
      if (existing) {
        throw new AppError(400, 'اسم المجموعة مستخدم - Group name already exists');
      }
    }
    return prisma.role.update({
      where: { id },
      data,
      include: { permissions: true },
    });
  }

  async updatePermissionGroupPermissions(
    roleId: string,
    permissions: Array<{ module: string; resource: string; actions: string[] }>
  ) {
    await prisma.rolePermission.deleteMany({ where: { roleId } });
    if (permissions.length > 0) {
      await prisma.rolePermission.createMany({
        data: permissions.map((p) => ({
          roleId,
          module: p.module,
          resource: p.resource,
          actions: p.actions,
        })),
      });
    }
    return prisma.role.findUnique({
      where: { id: roleId },
      include: { permissions: true },
    });
  }

  async getUserRoles(userId: string) {
    return prisma.userRole.findMany({
      where: { userId },
      include: {
        role: { include: { permissions: true } },
      },
    });
  }

  async assignUserRole(userId: string, roleId: string) {
    const [user, role] = await Promise.all([
      prisma.user.findUnique({ where: { id: userId } }),
      prisma.role.findFirst({ where: { id: roleId, isActive: true } }),
    ]);
    if (!user) throw new AppError(404, 'المستخدم غير موجود - User not found');
    if (!role) throw new AppError(404, 'المجموعة غير موجودة - Group not found');

    const existing = await prisma.userRole.findUnique({
      where: { userId_roleId: { userId, roleId } },
    });
    if (existing) {
      throw new AppError(400, 'المستخدم منتمي بالفعل لهذه المجموعة - User already in group');
    }

    return prisma.userRole.create({
      data: { userId, roleId },
      include: { role: true },
    });
  }

  async removeUserRole(userId: string, roleId: string) {
    const deleted = await prisma.userRole.deleteMany({
      where: { userId, roleId },
    });
    if (deleted.count === 0) {
      throw new AppError(404, 'لم يتم العثور على الانتماء - User not in group');
    }
    return { success: true };
  }
}

export const adminService = new AdminService();
