import { protectedProcedure } from '@/src/trpc/trpc-index';
import { z } from 'zod';
import { ApiError } from '@/src/lib/api-error';
import { recomputeUserNegativityScore } from '@/src/stores/user-negativity-store';
import {
  createUserByMobile,
  getUserByMobile,
  getUnresolvedComplaintsCount,
  getAllUsersWithFilters,
  getOrderCountsByUserIds,
  getLastOrdersByUserIds,
  getSuspensionStatusesByUserIds,
  getUserBasicInfo,
  getUserSuspensionStatus,
  getUserOrders,
  getOrderStatusesByOrderIds,
  getItemCountsByOrderIds,
  upsertUserSuspension,
  searchUsers,
  getAllNotifCreds,
  getAllUnloggedTokens,
  getNotifTokensByUserIds,
  getUserIncidentsWithRelations,
  createUserIncident,
} from '@/src/dbService';
import { queueDataPusher } from '@/src/lib/queue-data-pusher'

export const userRouter = {
  createUserByMobile: protectedProcedure
    .input(z.object({
      mobile: z.string().min(1, 'Mobile number is required'),
    }))
    .mutation(async ({ input }) => {
      // Clean mobile number (remove non-digits)
      const cleanMobile = input.mobile.replace(/\D/g, '');

      // Validate: exactly 10 digits
      if (cleanMobile.length !== 10) {
        throw new ApiError('Mobile number must be exactly 10 digits', 400);
      }

      // Check if user already exists
      const existingUser = await getUserByMobile(cleanMobile);

      if (existingUser) {
        throw new ApiError('User with this mobile number already exists', 409);
      }

      const newUser = await createUserByMobile(cleanMobile);

      return {
        success: true,
        data: newUser,
      };
    }),

  getEssentials: protectedProcedure
    .query(async () => {
      const count = await getUnresolvedComplaintsCount();
      
      return {
        unresolvedComplaints: count,
      };
    }),

  getAllUsers: protectedProcedure
    .input(z.object({
      limit: z.number().min(1).max(100).default(50),
      cursor: z.number().optional(),
      search: z.string().optional(),
    }))
    .query(async ({ input }) => {
      const { limit, cursor, search } = input;
      
      const { users: usersToReturn, hasMore } = await getAllUsersWithFilters(limit, cursor, search);

      // Get order stats for each user
      const userIds = usersToReturn.map((u: any) => u.id);
      
      const orderCounts = await getOrderCountsByUserIds(userIds);
      const lastOrders = await getLastOrdersByUserIds(userIds);
      const suspensionStatuses = await getSuspensionStatusesByUserIds(userIds);
      
      // Create lookup maps
      const orderCountMap = new Map(orderCounts.map(o => [o.userId, o.totalOrders]));
      const lastOrderMap = new Map(lastOrders.map(o => [o.userId, o.lastOrderDate]));
      const suspensionMap = new Map(suspensionStatuses.map(s => [s.userId, s.isSuspended]));

      // Combine data
      const usersWithStats = usersToReturn.map((user: any) => ({
        ...user,
        totalOrders: orderCountMap.get(user.id) || 0,
        lastOrderDate: lastOrderMap.get(user.id) || null,
        isSuspended: suspensionMap.get(user.id) ?? false,
      }));

      // Get next cursor
      const nextCursor = hasMore ? usersToReturn[usersToReturn.length - 1].id : undefined;

      return {
        users: usersWithStats,
        nextCursor,
        hasMore,
      };
    }),

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

      // Get user info
      const user = await getUserBasicInfo(userId);

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

      // Get user suspension status
      const isSuspended = await getUserSuspensionStatus(userId);

      // Get all orders for this user
      const userOrders = await getUserOrders(userId);

      // Get order status for each order
      const orderIds = userOrders.map((o: any) => o.id);
      const orderStatuses = await getOrderStatusesByOrderIds(orderIds);

      // Get item counts for each order
      const itemCounts = await getItemCountsByOrderIds(orderIds);

      // Create lookup maps
      const statusMap = new Map(orderStatuses.map(s => [s.orderId, s]));
      const itemCountMap = new Map(itemCounts.map(c => [c.orderId, c.itemCount]));

      // Determine status string
      const getStatus = (status: { isDelivered: boolean; isCancelled: boolean } | undefined) => {
        if (!status) return 'pending';
        if (status.isCancelled) return 'cancelled';
        if (status.isDelivered) return 'delivered';
        return 'pending';
      };

      // Combine data
      const ordersWithDetails = userOrders.map((order: any) => {
        const status = statusMap.get(order.id);
        return {
          id: order.id,
          readableId: order.readableId,
          totalAmount: order.totalAmount,
          createdAt: order.createdAt,
          isFlashDelivery: order.isFlashDelivery,
          status: getStatus(status),
          itemCount: itemCountMap.get(order.id) || 0,
        };
      });

      return {
        user: {
          ...user,
          isSuspended,
        },
        orders: ordersWithDetails,
      };
    }),

  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,
        message: `User ${isSuspended ? 'suspended' : 'unsuspended'} successfully`,
      };
    }),

  getUsersForNotification: protectedProcedure
    .input(z.object({
      search: z.string().optional(),
    }))
    .query(async ({ input }) => {
      const { search } = input;

      const usersList = await searchUsers(search);

      // Get eligible users (have notif_creds entry)
      const eligibleUsers = await getAllNotifCreds();

      const eligibleSet = new Set(eligibleUsers.map(u => u.userId));

      return {
        users: usersList.map((user: any) => ({
          id: user.id,
          name: user.name,
          mobile: user.mobile,
          isEligibleForNotif: eligibleSet.has(user.id),
        })),
      };
    }),

  sendNotification: protectedProcedure
    .input(z.object({
      userIds: z.array(z.number()).default([]),
      title: z.string().min(1, 'Title is required'),
      text: z.string().min(1, 'Message is required'),
      imageUrl: z.string().optional(),
    }))
    .mutation(async ({ input }) => {
      const { userIds, title, text, imageUrl } = input;

      let tokens: string[] = [];

      if (userIds.length === 0) {
        // Send to all users - get tokens from both logged-in and unlogged users
        const loggedInTokens = await getAllNotifCreds();
        const unloggedTokens = await getAllUnloggedTokens();
        
        tokens = [
          ...loggedInTokens.map(t => t.token),
          ...unloggedTokens.map(t => t.token)
        ];
      } else {
        // Send to specific users - get their tokens
        const userTokens = await getNotifTokensByUserIds(userIds);
        tokens = userTokens.map(t => t.token);
      }

      // tokens = ['ExponentPushToken[w4KTsLKnnp8SbURdl5-Q6x]', 'ExponentPushToken[81Io9TG3Qg0s3N0V8L86T-]', 'ExponentPushToken[YJRSQmMUEUbaI2VCZLaoN_]', 'ExponentPushToken[LQZgYkFG_3CweaUbv0fBKJ]']

      // Queue one job per token
      let queuedCount = 0;
      for (const token of tokens) {
        try {
          await queueDataPusher.pushNotifQueue({
            name: 'send-admin-notification',
            jobData: {
              token,
              title,
              body: text,
              imageUrl: imageUrl || null,
            },
            options: {
              attempts: 3,
              backoff: {
                type: 'exponential',
                delay: 2000,
              },
            },
          })
          // await queueDataPusher.pushNotifQueue({
          //     jobData: {
          //       token,
          //     title,
          //     body: text,
          //     imageUrl: imageUrl || null,
          //     }
          // })
          queuedCount++;
        } catch (error) {
          console.error(`Failed to queue notification for token:`, error);
        }
      }

      return {
        success: true,
        message: `Notification queued for ${queuedCount} users`,
      };
    }),

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

      const incidents = await getUserIncidentsWithRelations(userId);

      return {
        incidents: incidents.map((incident: any) => ({
          id: incident.id,
          userId: incident.userId,
          orderId: incident.orderId,
          dateAdded: incident.dateAdded,
          adminComment: incident.adminComment,
          addedBy: incident.addedBy?.name || 'Unknown',
          negativityScore: incident.negativityScore,
          orderStatus: incident.order?.orderStatus?.[0]?.isCancelled ? 'cancelled' : 'active',
        })),
      };
    }),

  addUserIncident: protectedProcedure
    .input(z.object({
      userId: z.number(),
      orderId: z.number().optional(),
      adminComment: z.string().optional(),
      negativityScore: z.number().optional(),
    }))
    .mutation(async ({ input, ctx }) => {
      const { userId, orderId, adminComment, negativityScore } = input;
      
      const adminUserId = ctx.staffUser?.id;
      
      if (!adminUserId) {
        throw new ApiError('Admin user not authenticated', 401);
      }

      const incident = await createUserIncident(
        userId,
        orderId,
        adminComment,
        adminUserId,
        negativityScore
      );

      recomputeUserNegativityScore(userId);

      return {
        success: true,
        data: incident,
      };
    }),
};
