import { router, protectedProcedure } from '@/src/trpc/trpc-index'
import { z } from 'zod'
import { extractCoordsFromRedirectUrl } from '@/src/lib/license-util'
import {
  getUserDefaultAddress as getDefaultAddressInDb,
  getUserAddresses as getUserAddressesInDb,
  getUserAddressById as getUserAddressByIdInDb,
  clearUserDefaultAddress as clearDefaultAddressInDb,
  createUserAddress as createUserAddressInDb,
  updateUserAddress as updateUserAddressInDb,
  deleteUserAddress as deleteUserAddressInDb,
  hasOngoingOrdersForAddress as hasOngoingOrdersForAddressInDb,
} from '@/src/dbService'
import type {
  UserAddressResponse,
  UserAddressesResponse,
  UserAddressDeleteResponse,
} from '@packages/shared'

export const addressRouter = router({
  getDefaultAddress: protectedProcedure
    .query(async ({ ctx }): Promise<UserAddressResponse> => {
      const userId = ctx.user.userId;

      const defaultAddress = await getDefaultAddressInDb(userId)

      /*
      // Old implementation - direct DB queries:
      const [defaultAddress] = await db
        .select()
        .from(addresses)
        .where(and(eq(addresses.userId, userId), eq(addresses.isDefault, true)))
        .limit(1);
      */

      return { success: true, data: defaultAddress }
    }),

  getUserAddresses: protectedProcedure
    .query(async ({ ctx }): Promise<UserAddressesResponse> => {
      const userId = ctx.user.userId;
      const userAddresses = await getUserAddressesInDb(userId)

      /*
      // Old implementation - direct DB queries:
      const userAddresses = await db.select().from(addresses).where(eq(addresses.userId, userId));
      */

      return { success: true, data: userAddresses }
    }),

  createAddress: protectedProcedure
    .input(z.object({
      name: z.string().min(1, 'Name is required'),
      phone: z.string().min(1, 'Phone is required'),
      addressLine1: z.string().min(1, 'Address line 1 is required'),
      addressLine2: z.string().optional(),
      city: z.string().min(1, 'City is required'),
      state: z.string().min(1, 'State is required'),
      pincode: z.string().min(1, 'Pincode is required'),
      isDefault: z.boolean().optional(),
      latitude: z.number().optional(),
      longitude: z.number().optional(),
      googleMapsUrl: z.string().optional(),
    }))
    .mutation(async ({ input, ctx }): Promise<UserAddressResponse> => {
      const userId = ctx.user.userId;
      const { name, phone, addressLine1, addressLine2, city, state, pincode, isDefault, googleMapsUrl } = input;

      let { latitude, longitude } = input;

      if (googleMapsUrl && latitude === undefined && longitude === undefined) {
        const coords = await extractCoordsFromRedirectUrl(googleMapsUrl);
        if (coords) {
          latitude = Number(coords.latitude);
          longitude = Number(coords.longitude);
        }
      }

      // Validate required fields
      if (!name || !phone || !addressLine1 || !city || !state || !pincode) {
        throw new Error('Missing required fields');
      }

      // If setting as default, unset other defaults
      if (isDefault) {
        await clearDefaultAddressInDb(userId)
      }

      const newAddress = await createUserAddressInDb({
        userId,
        name,
        phone,
        addressLine1,
        addressLine2,
        city,
        state,
        pincode,
        isDefault: isDefault || false,
        latitude,
        longitude,
        googleMapsUrl,
      })

      /*
      // Old implementation - direct DB queries:
      if (isDefault) {
        await db.update(addresses).set({ isDefault: false }).where(eq(addresses.userId, userId));
      }

      const [newAddress] = await db.insert(addresses).values({
        userId,
        name,
        phone,
        addressLine1,
        addressLine2,
        city,
        state,
        pincode,
        isDefault: isDefault || false,
        latitude,
        longitude,
        googleMapsUrl,
      }).returning();
      */

      return { success: true, data: newAddress }
    }),

  updateAddress: protectedProcedure
    .input(z.object({
      id: z.number().int().positive(),
      name: z.string().min(1, 'Name is required'),
      phone: z.string().min(1, 'Phone is required'),
      addressLine1: z.string().min(1, 'Address line 1 is required'),
      addressLine2: z.string().optional(),
      city: z.string().min(1, 'City is required'),
      state: z.string().min(1, 'State is required'),
      pincode: z.string().min(1, 'Pincode is required'),
      isDefault: z.boolean().optional(),
      latitude: z.number().optional(),
      longitude: z.number().optional(),
      googleMapsUrl: z.string().optional(),
    }))
    .mutation(async ({ input, ctx }): Promise<UserAddressResponse> => {
      const userId = ctx.user.userId;
      const { id, name, phone, addressLine1, addressLine2, city, state, pincode, isDefault, googleMapsUrl } = input;

      let { latitude, longitude } = input;

      if (googleMapsUrl && latitude === undefined && longitude === undefined) {
        const coords = await extractCoordsFromRedirectUrl(googleMapsUrl);
        if (coords) {
          latitude = Number(coords.latitude);
          longitude = Number(coords.longitude);
        }
      }

      // Check if address exists and belongs to user
      const existingAddress = await getUserAddressByIdInDb(userId, id)
      if (!existingAddress) {
        throw new Error('Address not found')
      }

      // If setting as default, unset other defaults
      if (isDefault) {
        await clearDefaultAddressInDb(userId)
      }

      const updatedAddress = await updateUserAddressInDb({
        userId,
        addressId: id,
        name,
        phone,
        addressLine1,
        addressLine2,
        city,
        state,
        pincode,
        isDefault: isDefault || false,
        googleMapsUrl,
        latitude,
        longitude,
      })

      /*
      // Old implementation - direct DB queries:
      if (isDefault) {
        await db.update(addresses).set({ isDefault: false }).where(eq(addresses.userId, userId));
      }

      const updateData: any = {
        name,
        phone,
        addressLine1,
        addressLine2,
        city,
        state,
        pincode,
        isDefault: isDefault || false,
        googleMapsUrl,
      };

      if (latitude !== undefined) {
        updateData.latitude = latitude;
      }
      if (longitude !== undefined) {
        updateData.longitude = longitude;
      }

      const [updatedAddress] = await db.update(addresses).set(updateData).where(and(eq(addresses.id, id), eq(addresses.userId, userId))).returning();
      */

      return { success: true, data: updatedAddress }
     }),

  deleteAddress: protectedProcedure
    .input(z.object({
      id: z.number().int().positive(),
    }))
    .mutation(async ({ input, ctx }): Promise<UserAddressDeleteResponse> => {
      const userId = ctx.user.userId;
      const { id } = input;

      const existingAddress = await getUserAddressByIdInDb(userId, id)
      if (!existingAddress) {
        throw new Error('Address not found or does not belong to user')
      }

      const hasOngoingOrders = await hasOngoingOrdersForAddressInDb(id)
      if (hasOngoingOrders) {
        throw new Error('Address is attached to an ongoing order. Please cancel the order first.')
      }

      if (existingAddress.isDefault) {
        throw new Error('Cannot delete default address. Please set another address as default first.')
      }

      const deleted = await deleteUserAddressInDb(userId, id)

      /*
      // Old implementation - direct DB queries:
      const existingAddress = await db.select().from(addresses).where(and(eq(addresses.id, id), eq(addresses.userId, userId))).limit(1);
      if (existingAddress.length === 0) {
        throw new Error('Address not found or does not belong to user');
      }

      const ongoingOrders = await db.select({
        order: orders,
        status: orderStatus,
        slot: deliverySlotInfo
      })
        .from(orders)
        .innerJoin(orderStatus, eq(orders.id, orderStatus.orderId))
        .innerJoin(deliverySlotInfo, eq(orders.slotId, deliverySlotInfo.id))
        .where(and(
          eq(orders.addressId, id),
          eq(orderStatus.isCancelled, false),
          gte(deliverySlotInfo.deliveryTime, new Date())
        ))
        .limit(1);

      if (ongoingOrders.length > 0) {
        throw new Error('Address is attached to an ongoing order. Please cancel the order first.');
      }

      if (existingAddress[0].isDefault) {
        throw new Error('Cannot delete default address. Please set another address as default first.');
      }

      await db.delete(addresses).where(and(eq(addresses.id, id), eq(addresses.userId, userId)));
      */

      if (!deleted) {
        throw new Error('Address not found or does not belong to user')
      }

      return { success: true, message: 'Address deleted successfully' }
    }),
});
