import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User, user_role, Prisma } from '@prisma/client';
import * as bcrypt from 'bcrypt';
import { v4 as uuidv4 } from 'uuid';

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

  async create(createUserDto: CreateUserDto, creatorRole?: string): Promise<User> {
    const salt = await bcrypt.genSalt();
    const hashedPassword = await bcrypt.hash(createUserDto.password, salt);

    const defaultPermissions = this.getDefaultPermissions(createUserDto.role || 'TELESALES_EXECUTIVE');

    return this.prisma.user.create({
      data: {
        ...createUserDto,
        id: uuidv4(),
        password: hashedPassword,
        permissions: JSON.stringify(defaultPermissions),
        status: (creatorRole === 'ADMIN' || creatorRole === 'GENERAL_MANAGER') ? 'APPROVED' : 'PENDING',
        updatedAt: new Date(),
      },
    });
  }

  private getDefaultPermissions(role: string): string[] {
    const common = ['dashboard', 'opportunities', 'clients', 'activities', 'products'];
    switch (role) {
      case 'ADMIN':
      case 'GENERAL_MANAGER':
        return [...common, 'tracking', 'targets', 'incentives', 'reports', 'funnel-analysis', 'users', 'settings', 'expenses', 'call-logs'];
      case 'MANAGER':
        return [...common, 'reports', 'incentives', 'targets', 'call-logs'];
      default:
        return common;
    }
  }

  async findAll() {
    return this.prisma.user.findMany();
  }

  async findOne(id: string) {
    return this.prisma.user.findUnique({ where: { id } });
  }

  async findAllByIds(ids: string[]) {
    return this.prisma.user.findMany({
      where: { id: { in: ids } },
      select: { id: true, name: true, role: true, email: true }
    });
  }

  async findByEmail(email: string) {
    return this.prisma.user.findUnique({ where: { email } });
  }

  async update(id: string, updateUserDto: UpdateUserDto) {
    if (updateUserDto.password) {
      const salt = await bcrypt.genSalt();
      updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt);
    }
    return this.prisma.user.update({
      where: { id },
      data: updateUserDto,
    });
  }

  async remove(id: string) {
    return this.prisma.user.delete({ where: { id } });
  }

  async updateStatus(id: string, status: 'PENDING' | 'APPROVED' | 'REJECTED') {
    return this.prisma.user.update({
      where: { id },
      data: { status } as any, // Cast to any to bypass schema generation issues if not fully synced
    });
  }

  /**
   * Recursively fetches all subordinate user IDs for a given user.
   * This is used for hierarchical data filtering.
   */
  async getSubordinateIds(userId: string): Promise<string[]> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      include: { subordinates: true },
    });

    if (!user || !user.subordinates || user.subordinates.length === 0) {
      return [];
    }

    let ids = user.subordinates.map((sub) => sub.id);

    // Recursively collect IDs from subordinates
    for (const sub of user.subordinates) {
      const nestedIds = await this.getSubordinateIds(sub.id);
      ids = [...ids, ...nestedIds];
    }

    return ids;
  }

  async changePassword(userId: string, oldPassword: string, newPassword: string) {
    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) {
      throw new Error('User not found');
    }

    const isMatch = await bcrypt.compare(oldPassword, user.password);
    if (!isMatch) {
      throw new Error('Invalid current password');
    }

    const salt = await bcrypt.genSalt();
    const hashedPassword = await bcrypt.hash(newPassword, salt);

    return this.prisma.user.update({
      where: { id: userId },
      data: { password: hashedPassword },
    });
  }
}
