import { router, publicProcedure, protectedProcedure } from '@/src/trpc/trpc-index'
import { z } from 'zod';
import bcrypt from 'bcryptjs';
import { SignJWT } from 'jose';
import { getEncodedJwtSecret } from '@/src/lib/env-exporter';
import { ApiError } from '@/src/lib/api-error'
import {
  getStaffUserByName,
  getAllStaff,
  getAllUsers,
  getUserWithDetails,
  upsertUserSuspension,
  checkStaffUserExists,
  checkStaffRoleExists,
  createStaffUser,
  getAllRoles,
} from '@/src/dbService'
import type { StaffUser, StaffRole } from '@packages/shared'

export const staffUserRouter = router({
  login: publicProcedure
    .input(z.object({
      name: z.string(),
      password: z.string(),
    }))
    .mutation(async ({ input }) => {
      const { name, password } = input;

      if (!name || !password) {
        throw new ApiError('Name and password are required', 400);
      }

      const staff = await getStaffUserByName(name);

      if (!staff) {
        throw new ApiError('Invalid credentials', 401);
      }

      const isPasswordValid = await bcrypt.compare(password, staff.password);
      if (!isPasswordValid) {
        throw new ApiError('Invalid credentials', 401);
      }

      const token = await new SignJWT({ staffId: staff.id, name: staff.name })
        .setProtectedHeader({ alg: 'HS256' })
        .setExpirationTime('30d')
        .sign(getEncodedJwtSecret());

      return {
        message: 'Login successful',
        token,
        staff: { id: staff.id, name: staff.name },
      };
    }),

  getStaff: protectedProcedure
    .query(async ({ ctx }) => {
      const staff = await getAllStaff();

      // Transform the data to include role and permissions in a cleaner format
      const transformedStaff = staff.map((user) => ({
        id: user.id,
        name: user.name,
        role: user.role ? {
          id: user.role.id,
          name: user.role.roleName,
        } : null,
        permissions: user.role?.rolePermissions.map((rp: any) => ({
          id: rp.permission.id,
          name: rp.permission.permissionName,
        })) || [],
      }));

      return {
        staff: transformedStaff,
      };
    }),

  getUsers: protectedProcedure
    .input(z.object({
      cursor: z.number().optional(),
      limit: z.number().default(20),
      search: z.string().optional(),
    }))
    .query(async ({ input }) => {
      const { cursor, limit, search } = input;

      const { users: usersToReturn, hasMore } = await getAllUsers(cursor, limit, search);

      const formattedUsers = usersToReturn.map((user: any) => ({
        id: user.id,
        name: user.name,
        email: user.email,
        mobile: user.mobile,
        image: user.userDetails?.profileImage || null,
      }));

      return {
        users: formattedUsers,
        nextCursor: hasMore ? usersToReturn[usersToReturn.length - 1].id : undefined,
      };
    }),

  getUserDetails: protectedProcedure
    .input(z.object({ userId: z.number() }))
    .query(async ({ input }) => {
      const { userId } = input;

      const user = await getUserWithDetails(userId);

      if (!user) {
        throw new ApiError("User not found", 404);
      }

      const lastOrder = user.orders[0];

      return {
        id: user.id,
        name: user.name,
        email: user.email,
        mobile: user.mobile,
        addedOn: user.createdAt,
        lastOrdered: lastOrder?.createdAt || null,
        isSuspended: user.userDetails?.isSuspended || false,
      };
    }),

  updateUserSuspension: protectedProcedure
    .input(z.object({ userId: z.number(), isSuspended: z.boolean() }))
    .mutation(async ({ input }) => {
      const { userId, isSuspended } = input;

      await upsertUserSuspension(userId, isSuspended);

      return { success: true };
    }),

  createStaffUser: protectedProcedure
    .input(z.object({
      name: z.string().min(1, 'Name is required'),
      password: z.string().min(6, 'Password must be at least 6 characters'),
      roleId: z.number().int().positive('Role is required'),
    }))
    .mutation(async ({ input, ctx }) => {
      const { name, password, roleId } = input;

      // Check if staff user already exists
      const existingUser = await checkStaffUserExists(name);

      if (existingUser) {
        throw new ApiError('Staff user with this name already exists', 409);
      }

      // Check if role exists
      const roleExists = await checkStaffRoleExists(roleId);

      if (!roleExists) {
        throw new ApiError('Invalid role selected', 400);
      }

      // Hash password
      const hashedPassword = await bcrypt.hash(password, 12);

      // Create staff user
      const newUser = await createStaffUser(name, hashedPassword, roleId);

      return { success: true, user: { id: newUser.id, name: newUser.name } };
    }),

  getRoles: protectedProcedure
    .query(async ({ ctx }) => {
      const roles = await getAllRoles();

      return {
        roles: roles.map((role: any) => ({
          id: role.id,
          name: role.roleName,
        })),
      };
    }),
});
