import { Injectable } from "@nestjs/common";
import { CreateBuddyDto } from "./dto/create-buddy.dto";
import { UpdateBuddyDto } from "./dto/update-buddy.dto";
import { InjectModel } from "@nestjs/mongoose";
import { ResponseService } from "src/common/service/response.service";
import { User, UserDocument } from "src/user/schemas/user.schema";
import { CityDocument, City } from "src/training/schemas/cities.schema";
import {
  SUPPORTED_LANGUAGES,
  DEFAULT_LANGUAGE,
} from "src/common/constants/language.constant";
import {
  BuddyRequestDocument,
  BuddyRequest,
} from "./schema/buddy-requests.schema";
import {
  TrainingHomeGym,
  TrainingHomeGymDocument,
} from "src/training/schemas/training-homegym.schema";
import { Request, Response } from "express";
import { Model, Types } from "mongoose";
import { SearchBuddiesDto } from "./dto/search-buddies.dto";

@Injectable()
export class BuddiesService {
  constructor(
    @InjectModel(User.name)
    private userModel: Model<UserDocument>,
    @InjectModel(City.name)
    private cityModel: Model<CityDocument>,
    @InjectModel(TrainingHomeGym.name)
    private trainingHomeGymModel: Model<TrainingHomeGymDocument>,
    @InjectModel(BuddyRequest.name)
    private buddyRequestModel: Model<BuddyRequestDocument>,

    private readonly resService: ResponseService,
  ) {}

  private paginationMeta(total: number, page: number, limit: number) {
    return {
      total,
      page,
      limit,
      pages: Math.ceil(total / limit),
    };
  }

  private buildUserFilters(body: SearchBuddiesDto) {
    const { goals, styles, level, trainingTimeWeek, trainingTimeWeekend } =
      body;
    const goalIds = goals?.map((id) => new Types.ObjectId(id)) || [];
    const styleIds = styles?.map((id) => new Types.ObjectId(id)) || [];
    const levels = level?.map((l) => new RegExp(`^${l}$`, "i")) || [];
    return {
      ...(goalIds.length && { training_goals: { $in: goalIds } }),
      ...(styleIds.length && { training_styles: { $in: styleIds } }),
      ...(levels.length && { training_experience: { $in: levels } }),
      ...(trainingTimeWeek?.length && {
        training_time_week: { $in: trainingTimeWeek },
      }),
      ...(trainingTimeWeekend?.length && {
        training_time_weekend: { $in: trainingTimeWeekend },
      }),
    };
  }

  private async findUsersInGyms(
    userId: string,
    gymIds: Types.ObjectId[],
    body: SearchBuddiesDto,
    lang: string,
  ) {
    const filters = this.buildUserFilters(body);

    const safeLang = SUPPORTED_LANGUAGES.includes(lang)
      ? lang
      : DEFAULT_LANGUAGE;

    return this.trainingHomeGymModel.aggregate([
      {
        $match: {
          _id: { $in: gymIds },
          status: 1,
          deleted_at: null,
        },
      },
      {
        $lookup: {
          from: "users",
          let: { gymId: "$_id" },
          pipeline: [
            {
              $match: {
                $expr: { $in: ["$$gymId", "$home_gyms"] },
                _id: { $ne: new Types.ObjectId(userId) },
                status: 1,
                deleted_at: null,
                is_profile_visible: true,
                ...filters,
              },
            },
            {
              $addFields: {
                training_goal_ids: {
                  $map: {
                    input: "$training_goals",
                    as: "goal",
                    in: { $toObjectId: "$$goal" },
                  },
                },

                // Check whether current gym is primary gym
                isPrimaryGym: {
                  $eq: [{ $arrayElemAt: ["$home_gyms", 0] }, "$$gymId"],
                },

                totalGyms: {
                  $size: {
                    $ifNull: ["$home_gyms", []],
                  },
                },
              },
            },
            {
              $lookup: {
                from: "traininggoals",
                let: { goalIds: "$training_goal_ids" },
                pipeline: [
                  {
                    $match: {
                      $expr: { $in: ["$_id", "$$goalIds"] },
                    },
                  },
                  {
                    $project: {
                      _id: 1,
                      name: `$name.${safeLang}`,
                    },
                  },
                ],
                as: "training_goals",
              },
            },
            {
              $lookup: {
                from: "buddyrequests",
                let: { otherUserId: "$_id" },
                pipeline: [
                  {
                    $match: {
                      deleted_at: null,
                      $expr: {
                        $or: [
                          {
                            $and: [
                              { $eq: ["$sender", new Types.ObjectId(userId)] },
                              { $eq: ["$receiver", "$$otherUserId"] },
                            ],
                          },
                          {
                            $and: [
                              { $eq: ["$sender", "$$otherUserId"] },
                              {
                                $eq: ["$receiver", new Types.ObjectId(userId)],
                              },
                            ],
                          },
                        ],
                      },
                    },
                  },
                  {
                    $project: {
                      _id: 0,
                      status: 1,
                      sender: 1,
                    },
                  },
                ],
                as: "buddy_request",
              },
            },
            {
              $addFields: {
                buddy_status: {
                  $let: {
                    vars: {
                      req: { $arrayElemAt: ["$buddy_request", 0] },
                    },
                    in: {
                      $cond: [
                        {
                          $in: [
                            { $ifNull: ["$$req.status", null] },
                            ["rejected", null],
                          ],
                        },
                        "request",
                        {
                          $cond: [
                            { $eq: ["$$req.status", "accepted"] },
                            "chat",
                            {
                              $cond: [
                                {
                                  $ne: [
                                    "$$req.sender",
                                    new Types.ObjectId(userId),
                                  ],
                                },
                                "review",
                                "$$req.status",
                              ],
                            },
                          ],
                        },
                      ],
                    },
                  },
                },
              },
            },

            // Sort users so primary gym row comes first
            // {
            //   $sort: {
            //     isPrimaryGym: -1,
            //   },
            // },

            {
              $sort: {
                isPrimaryGym: -1,
                user_name: 1,
                // _id: 1,
              },
            },
            // Remove duplicate users inside same gym response
            {
              $group: {
                _id: "$_id",
                user: { $first: "$$ROOT" },
              },
            },
            {
              $replaceRoot: {
                newRoot: "$user",
              },
            },

            {
              $project: {
                _id: 1,
                user_name: 1,
                name: { $concat: ["$first_name", " ", "$last_name"] },
                profile_image: 1,
                training_experience: 1,
                training_time_week: 1,
                training_time_weekend: 1,
                training_goals: 1,
                buddy_status: 1,
                isPrimaryGym: 1,
                totalGyms: 1,
              },
            },
          ],
          as: "users",
        },
      },
      {
        $project: {
          _id: 1,
          gymName: `$name.${safeLang}`,
          location: 1,
          users: 1,
        },
      },
      {
        $addFields: {
          users: {
            $map: {
              input: "$users",
              as: "user",
              in: {
                $mergeObjects: [
                  "$$user",
                  {
                    currentGymId: "$_id",
                  },
                ],
              },
            },
          },
        },
      },

      {
        $unwind: {
          path: "$users",
          preserveNullAndEmptyArrays: true,
        },
      },

      // {
      //   $sort: {
      //     "users._id": 1,
      //     "users.isPrimaryGym": -1,
      //     _id: 1,
      //   },
      // },
      {
        $sort: {
          "users.isPrimaryGym": -1,
          "users.user_name": 1,
          _id: 1,
        },
      },
      {
        $group: {
          _id: {
            $ifNull: [
              "$users._id",
              { $concat: ["gym_", { $toString: "$_id" }] },
            ],
          },

          selectedUser: {
            $first: "$users",
          },

          selectedGym: {
            $first: {
              _id: "$_id",
              gymName: "$gymName",
              location: "$location",
            },
          },
        },
      },
      {
        $group: {
          _id: "$selectedGym._id",
          gymName: {
            $first: "$selectedGym.gymName",
          },
          location: {
            $first: "$selectedGym.location",
          },
          users: {
            $push: {
              $cond: [
                {
                  $and: [
                    { $ne: ["$selectedUser", null] },
                    { $ne: ["$selectedUser._id", null] },
                  ],
                },
                "$selectedUser",
                "$$REMOVE",
              ],
            },
          },
        },
      },
      // {
      //   $addFields: {
      //     userCount: { $size: "$users" },
      //   },
      // },
      // {
      //   $sort: {
      //     userCount: -1,
      //   },
      // },
      // {
      //   $project: {
      //     _id: 1,
      //     gymName: 1,
      //     location: 1,
      //     users: {
      //       $map: {
      //         input: "$users",
      //         as: "user",
      //         in: {
      //           _id: "$$user._id",
      //           user_name: "$$user.user_name",
      //           name: "$$user.name",
      //           profile_image: "$$user.profile_image",
      //           training_experience: "$$user.training_experience",
      //           training_time_week: "$$user.training_time_week",
      //           training_time_weekend: "$$user.training_time_weekend",
      //           training_goals: "$$user.training_goals",
      //           buddy_status: "$$user.buddy_status",
      //           is_primary_gym: "$$user.isPrimaryGym",
      //           total_gym_count: "$$user.totalGyms",
      //         },
      //       },
      //     },
      //   },
      // },
      {
        $addFields: {
          userCount: { $size: "$users" },
        },
      },
      {
        $sort: {
          userCount: -1,
        },
      },
      {
        $addFields: {
          users: {
            $sortArray: {
              input: "$users",
              sortBy: {
                isPrimaryGym: -1,
                user_name: 1,
              },
            },
          },
        },
      },
      {
        $project: {
          _id: 1,
          gymName: 1,
          location: 1,
          users: {
            $map: {
              input: "$users",
              as: "user",
              in: {
                _id: "$$user._id",
                user_name: "$$user.user_name",
                name: "$$user.name",
                profile_image: "$$user.profile_image",
                training_experience: "$$user.training_experience",
                training_time_week: "$$user.training_time_week",
                training_time_weekend: "$$user.training_time_weekend",
                training_goals: "$$user.training_goals",
                buddy_status: "$$user.buddy_status",
                is_primary_gym: "$$user.isPrimaryGym",
                total_gym_count: "$$user.totalGyms",
              },
            },
          },
        },
      },
      {
        $addFields: {
          userCount: { $size: "$users" },
        },
      },
      {
        $sort: {
          userCount: -1,
        },
      },
      {
        $project: {
          _id: 1,
          gymName: 1,
          location: 1,
          users: 1,
        },
      },
      {
        $lookup: {
          from: "users",
          let: { gymId: "$_id" },
          pipeline: [
            {
              $match: {
                $expr: { $in: ["$$gymId", "$home_gyms"] },
                _id: { $ne: new Types.ObjectId(userId) },
                status: 1,
                deleted_at: null,
                is_profile_visible: true,
                ...filters,
              },
            },
            {
              $group: {
                _id: null,
                count: { $sum: 1 },
              },
            },
          ],
          as: "totalUserCountData",
        },
      },
      {
        $addFields: {
          total_gym_user: {
            $ifNull: [{ $arrayElemAt: ["$totalUserCountData.count", 0] }, 0],
          },
        },
      },
      {
        $project: {
          totalUserCountData: 0,
        },
      },
    ]);
  }

  private async searchByUsername(
    userId: string,
    body: SearchBuddiesDto,
    lang: string,
  ) {
    const filters = this.buildUserFilters(body);
    const { userName } = body;

    const safeLang = SUPPORTED_LANGUAGES.includes(lang)
      ? lang
      : DEFAULT_LANGUAGE;

    return this.userModel.aggregate([
      {
        $match: {
          user_name: { $regex: `^${userName}`, $options: "i" },
          _id: { $ne: new Types.ObjectId(userId) },
          status: 1,
          deleted_at: null,
          // is_profile_visible: true,
          ...filters,
        },
      },

      // Convert training goal ids
      {
        $addFields: {
          training_goal_ids: {
            $map: {
              input: "$training_goals",
              as: "goal",
              in: { $toObjectId: "$$goal" },
            },
          },
        },
      },

      // Training goals lookup
      {
        $lookup: {
          from: "traininggoals",
          let: { goalIds: "$training_goal_ids" },
          pipeline: [
            {
              $match: {
                $expr: { $in: ["$_id", "$$goalIds"] },
              },
            },
            {
              $project: {
                _id: 0,
                name: `$name.${safeLang}`,
              },
            },
          ],
          as: "training_goals",
        },
      },

      // Buddy request lookup
      // {
      //   $lookup: {
      //     from: "buddyrequests",
      //     let: { otherUserId: "$_id" },
      //     pipeline: [
      //       {
      //         $match: {
      //           deleted_at: null,
      //           $expr: {
      //             $or: [
      //               {
      //                 $and: [
      //                   { $eq: ["$sender", new Types.ObjectId(userId)] },
      //                   { $eq: ["$receiver", "$$otherUserId"] },
      //                 ],
      //               },
      //               {
      //                 $and: [
      //                   { $eq: ["$sender", "$$otherUserId"] },
      //                   { $eq: ["$receiver", new Types.ObjectId(userId)] },
      //                 ],
      //               },
      //             ],
      //           },
      //         },
      //       },
      //     ],
      //     as: "buddy_request",
      //   },
      // },
      {
        $lookup: {
          from: "buddyrequests",
          let: { otherUserId: "$_id" },
          pipeline: [
            {
              $match: {
                deleted_at: null,
                $expr: {
                  $or: [
                    {
                      $and: [
                        { $eq: ["$sender", new Types.ObjectId(userId)] },
                        { $eq: ["$receiver", "$$otherUserId"] },
                      ],
                    },
                    {
                      $and: [
                        { $eq: ["$sender", "$$otherUserId"] },
                        { $eq: ["$receiver", new Types.ObjectId(userId)] },
                      ],
                    },
                  ],
                },
              },
            },
            {
              $project: {
                status: 1,
                sender: 1,
              },
            },
            { $limit: 1 },
          ],
          as: "buddy_request",
        },
      },
      // {
      //   $addFields: {
      //     buddy_status: {
      //       $cond: [
      //         {
      //           $in: [
      //             {
      //               $ifNull: [
      //                 { $arrayElemAt: ["$buddy_request.status", 0] },
      //                 null,
      //               ],
      //             },
      //             ["rejected", null],
      //           ],
      //         },
      //         "request",
      //         { $arrayElemAt: ["$buddy_request.status", 0] },
      //       ],
      //     },
      //   },
      // },

      {
        $addFields: {
          buddy_status: {
            $let: {
              vars: {
                req: { $arrayElemAt: ["$buddy_request", 0] },
              },
              in: {
                $cond: [
                  {
                    $in: [
                      { $ifNull: ["$$req.status", null] },
                      ["rejected", null],
                    ],
                  },
                  "request",
                  {
                    $cond: [
                      { $eq: ["$$req.status", "accepted"] },
                      "chat",
                      {
                        $cond: [
                          {
                            $ne: ["$$req.sender", new Types.ObjectId(userId)],
                          },
                          "review",
                          "$$req.status",
                        ],
                      },
                    ],
                  },
                ],
              },
            },
          },
        },
      },
      // Lookup gyms
      {
        $lookup: {
          from: "traininghomegyms",
          let: { gymIds: "$home_gyms" },
          pipeline: [
            {
              $match: {
                $expr: { $in: ["$_id", "$$gymIds"] },
                status: 1,
                deleted_at: null,
              },
            },
            {
              $project: {
                _id: 1,
                location: 1,
                gymName: `$name.${safeLang}`,
              },
            },
          ],
          as: "gyms",
        },
      },

      {
        $unwind: {
          path: "$gyms",
          preserveNullAndEmptyArrays: true,
        },
      },

      // Format user object
      {
        $project: {
          gymId: { $ifNull: ["$gyms._id", null] },
          gymName: { $ifNull: ["$gyms.gymName", null] },
          location: { $ifNull: ["$gyms.location", null] },
          user: {
            _id: "$_id",
            user_name: "$user_name",
            name: { $concat: ["$first_name", " ", "$last_name"] },
            profile_image: "$profile_image",
            training_experience: "$training_experience",
            training_time_week: "$training_time_week",
            training_time_weekend: "$training_time_weekend",
            training_goals: "$training_goals",
            buddy_status: "$buddy_status",
          },
        },
      },

      // Group by gym
      {
        $group: {
          _id: "$gymId",
          gymName: { $first: "$gymName" },
          location: { $first: "$location" },
          users: { $push: "$user" },
        },
      },
    ]);
  }

  private async searchByGym(
    userId: string,
    body: SearchBuddiesDto,
    lang: string,
  ) {
    const safeLang = SUPPORTED_LANGUAGES.includes(lang)
      ? lang
      : DEFAULT_LANGUAGE;

    const gymNameField = `name.${safeLang}`;
    const gyms = await this.trainingHomeGymModel.find({
      [gymNameField]: { $regex: `^${body.gymName}`, $options: "i" },
      status: 1,
      deleted_at: null,
    });

    const gymIds = gyms.map((g) => g._id);
    console.log(gymIds[0], "Matching gym IDs", userId);

    return this.findUsersInGyms(userId, gymIds, body, lang);
  }

  // private async searchByLocation(
  //   userId: string,
  //   body: SearchBuddiesDto,
  //   lang: string,
  // ) {
  //   let gymIds: string[] = [];

  //   if (body.location?.lat != null && body.location?.lng != null) {
  //     const { lat, lng } = body.location;
  //     // const radiusKm = body.radius ?? 20;
  //     const radiusKm = body.radius;
  //     const gyms = await this.trainingHomeGymModel.find(
  //       {
  //         location: {
  //           $near: {
  //             $geometry: {
  //               type: "Point",
  //               coordinates: [lng, lat],
  //             },
  //             // $maxDistance: radiusKm * 1000,
  //           },
  //         },
  //         status: 1,
  //         deleted_at: null,
  //       },
  //       { _id: 1 },
  //     );
  //     gymIds = gyms.map((g) => g._id.toString());
  //   } else {
  //     console.log("No location provided, using user's primary gym", userId);
  //     const user = await this.userModel
  //       .findById(userId)
  //       .select("home_gyms")
  //       .lean();

  //     if (user?.home_gyms?.length) {
  //       const primaryGymId = user.home_gyms[0];

  //       // Get primary gym location (always exists as per your case)
  //       const primaryGym = await this.trainingHomeGymModel.findById(
  //         primaryGymId,
  //         { location: 1 },
  //       );

  //       if (!primaryGym) {
  //         return [];
  //       }

  //       const [lng, lat] = primaryGym.location.coordinates;
  //       // const radiusKm = body.radius ?? 20;

  //       // Find nearby gyms
  //       const gyms = await this.trainingHomeGymModel.find(
  //         {
  //           location: {
  //             $near: {
  //               $geometry: {
  //                 type: "Point",
  //                 coordinates: [lng, lat],
  //               },
  //               // $maxDistance: radiusKm * 1000,
  //             },
  //           },
  //           status: 1,
  //           deleted_at: null,
  //         },
  //         { _id: 1 },
  //       );

  //       const nearbyGymIds = gyms.map((g) => g._id.toString());

  //       // Keep primary gym at top
  //       gymIds = [
  //         primaryGymId.toString(),
  //         ...nearbyGymIds.filter((id) => id !== primaryGymId.toString()),
  //       ];

  //       console.log(gymIds, "Primary + nearby gyms");
  //     } else {
  //       // Default to Delhi location
  //       // const defaultLat = 28.6139;
  //       // const defaultLng = 77.209;
  //       // const radiusKm = body.radius ?? 20;

  //       // Defalut to humburg
  //       const defaultLat = 53.55811699336884;
  //       const defaultLng = 9.998501206772588;
  //       const radiusKm = body.radius ?? 20;

  //       const gyms = await this.trainingHomeGymModel.find(
  //         {
  //           location: {
  //             $near: {
  //               $geometry: {
  //                 type: "Point",
  //                 coordinates: [defaultLng, defaultLat],
  //               },
  //               // $maxDistance: radiusKm * 1000,
  //             },
  //           },
  //           status: 1,
  //           deleted_at: null,
  //         },
  //         { _id: 1 },
  //       );
  //       gymIds = gyms.map((g) => g._id.toString());
  //     }
  //   }

  //   if (!gymIds.length) {
  //     return [];
  //   }

  //   const objectIds = gymIds.map((id) => new Types.ObjectId(id));
  //   console.log(objectIds[0], "Final gym IDs for location search", userId);

  //   return this.findUsersInGyms(userId, objectIds, body, lang);
  // }

  private async searchByLocation(
    userId: string,
    body: SearchBuddiesDto,
    lang: string,
  ) {
    let gymIds: string[] = [];

    // 1. Search using visible map bounds
    if (
      body.location?.northEastLat != null &&
      body.location?.northEastLng != null &&
      body.location?.southWestLat != null &&
      body.location?.southWestLng != null
    ) {
      const { northEastLat, northEastLng, southWestLat, southWestLng } =
        body.location;

      const gyms = await this.trainingHomeGymModel.find(
        {
          location: {
            $geoWithin: {
              $box: [
                [southWestLng, southWestLat], // bottom-left
                [northEastLng, northEastLat], // top-right
              ],
            },
          },
          status: 1,
          deleted_at: null,
        },
        { _id: 1 },
      );

      gymIds = gyms.map((g) => g._id.toString());
    }

    // 2. Search using single lat/lng + optional radius
    else if (body.location?.lat != null && body.location?.lng != null) {
      const { lat, lng } = body.location;
      const radiusKm = body.radius;

      const gyms = await this.trainingHomeGymModel.find(
        {
          location: {
            $near: {
              $geometry: {
                type: "Point",
                coordinates: [lng, lat],
              },
              ...(radiusKm ? { $maxDistance: radiusKm * 1000 } : {}),
            },
          },
          status: 1,
          deleted_at: null,
        },
        { _id: 1 },
      );

      gymIds = gyms.map((g) => g._id.toString());
    }

    // 3. Fallback to user's primary gym
    else {
      console.log("No location provided, using user's primary gym", userId);

      const user = await this.userModel
        .findById(userId)
        .select("home_gyms")
        .lean();

      if (user?.home_gyms?.length) {
        const primaryGymId = user.home_gyms[0];

        const primaryGym = await this.trainingHomeGymModel.findById(
          primaryGymId,
          { location: 1 },
        );

        if (!primaryGym) {
          return [];
        }

        const [lng, lat] = primaryGym.location.coordinates;
        const radiusKm = body.radius ?? 20;

        const gyms = await this.trainingHomeGymModel.find(
          {
            location: {
              $near: {
                $geometry: {
                  type: "Point",
                  coordinates: [lng, lat],
                },
                $maxDistance: radiusKm * 1000,
              },
            },
            status: 1,
            deleted_at: null,
          },
          { _id: 1 },
        );

        const nearbyGymIds = gyms.map((g) => g._id.toString());

        gymIds = [
          primaryGymId.toString(),
          ...nearbyGymIds.filter((id) => id !== primaryGymId.toString()),
        ];
      } else {
        // Default map center if user has no gym
        const defaultLat = 53.55811699336884;
        const defaultLng = 9.998501206772588;
        const radiusKm = body.radius ?? 20;

        const gyms = await this.trainingHomeGymModel.find(
          {
            location: {
              $near: {
                $geometry: {
                  type: "Point",
                  coordinates: [defaultLng, defaultLat],
                },
                $maxDistance: radiusKm * 1000,
              },
            },
            status: 1,
            deleted_at: null,
          },
          { _id: 1 },
        );

        gymIds = gyms.map((g) => g._id.toString());
      }
    }

    if (!gymIds.length) {
      return [];
    }

    const objectIds = gymIds.map((id) => new Types.ObjectId(id));

    return this.findUsersInGyms(userId, objectIds, body, lang);
  }

  async findNearbyBuddies(
    userId: string,
    radiusInKm: number,
    req: Request,
    res: Response,
  ) {
    const user = await this.userModel.findById(userId);

    if (!user?.home_gyms?.length) {
      return this.resService.notFound(res, "No gym associated with user", req);
    }

    const gyms = await this.trainingHomeGymModel.find({
      _id: { $in: user.home_gyms },
    });

    if (!gyms.length) {
      return this.resService.notFound(res, "Gyms not found", req);
    }

    const radiusInMeters = radiusInKm * 1000;

    // Collect unique nearby gym ids
    const nearbyGymIds = new Set<string>();

    for (const gym of gyms) {
      const [lng, lat] = gym.location.coordinates;

      const nearbyGyms = await this.trainingHomeGymModel
        .find({
          location: {
            $near: {
              $geometry: {
                type: "Point",
                coordinates: [lng, lat],
              },
              $maxDistance: radiusInMeters,
            },
          },
        })
        .select("_id");

      nearbyGyms.forEach((g) => nearbyGymIds.add(g._id.toString()));
    }

    const gymIdsArray = Array.from(nearbyGymIds);

    //  Aggregation to return gyms with users
    const result = await this.trainingHomeGymModel.aggregate([
      {
        $match: {
          _id: { $in: gymIdsArray.map((id) => new Types.ObjectId(id)) },
        },
      },
      {
        $lookup: {
          from: "users",
          localField: "_id",
          foreignField: "home_gyms",
          as: "users",
        },
      },
      {
        $project: {
          title: 1,
          name: 1,
          address: 1,
          location: 1,
          users: {
            $filter: {
              input: "$users",
              as: "user",
              cond: { $ne: ["$$user._id", user._id] },
            },
          },
        },
      },
    ]);

    return this.resService.success(
      res,
      "Nearby buddies fetched successfully",
      req,
      result,
    );
  }

  async searchBuddies(
    userId: string,
    body: SearchBuddiesDto,
    req: Request,
    res: Response,
    lang: string,
  ) {
    try {
      const { userName, gymName, location } = body;

      // Search by username
      if (userName) {
        const result = await this.searchByUsername(userId, body, lang);
        return this.resService.success(res, "Buddy fetched", req, result);
      }

      // Search by gym name
      if (gymName) {
        const result = await this.searchByGym(userId, body, lang);
        return this.resService.success(res, "Buddies fetched", req, result);
      }

      // Search by location OR primary gym
      const result = await this.searchByLocation(userId, body, lang);

      return this.resService.success(res, "Buddies fetched", req, result);
    } catch (error) {
      console.log(error, "Error in searchBuddies");
      return this.resService.serverError(res, "Error searching buddies", req);
    }
  }

  async getBuddyProfile(
    currentUserId: string,
    buddyId: string,
    req: Request,
    res: Response,
    lang: string,
  ) {
    try {
      if (!Types.ObjectId.isValid(buddyId)) {
        return this.resService.badRequest(res, "Invalid buddy ID", req);
      }

      // if (currentUserId.toString() === buddyId.toString()) {
      //   return this.resService.badRequest(
      //     res,
      //     "You cannot fetch your own profile here",
      //     req,
      //   );
      // }

      const safeLang = SUPPORTED_LANGUAGES.includes(lang)
        ? lang
        : DEFAULT_LANGUAGE;

      const userObjectId = new Types.ObjectId(currentUserId);
      const buddyObjectId = new Types.ObjectId(buddyId);
      const now = new Date();

      const currentUser = await this.userModel.findById(userObjectId).lean();

      if (!currentUser) {
        return this.resService.notFound(res, "User not found", req);
      }

      const currentGoals = (currentUser.training_goals || []).map((id) =>
        id.toString(),
      );

      const currentStyles = (currentUser.training_styles || []).map((id) =>
        id.toString(),
      );

      const currentExperience = currentUser.training_experience;

      const result = await this.userModel.aggregate([
        {
          $match: {
            _id: buddyObjectId,
            deleted_at: null,
            status: 1,
          },
        },
        {
          $lookup: {
            from: "traininggoals",
            localField: "training_goals",
            foreignField: "_id",
            as: "training_goals",
          },
        },
        {
          $lookup: {
            from: "trainingstyles",
            localField: "training_styles",
            foreignField: "_id",
            as: "training_styles",
          },
        },
        // {
        //   $lookup: {
        //     from: "traininghomegyms",
        //     localField: "home_gyms",
        //     foreignField: "_id",
        //     as: "home_gyms",
        //   },
        // },

        {
          $lookup: {
            from: "traininghomegyms",
            let: { gymIds: "$home_gyms" },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $in: ["$_id", "$$gymIds"],
                  },
                },
              },
              {
                $addFields: {
                  sortOrder: {
                    $indexOfArray: ["$$gymIds", "$_id"],
                  },
                },
              },
              {
                $sort: {
                  sortOrder: 1,
                },
              },
            ],
            as: "home_gyms",
          },
        },
        {
          $addFields: {
            buddyGoals: {
              $map: {
                input: "$training_goals",
                as: "goals",
                in: { $toString: "$$goals._id" },
              },
            },
            buddyStyles: {
              $map: {
                input: "$training_styles",
                as: "styles",
                in: { $toString: "$$styles._id" },
              },
            },
          },
        },
        {
          $addFields: {
            commonGoals: {
              $setIntersection: ["$buddyGoals", currentGoals],
            },
            commonStyles: {
              $setIntersection: ["$buddyStyles", currentStyles],
            },
            levelMatch: {
              $eq: ["$training_experience", currentExperience],
            },
          },
        },
        {
          $addFields: {
            score: {
              $add: [
                { $multiply: [{ $size: "$commonGoals" }, 2] },
                { $multiply: [{ $size: "$commonStyles" }, 2] },
                { $cond: ["$levelMatch", 3, 0] },
              ],
            },
          },
        },
        {
          $addFields: {
            profile_match_score: {
              $min: [
                {
                  $round: [
                    {
                      $multiply: [{ $divide: ["$score", 7] }, 100],
                    },
                    0,
                  ],
                },
                100,
              ],
            },
          },
        },
        {
          $lookup: {
            from: "buddyrequests",
            let: { buddyId: "$_id" },
            pipeline: [
              {
                $match: {
                  deleted_at: null,
                  $expr: {
                    $or: [
                      {
                        $and: [
                          { $eq: ["$sender", userObjectId] },
                          { $eq: ["$receiver", "$$buddyId"] },
                        ],
                      },
                      {
                        $and: [
                          { $eq: ["$sender", "$$buddyId"] },
                          { $eq: ["$receiver", userObjectId] },
                        ],
                      },
                    ],
                  },
                },
              },
              {
                $project: {
                  _id: 0,
                  status: 1,
                  sender: 1,
                },
              },
            ],
            as: "buddy_request",
          },
        },
        {
          $addFields: {
            buddy_status: {
              $let: {
                vars: {
                  req: { $arrayElemAt: ["$buddy_request", 0] },
                },
                in: {
                  $cond: [
                    {
                      $in: [
                        { $ifNull: ["$$req.status", null] },
                        ["rejected", null],
                      ],
                    },
                    "request",
                    {
                      $cond: [
                        {
                          $ne: ["$$req.sender", userObjectId],
                        },
                        "review",
                        "$$req.status",
                      ],
                    },
                  ],
                },
              },
            },
          },
        },
        {
          $lookup: {
            from: "challengesusers",
            let: { userId: "$_id" },
            pipeline: [
              {
                $match: {
                  deleted_at: null,
                  $expr: {
                    $eq: [{ $toString: "$user_id" }, { $toString: "$$userId" }],
                  },
                },
              },

              {
                $lookup: {
                  from: "challenges",
                  localField: "challenge_id",
                  foreignField: "_id",
                  as: "challenge",
                },
              },

              {
                $unwind: "$challenge",
              },

              {
                $addFields: {
                  durationDays: { $toInt: "$challenge.duration" },
                  joined_at: "$createdAt",
                },
              },
              {
                $addFields: {
                  ends_at: {
                    $dateAdd: {
                      startDate: "$joined_at",
                      unit: "day",
                      amount: "$durationDays",
                    },
                  },
                },
              },

              {
                $project: {
                  _id: "$challenge._id",
                  title: {
                    $ifNull: [
                      `$challenge.title.${safeLang}`,
                      "$challenge.title.en",
                    ],
                  },
                  image: "$challenge.attachment",
                  durationDays: 1,
                  joined_at: 1,
                  ends_at: 1,
                },
              },
            ],
            as: "all_challenges",
          },
        },
        {
          $addFields: {
            joined_challenges: "$all_challenges",
            completed_challenges: {
              $filter: {
                input: "$all_challenges",
                as: "c",
                cond: {
                  $lte: ["$$c.ends_at", now],
                },
              },
            },
          },
        },
        {
          $addFields: {
            joined_challenges: {
              $cond: ["$show_active_challenges", "$joined_challenges", []],
            },
            completed_challenges: {
              $cond: ["$show_achievements", "$completed_challenges", []],
            },
          },
        },
        {
          $project: {
            buddy_status: 1,
            profile: {
              _id: "$_id",
              name: { $concat: ["$first_name", " ", "$last_name"] },
              profile_image: "$profile_image",
              training_experience: "$training_experience",
              trained_per_week: "$trained_per_week",
              training_time_week: "$training_time_week",
              training_time_weekend: "$training_time_weekend",
              profile_match_score: "$profile_match_score",

              training_goals: {
                $map: {
                  input: "$training_goals",
                  as: "goals",
                  in: {
                    _id: "$$goals._id",
                    name: `$$goals.name.${safeLang}`,
                  },
                },
              },

              training_styles: {
                $map: {
                  input: "$training_styles",
                  as: "styles",
                  in: {
                    _id: "$$styles._id",
                    name: `$$styles.name.${safeLang}`,
                  },
                },
              },
              home_gyms: {
                $map: {
                  input: "$home_gyms",
                  as: "gyms",
                  in: {
                    _id: "$$gyms._id",
                    name: `$$gyms.name.${safeLang}`,
                  },
                },
              },
              joined_challenges: "$joined_challenges",
              completed_challenges: "$completed_challenges",
            },
          },
        },
      ]);

      if (!result.length) {
        return this.resService.notFound(res, "Buddy not found", req);
      }

      return this.resService.success(
        res,
        "Buddy profile fetched successfully",
        req,
        result[0],
      );
    } catch (error) {
      console.error(error);
      return this.resService.serverError(
        res,
        "Error fetching buddy profile",
        req,
      );
    }
  }

  async sendBuddyRequest(
    senderId: string,
    receiverId: string,
    req: Request,
    res: Response,
  ) {
    try {
      if (senderId === receiverId) {
        return this.resService.badRequest(
          res,
          "You cannot send request to yourself",
          req,
        );
      }

      const existing = await this.buddyRequestModel.findOne({
        sender: new Types.ObjectId(senderId),
        receiver: new Types.ObjectId(receiverId),
      });

      // If request exists and still active
      if (existing && !existing.deleted_at) {
        return this.resService.badRequest(res, "Request already sent", req);
      }

      let request;

      // Reactivate previously unsent request
      if (existing && existing.deleted_at) {
        request = await this.buddyRequestModel.findByIdAndUpdate(
          existing._id,
          {
            deleted_at: null,
            status: "pending",
          },
          { new: true },
        );
      } else {
        request = await this.buddyRequestModel.create({
          sender: new Types.ObjectId(senderId),
          receiver: new Types.ObjectId(receiverId),
          status: "pending",
        });
      }

      return this.resService.success(
        res,
        "Buddy request sent successfully",
        req,
        request,
      );
    } catch (error) {
      return this.resService.serverError(
        res,
        "Error sending buddy request",
        req,
      );
    }
  }

  async getBuddyPendingRequests(
    userId: string,
    req: Request,
    res: Response,
    lang: string,
    page = 1,
    limit = 20,
  ) {
    try {
      const userObjectId = new Types.ObjectId(userId);
      const skip = (page - 1) * limit;

      const safeLang = SUPPORTED_LANGUAGES.includes(lang)
        ? lang
        : DEFAULT_LANGUAGE;

      const result = await this.buddyRequestModel.aggregate([
        {
          $match: {
            receiver: userObjectId,
            status: "pending",
            deleted_at: null,
          },
        },
        {
          $lookup: {
            from: "users",
            localField: "sender",
            foreignField: "_id",
            as: "sender",
          },
        },
        { $unwind: "$sender" },
        {
          $lookup: {
            from: "traininghomegyms",
            localField: "sender.home_gyms",
            foreignField: "_id",
            as: "home_gyms",
          },
        },
        {
          $project: {
            _id: 1,
            status: 1,
            createdAt: 1,
            profile: {
              _id: "$sender._id",
              name: {
                $concat: ["$sender.first_name", " ", "$sender.last_name"],
              },
              user_name: "$sender.user_name",
              profile_image: "$sender.profile_image",
              training_experience: "$sender.training_experience",
              trained_per_week: "$sender.trained_per_week",
              training_time_week: "$sender.training_time_week",
              home_gyms: {
                $map: {
                  input: "$home_gyms",
                  as: "gym",
                  in: {
                    _id: "$$gym._id",
                    name: {
                      $ifNull: [`$$gym.name.${safeLang}`, "$$gym.name.en"],
                    },
                  },
                },
              },
            },
          },
        },
        { $sort: { createdAt: -1 } },

        {
          $facet: {
            items: [{ $skip: skip }, { $limit: limit }],
            totalCount: [{ $count: "count" }],
          },
        },
      ]);

      console.log(result, "result ");

      const items = result[0]?.items || [];
      const total = result[0]?.totalCount[0]?.count || 0;

      return this.resService.success(
        res,
        "Buddy requests fetched successfully",
        req,
        {
          items,
          ...this.paginationMeta(total, page, limit),
        },
      );
    } catch (error) {
      console.error(error);
      return this.resService.serverError(
        res,
        "Error fetching buddy requests",
        req,
      );
    }
  }

  async getBuddyRecentRequests(
    userId: string,
    req: Request,
    res: Response,
    lang: string,
  ) {
    try {
      const userObjectId = new Types.ObjectId(userId);

      const safeLang = SUPPORTED_LANGUAGES.includes(lang)
        ? lang
        : DEFAULT_LANGUAGE;

      const now = new Date();

      //  Recent Buddies
      const recentBuddies = await this.buddyRequestModel.aggregate([
        {
          $match: {
            $or: [{ sender: userObjectId }, { receiver: userObjectId }],
            status: "accepted",
            deleted_at: null,
          },
        },
        {
          $addFields: {
            buddyUser: {
              $cond: [
                { $eq: ["$sender", userObjectId] },
                "$receiver",
                "$sender",
              ],
            },
          },
        },
        {
          $lookup: {
            from: "users",
            localField: "buddyUser",
            foreignField: "_id",
            as: "buddy",
          },
        },
        { $unwind: "$buddy" },

        //  "accepted today" logic
        {
          $addFields: {
            acceptedAt: "$updatedAt",
            isToday: {
              $eq: [
                {
                  $dateToString: {
                    format: "%Y-%m-%d",
                    date: "$updatedAt",
                  },
                },
                {
                  $dateToString: {
                    format: "%Y-%m-%d",
                    date: now,
                  },
                },
              ],
            },
          },
        },

        {
          $project: {
            _id: 1,
            acceptedAt: 1,
            isToday: 1,
            profile: {
              _id: "$buddy._id",
              name: {
                $concat: ["$buddy.first_name", " ", "$buddy.last_name"],
              },
              user_name: "$buddy.user_name",
              profile_image: "$buddy.profile_image",
            },
          },
        },
        { $sort: { acceptedAt: -1 } },
        { $limit: 6 },
      ]);

      return this.resService.success(
        res,
        "Buddy requests fetched successfully",
        req,
        recentBuddies,
      );
    } catch (error) {
      console.error(error);
      return this.resService.serverError(
        res,
        "Error fetching buddy requests",
        req,
      );
    }
  }

  async unsendBuddyRequest(
    senderId: string,
    receiverId: string,
    req: Request,
    res: Response,
  ) {
    try {
      const request = await this.buddyRequestModel.findOneAndUpdate(
        {
          sender: new Types.ObjectId(senderId),
          receiver: new Types.ObjectId(receiverId),
          deleted_at: null,
        },
        {
          $set: { deleted_at: new Date() },
        },
        { new: true },
      );

      if (!request) {
        return this.resService.badRequest(res, "Buddy request not found", req);
      }

      return this.resService.success(
        res,
        "Buddy request unsent successfully",
        req,
        null,
      );
    } catch (error) {
      return this.resService.serverError(
        res,
        "Error unsending buddy request",
        req,
      );
    }
  }

  async updateBuddyRequest(
    userId: string,
    requestId: string,
    status: string,
    req: Request,
    res: Response,
  ) {
    try {
      if (!Types.ObjectId.isValid(requestId)) {
        return this.resService.badRequest(res, "Invalid request ID", req);
      }
      if (!status) {
        return this.resService.badRequest(res, "Status is required", req);
      }

      const allowedStatuses = ["accepted", "rejected"];
      if (!allowedStatuses.includes(status)) {
        return this.resService.badRequest(
          res,
          "Status must be either 'accepted' or 'rejected'",
          req,
        );
      }

      const request = await this.buddyRequestModel.findOne({
        _id: new Types.ObjectId(requestId),
        receiver: new Types.ObjectId(userId),
        deleted_at: null,
      });

      if (!request) {
        return this.resService.notFound(res, "Request not found", req);
      }

      if (request.status === "accepted") {
        return this.resService.badRequest(res, "Request already accepted", req);
      }

      request.status = status;
      await request.save();

      return this.resService.success(
        res,
        "Request updated successfully",
        req,
        request,
      );
    } catch (error) {
      return this.resService.serverError(res, "Error updating request", req);
    }
  }

  async globalSearch(
    userId: string,
    query: string,
    page = 1,
    limit = 10,
    req: Request,
    res: Response,
    lang: string,
  ) {
    try {
      let regex = new RegExp(`${query}`, "i");
      const skip = (page - 1) * limit;

      const safeLang = SUPPORTED_LANGUAGES.includes(lang)
        ? lang
        : DEFAULT_LANGUAGE;

      const gymNameField = `name.${safeLang}`;
      const addressField = `address.${safeLang}`;

      // Get user's primary gym
      const user = await this.userModel
        .findById(userId)
        .select("home_gyms")
        .lean();

      const primaryGymId = user?.home_gyms?.[0]?.toString();

      // ---------------- GYMS ----------------
      const gyms = await this.trainingHomeGymModel
        .find({
          deleted_at: null,
          status: 1,
          ...(query && { [gymNameField]: { $regex: regex } }),
        })
        .select(`_id ${gymNameField}`)
        .sort({ [gymNameField]: 1 })
        .lean();

      const gymResults = gyms.map((gym) => ({
        id: gym._id,
        title: gym?.name?.[safeLang],
        dataType: "Gym",
        isPrimaryGym: gym._id.toString() === primaryGymId,
      }));

      // ---------------- USERS ----------------
      // let userResults: any[] = [];

      let userResults: any[] = [];

      if (query) {
        regex = new RegExp(`^${query}`, "i");
        const users = await this.userModel
          .find({
            deleted_at: null,
            is_profile_visible: true,
            status: 1,
            _id: { $ne: new Types.ObjectId(userId) },
            user_name: { $regex: regex },
          })
          .select("_id user_name")
          .sort({ user_name: 1 })
          .lean();

        userResults = users.map((user) => ({
          id: user._id,
          title: user.user_name,
          dataType: "User",
        }));
      }

      // ---------------- CITIES ----------------
      // let cityResults: any[] = [];
      // if (query) {
      //   const cities = await this.trainingHomeGymModel.aggregate([
      //     {
      //       $match: {
      //         [addressField]: { $regex: regex },
      //         [gymNameField]: { $not: regex },
      //         deleted_at: null,
      //         status: 1,
      //       },
      //     },
      //     {
      //       $group: {
      //         _id: `$${addressField}`,
      //         gymId: { $first: "$_id" },
      //       },
      //     },
      //     {
      //       $sort: { _id: 1 },
      //     },
      //     {
      //       $project: {
      //         id: "$gymId",
      //         title: "$_id",
      //         dataType: { $literal: "City" },
      //       },
      //     },
      //   ]);

      //   cityResults = cities.map((city) => ({
      //     id: city.id,
      //     title: city.title,
      //     dataType: "City",
      //   }));
      // }

      let cityResults: any[] = [];

      if (query) {
        regex = new RegExp(`^${query}`, "i");
        const cities = await this.cityModel
          .find({
            ...(query && { [`name.en`]: { $regex: regex } }),
          })
          .select(`_id name.en location`)
          .sort({ [`name.en`]: 1 })
          .lean();
        cityResults = cities.map((city) => ({
          id: city._id,
          title: city?.name?.en,
          dataType: "City",
          location: city.location,
        }));
      }

      const combinedResults = [
        ...gymResults,
        ...userResults,
        ...cityResults,
      ].sort((a, b) => {
        if (a.isPrimaryGym) return -1;
        if (b.isPrimaryGym) return 1;
        return a.title.localeCompare(b.title);
      });

      const total = combinedResults.length;
      const items = combinedResults.slice(skip, skip + limit);
      const pages = Math.ceil(total / limit);

      return this.resService.success(
        res,
        "Search results fetched successfully",
        req,
        {
          items,
          total,
          page,
          limit,
          pages,
        },
      );
    } catch (error) {
      return this.resService.serverError(
        res,
        "Error fetching search results",
        req,
      );
    }
  }
}
