import {
  Injectable,
  NotFoundException,
  ConflictException,
  BadRequestException,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UpdateProfileDto } from './dto/update-profile.dto';
import { QueryUsersDto } from './dto/query-users.dto';
import { Prisma } from '@prisma/client';

@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Create a new user
   */
  async create(createUserDto: CreateUserDto) {
    const { roleIds, ...userData } = createUserDto;

    // Check if email already exists
    const existingUser = await this.prisma.user.findUnique({
      where: { email: userData.email },
    });

    if (existingUser) {
      throw new ConflictException('User with this email already exists');
    }

    // Check if keycloakId already exists (if provided)
    if (userData.keycloakId) {
      const existingKeycloakUser = await this.prisma.user.findUnique({
        where: { keycloakId: userData.keycloakId },
      });

      if (existingKeycloakUser) {
        throw new ConflictException('User with this Keycloak ID already exists');
      }
    }

    return this.prisma.user.create({
      data: {
        ...userData,
        roles: roleIds
          ? {
              create: roleIds.map((roleId) => ({
                role: { connect: { id: roleId } },
              })),
            }
          : undefined,
      },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });
  }

  /**
   * Find all users with optional filtering and pagination
   */
  async findAll(query: QueryUsersDto) {
    const { page = 1, limit = 20, search, departmentId, isActive, sortBy, sortOrder } = query;

    const skip = (page - 1) * limit;

    const where: Prisma.UserWhereInput = {
      ...(search && {
        OR: [
          { email: { contains: search, mode: 'insensitive' } },
          { firstName: { contains: search, mode: 'insensitive' } },
          { lastName: { contains: search, mode: 'insensitive' } },
        ],
      }),
      ...(departmentId && { departmentId }),
      ...(isActive !== undefined && { isActive }),
    };

    const orderBy: Prisma.UserOrderByWithRelationInput = sortBy
      ? { [sortBy]: sortOrder || 'asc' }
      : { createdAt: 'desc' };

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where,
        skip,
        take: limit,
        orderBy,
        include: {
          roles: {
            include: {
              role: true,
            },
          },
          department: true,
        },
      }),
      this.prisma.user.count({ where }),
    ]);

    return {
      data: users,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  /**
   * Find a single user by ID
   */
  async findOne(id: string) {
    const user = await this.prisma.user.findUnique({
      where: { id },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
        employee: true,
      },
    });

    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`);
    }

    return user;
  }

  /**
   * Find a user by email
   */
  async findByEmail(email: string) {
    const user = await this.prisma.user.findUnique({
      where: { email },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });

    if (!user) {
      throw new NotFoundException(`User with email ${email} not found`);
    }

    return user;
  }

  /**
   * Find a user by Keycloak ID
   */
  async findByKeycloakId(keycloakId: string) {
    const user = await this.prisma.user.findUnique({
      where: { keycloakId },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });

    if (!user) {
      throw new NotFoundException(`User with Keycloak ID ${keycloakId} not found`);
    }

    return user;
  }

  /**
   * Update a user
   */
  async update(id: string, updateUserDto: UpdateUserDto) {
    const { roleIds, ...userData } = updateUserDto;

    // Check if user exists
    await this.findOne(id);

    // Check email uniqueness if updating email
    if (userData.email) {
      const existingUser = await this.prisma.user.findFirst({
        where: {
          email: userData.email,
          id: { not: id },
        },
      });

      if (existingUser) {
        throw new ConflictException('User with this email already exists');
      }
    }

    // Update user with role management
    return this.prisma.user.update({
      where: { id },
      data: {
        ...userData,
        ...(roleIds && {
          roles: {
            deleteMany: {},
            create: roleIds.map((roleId) => ({
              role: { connect: { id: roleId } },
            })),
          },
        }),
      },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });
  }

  /**
   * Delete a user (soft delete by deactivating)
   */
  async remove(id: string) {
    await this.findOne(id);

    return this.prisma.user.update({
      where: { id },
      data: { isActive: false },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });
  }

  /**
   * Permanently delete a user (hard delete)
   */
  async hardDelete(id: string) {
    await this.findOne(id);

    // Check if user has related data
    const hasEmployee = await this.prisma.employee.findUnique({
      where: { userId: id },
    });

    if (hasEmployee) {
      throw new BadRequestException(
        'Cannot delete user with employee data. Deactivate instead.',
      );
    }

    return this.prisma.user.delete({
      where: { id },
    });
  }

  /**
   * Reactivate a deactivated user
   */
  async reactivate(id: string) {
    const user = await this.findOne(id);

    if (user.isActive) {
      throw new BadRequestException('User is already active');
    }

    return this.prisma.user.update({
      where: { id },
      data: { isActive: true },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });
  }

  /**
   * Assign roles to a user
   */
  async assignRoles(id: string, roleIds: string[]) {
    await this.findOne(id);

    // Validate all roles exist
    const roles = await this.prisma.role.findMany({
      where: { id: { in: roleIds } },
    });

    if (roles.length !== roleIds.length) {
      throw new BadRequestException('One or more roles not found');
    }

    // Remove existing roles and add new ones
    await this.prisma.userRole.deleteMany({
      where: { userId: id },
    });

    return this.prisma.user.update({
      where: { id },
      data: {
        roles: {
          create: roleIds.map((roleId) => ({
            role: { connect: { id: roleId } },
          })),
        },
      },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
      },
    });
  }

  /**
   * Get current user's profile
   */
  async getMyProfile(userId: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
        employee: true,
        preferences: true,
      },
    });

    if (!user) {
      throw new NotFoundException(`User with ID ${userId} not found`);
    }

    return user;
  }

  /**
   * Update current user's profile (limited fields)
   */
  async updateMyProfile(userId: string, updateDto: UpdateProfileDto) {
    await this.findOne(userId);

    return this.prisma.user.update({
      where: { id: userId },
      data: {
        ...(updateDto.firstName && { firstName: updateDto.firstName }),
        ...(updateDto.lastName && { lastName: updateDto.lastName }),
        ...(updateDto.avatar !== undefined && { avatar: updateDto.avatar }),
      },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
        department: true,
        employee: true,
      },
    });
  }

  /**
   * Get user activity (audit logs)
   */
  async getUserActivity(
    userId: string,
    pagination: { page?: number; limit?: number } = {},
  ) {
    await this.findOne(userId);

    const { page = 1, limit = 20 } = pagination;
    const skip = (page - 1) * limit;

    const [logs, total] = await Promise.all([
      this.prisma.auditLog.findMany({
        where: { userId },
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.auditLog.count({ where: { userId } }),
    ]);

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