import bcrypt from "bcryptjs";
import { UserRepository } from "../repositories/UserRepository.js";
import role from "../constants/role.js";

export class StaffService {
  constructor(private readonly userRepository = new UserRepository()) {}

  async createStaff(data: any) {
    const superAdminRoleId = BigInt(role.ADMIN);

    if (BigInt(data.roleId) === superAdminRoleId) {
      const existingSuperAdmins = await this.userRepository.countByRoleId(superAdminRoleId);
      if (existingSuperAdmins > 0) {
        throw new Error("Only one Super Admin can exist in the system");
      }
      if (data.status === false) {
        throw new Error("Super Admin account must be active");
      }
    } else {
      const activeSuperAdmins = await this.userRepository.countByRoleId(superAdminRoleId, undefined, true);
      if (activeSuperAdmins === 0) {
        throw new Error("One active Super Admin must exist in the system");
      }
    }

    if (data.email) {
      const existingEmail = await this.userRepository.findByEmail(data.email);
      if (existingEmail) {
        throw new Error("Email already in use");
      }
    }

    if (data.mobileNo) {
      const existingMobile = await this.userRepository.findByMobileNo(data.mobileNo);
      if (existingMobile) {
        throw new Error("Mobile number already in use");
      }
    }

    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(data.password, salt);

    const created = await this.userRepository.create({
      ...data,
      password: hashedPassword,
    });
    const { password, ...rest } = created;
    return rest;
  }

  async getStaff(page: number = 1, limit: number = 10, search?: string, roleId?: bigint, status?: boolean) {
    const skip = (page - 1) * limit;
    const { items, total } = await this.userRepository.findAll(skip, limit, search, roleId, status);
    
    // Omit passwords from the response
    const staffList = items.map(user => {
      const { password, ...rest } = user;
      return rest;
    });

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

  async getStaffById(id: bigint) {
    const staff = await this.userRepository.findById(id);
    if (!staff) {
      throw new Error("Staff not found");
    }
    const { password, ...rest } = staff;
    return rest;
  }

  async updateStaff(id: bigint, data: any, currentUserId?: bigint) {
    if (currentUserId !== undefined && id === currentUserId) {
      throw new Error("You cannot update your own account in the Staff section");
    }

    const existing = await this.userRepository.findById(id);
    if (!existing) {
      throw new Error("Staff not found");
    }

    const superAdminRoleId = BigInt(role.ADMIN);
    const isExistingSuperAdmin = existing.roleId === superAdminRoleId;
    const isTargetRoleSuperAdmin =
      data.roleId !== undefined ? BigInt(data.roleId) === superAdminRoleId : isExistingSuperAdmin;

    if (isExistingSuperAdmin) {
      if (data.roleId !== undefined && !isTargetRoleSuperAdmin) {
        const otherActiveSuperAdmins = await this.userRepository.countByRoleId(superAdminRoleId, id, true);
        if (otherActiveSuperAdmins === 0) {
          throw new Error("Cannot change role: one Super Admin must always exist");
        }
      }

      if (data.status === false) {
        const otherActiveSuperAdmins = await this.userRepository.countByRoleId(superAdminRoleId, id, true);
        if (otherActiveSuperAdmins === 0) {
          throw new Error("Cannot deactivate Super Admin: one active Super Admin must always exist");
        }
      }
    } else if (isTargetRoleSuperAdmin) {
      const existingSuperAdmins = await this.userRepository.countByRoleId(superAdminRoleId, id);
      if (existingSuperAdmins > 0) {
        throw new Error("Only one Super Admin can exist in the system");
      }
    }

    if (data.email && data.email !== existing.email) {
      const emailCheck = await this.userRepository.findByEmail(data.email);
      if (emailCheck) {
        throw new Error("Email already in use");
      }
    }

    if (data.mobileNo && data.mobileNo !== existing.mobileNo) {
      const mobileCheck = await this.userRepository.findByMobileNo(data.mobileNo);
      if (mobileCheck) {
        throw new Error("Mobile number already in use");
      }
    }

    const updateData = { ...data };
    if (data.password) {
      const salt = await bcrypt.genSalt(10);
      updateData.password = await bcrypt.hash(data.password, salt);
    }

    const updated = await this.userRepository.update(id, updateData);
    const { password, ...rest } = updated;
    return rest;
  }

  async getRoles() {
    return this.userRepository.getRoles();
  }
}
