import { Injectable } from "@nestjs/common";
import { CreateTrainingCalloutDto } from "./dto/create-training-callout.dto";
import { UpdateTrainingCalloutDto } from "./dto/update-training-callout.dto";
import { Types } from "mongoose";
import { ResponseService } from "src/common/service/response.service";
import { Request, Response } from "express";
import { InjectModel } from "@nestjs/mongoose";
import {
  BuddyRequest,
  BuddyRequestDocument,
} from "src/buddies/schema/buddy-requests.schema";
import {
  TrainingHomeGym,
  TrainingHomeGymSchema,
  TrainingHomeGymDocument,
} from "src/training/schemas/training-homegym.schema";
import { Model } from "mongoose";
import {
  TrainingCallout,
  TrainingCalloutDocument,
} from "./schema/training-callout.schema";
import { GetTrainingCalloutDto } from "./dto/get-training-callout.dto";
import { User, UserSchema, UserDocument } from "src/user/schemas/user.schema";

import {
  DEFAULT_LANGUAGE,
  SUPPORTED_LANGUAGES,
} from "src/common/constants/language.constant";

@Injectable()
export class TrainingCalloutService {
  constructor(
    @InjectModel(TrainingCallout.name)
    private trainingCalloutModel: Model<TrainingCalloutDocument>,
    @InjectModel(BuddyRequest.name)
    private buddyModel: Model<BuddyRequestDocument>,
    @InjectModel(User.name)
    private userModel: Model<UserDocument>,
    @InjectModel(TrainingHomeGym.name)
    private trainingHomeGymModel: Model<TrainingHomeGymDocument>,
    private readonly resService: ResponseService,
  ) {}

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

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

  //   const { page = 1, limit = 10 } = body;

  //   const skip = (page - 1) * limit;

  //   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((gym) => gym._id);

  //   const filter = {
  //     isActive: true,
  //     gym_id: { $in: gymIds },
  //   };

  //   const [items, total] = await Promise.all([
  //     this.trainingCalloutModel
  //       .find(filter)
  //       .populate("created_by", "user_name profile_image")
  //       .populate({
  //         path: "gym_id",
  //         select: `${gymNameField} address`,
  //       })
  //       .sort({ createdAt: -1 })
  //       .skip(skip)
  //       .limit(limit)
  //       .lean(),

  //     this.trainingCalloutModel.countDocuments(filter),
  //   ]);

  //   return {
  //     items,
  //     ...this.paginationMeta(total, page, limit),
  //   };
  // }

  // private async searchTrainingCalloutByLocation(
  //   userId: string,
  //   body: SearchTrainingCalloutDto,
  // ) {
  //   const { location, radiusKm = 10, page = 1, limit = 10 } = body;

  //   const skip = (page - 1) * limit;

  //   if (!location || location.lat == null || location.lng == null) {
  //     return {
  //       items: [],
  //       ...this.paginationMeta(0, page, limit),
  //     };
  //   }

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

  //   const gymIds = gyms.map((gym) => gym._id);

  //   const filter = {
  //     isActive: true,
  //     gym_id: { $in: gymIds },
  //   };

  //   const [items, total] = await Promise.all([
  //     this.trainingCalloutModel
  //       .find(filter)
  //       .populate("created_by", "user_name profile_image")
  //       .populate("gym_id", "gymName address")
  //       .sort({ createdAt: -1 })
  //       .skip(skip)
  //       .limit(limit)
  //       .lean(),

  //     this.trainingCalloutModel.countDocuments(filter),
  //   ]);

  //   return {
  //     items,
  //     ...this.paginationMeta(total, page, limit),
  //   };
  // }

  private async getAllTrainingCallouts(userId: string, page = 1, limit = 10) {
    page = Number(page) || 1;
    limit = Number(limit) || 10;

    const skip = (page - 1) * limit;

    const filter = {
      isActive: true,
    };

    const [items, total] = await Promise.all([
      this.trainingCalloutModel
        .find(filter)
        .populate("createdBy", "userName profileImage")
        .populate("gymId", "gymName address")
        .sort({ createdAt: -1 })
        .skip(skip)
        .limit(limit)
        .lean(),

      this.trainingCalloutModel.countDocuments(filter),
    ]);

    return {
      items,
      ...this.paginationMeta(total, page, limit),
    };
  }

  async createTrainingCallout(
    userId: string,
    body: CreateTrainingCalloutDto,
    req: Request,
    res: Response,
  ) {
    try {
      const gymExists = await this.trainingHomeGymModel.findById(body.gymId);

      if (!gymExists) {
        return this.resService.notFound(res, "Gym not found", req);
      }
      const trainingCallout = await this.trainingCalloutModel.create({
        created_by: new Types.ObjectId(userId),
        title: body.title,
        description: body.description,
        training_date: new Date(body.trainingDate),
        gym_id: new Types.ObjectId(body.gymId),
        max_participants: body.maxParticipants || 1,
        visible_to: body.visibleTo || "community",
        joined_users: [],
      });

      return this.resService.success(
        res,
        "Training call out created successfully",
        req,
        trainingCallout,
      );
    } catch (error) {
      console.log(error, "Error in createTrainingCallout");
      return this.resService.serverError(
        res,
        "Error creating training call out",
        req,
      );
    }
  }

  async updateTrainingCallout(
    userId: string,
    calloutId: string,
    body: UpdateTrainingCalloutDto,
    req: Request,
    res: Response,
  ) {
    try {
      const trainingCallout = await this.trainingCalloutModel.findOne({
        _id: new Types.ObjectId(calloutId),
        created_by: new Types.ObjectId(userId),
        deleted_at: null,
      });

      if (!trainingCallout) {
        return this.resService.notFound(
          res,
          "Training call out not found",
          req,
        );
      }

      await this.trainingCalloutModel.updateOne(
        {
          _id: new Types.ObjectId(calloutId),
        },
        {
          $set: {
            ...(body.title && { title: body.title }),
            ...(body.description && {
              description: body.description,
            }),
            ...(body.trainingDate && {
              training_date: new Date(body.trainingDate),
            }),
            ...(body.gymId && {
              gym_id: new Types.ObjectId(body.gymId),
            }),
            ...(body.maxParticipants && {
              max_participants: body.maxParticipants,
            }),
            ...(body.visibleTo && {
              visible_to: body.visibleTo,
            }),
          },
        },
      );

      const updatedCallout =
        await this.trainingCalloutModel.findById(calloutId);

      return this.resService.success(
        res,
        "Training call out updated successfully",
        req,
        updatedCallout,
      );
    } catch (error) {
      console.log(error, "Error in updateTrainingCallout");
      return this.resService.serverError(
        res,
        "Error updating training call out",
        req,
      );
    }
  }

  async deleteTrainingCallout(
    userId: string,
    calloutId: string,
    req: Request,
    res: Response,
  ) {
    try {
      const trainingCallout = await this.trainingCalloutModel.findOne({
        _id: new Types.ObjectId(calloutId),
        created_by: new Types.ObjectId(userId),
        deletedAt: null,
      });

      if (!trainingCallout) {
        return this.resService.notFound(
          res,
          "Training call out not found",
          req,
        );
      }

      let updated = await this.trainingCalloutModel.updateOne(
        {
          _id: new Types.ObjectId(calloutId),
        },
        {
          $set: {
            deleted_at: new Date(),
          },
        },
      );

      return this.resService.success(
        res,
        "Training call out deleted successfully",
        req,
      );
    } catch (error) {
      console.log(error, "Error in deleteTrainingCallout");
      return this.resService.serverError(
        res,
        "Error deleting training call out",
        req,
      );
    }
  }

  // async getTrainingCallouts(
  //   userId: string,
  //   req: Request,
  //   res: Response,
  //   page = 1,
  //   limit = 10,
  // ) {
  //   try {
  //     page = Number(page) || 1;
  //     limit = Number(limit) || 10;

  //     const skip = (page - 1) * limit;

  //     const filter = {
  //       isActive: true,
  //     };

  //     const [items, total] = await Promise.all([
  //       this.trainingCalloutModel
  //         .find(filter)
  //         .populate("created_by", "username profile_image")
  //         .populate("gym_id", "gymName address")
  //         .sort({ createdAt: -1 })
  //         .skip(skip)
  //         .limit(limit)
  //         .lean(),

  //       this.trainingCalloutModel.countDocuments(filter),
  //     ]);

  //     return this.resService.success(
  //       res,
  //       "Training call outs fetched successfully",
  //       req,
  //       {
  //         items,
  //         ...this.paginationMeta(total, page, limit),
  //       },
  //     );
  //   } catch (error) {
  //     console.log(error, "Error in getTrainingCallouts");
  //     return this.resService.serverError(
  //       res,
  //       "Error fetching training call outs",
  //       req,
  //     );
  //   }
  // }

  async joinTrainingCallout(
    userId: string,
    calloutId: string,
    req: Request,
    res: Response,
  ) {
    try {
      const callout = await this.trainingCalloutModel.findOne({
        _id: new Types.ObjectId(calloutId),
        deletedAt: null,
      });

      if (!callout) {
        return this.resService.notFound(
          res,
          "Training call out not found",
          req,
        );
      }

      const alreadyJoined = callout.joined_users?.some(
        (id) => id.toString() === userId,
      );

      if (alreadyJoined) {
        return this.resService.badRequest(
          res,
          "You have already joined this training call out",
          req,
        );
      }

      const currentParticipants = callout.joined_users?.length ?? 0;

      // Only participant slots (creator not counted in max_participants)
      const allowedParticipants = callout.max_participants ?? 0;

      if (currentParticipants >= allowedParticipants) {
        return this.resService.badRequest(
          res,
          "Training call out is already full",
          req,
        );
      }

      if (!callout.joined_users) {
        callout.joined_users = [];
      }

      callout.joined_users.push(new Types.ObjectId(userId));
      await callout.save();

      // Set suggestion_sent flag to true to trigger buddy suggestions
      const updated = await this.userModel.updateOne(
        { _id: new Types.ObjectId(userId) },
        { $set: { suggestion_sent: true } },
      );

      console.log(
        "Set suggestion_sent flag to true for user:",
        userId,
        updated,
      );

      return this.resService.success(
        res,
        "Joined training call out successfully",
        req,
        callout,
      );
    } catch (error) {
      console.log(error, "Error in joinTrainingCallout");
      return this.resService.serverError(
        res,
        "Error joining training call out",
        req,
      );
    }
  }

  async getMyJoinedTrainingCallouts(
    userId: string,
    page = 1,
    limit = 10,
    req: Request,
    res: Response,
  ) {
    try {
      page = Number(page) || 1;
      limit = Number(limit) || 10;
      const skip = (page - 1) * limit;

      const filter = {
        // is_active: true,
        joined_users: { $in: [new Types.ObjectId(userId)] },
      };

      const [items, total] = await Promise.all([
        this.trainingCalloutModel
          .find(filter)
          .populate("created_by", "user_name profile_image")
          .populate("gym_id", "gymName address")
          .sort({ createdAt: -1 })
          .skip(skip)
          .limit(limit)
          .lean(),

        this.trainingCalloutModel.countDocuments(filter),
      ]);

      return this.resService.success(
        res,
        "My joined training call outs fetched successfully",
        req,
        {
          items,
          ...this.paginationMeta(total, page, limit),
        },
      );
    } catch (error) {
      console.log(error, "Error in getMyJoinedTrainingCallouts");
      return this.resService.serverError(
        res,
        "Error fetching joined training call outs",
        req,
      );
    }
  }

  //   async getTrainingCallouts(
  //   userId: string,
  //   body: GetTrainingCalloutDto,
  //   req: Request,
  //   res: Response,
  // ) {
  //   try {
  //     const {
  //       userName,
  //       gymName,
  //       location: {
  //         lat,
  //         lng,
  //         northEastLat: maxLat,
  //         northEastLng: maxLng,
  //         southWestLat: minLat,
  //         southWestLng: minLng,
  //       } = {},
  //       radius = 10,
  //     } = body;

  //     const userObjectId = new Types.ObjectId(userId);

  //     let userFilterIds: Types.ObjectId[] = [];
  //     let gymIds: Types.ObjectId[] = [];
  //     const gymFilter: any = {};

  //     // ---------------------------------------------------
  //     // 1. Filter User IDs by userName
  //     // ---------------------------------------------------
  //     if (userName) {
  //       const users = await this.userModel
  //         .find({
  //           user_name: { $regex: userName, $options: "i" },
  //           deletedAt: null,
  //         })
  //         .select("_id");

  //       userFilterIds = users.map((user) => user._id);
  //     }

  //     // ---------------------------------------------------
  //     // 2. Build Gym Filter
  //     // ---------------------------------------------------
  //     if (gymName) {
  //       gymFilter.name = { $regex: gymName, $options: "i" };
  //     }

  //     // ---------------------------------------------------
  //     // 3. Filter by Nearby Gym Location
  //     // ---------------------------------------------------
  //     if (lat != null && lng != null) {
  //       gymFilter.location = {
  //         $near: {
  //           $geometry: {
  //             type: "Point",
  //             coordinates: [lng, lat],
  //           },
  //           $maxDistance: radius * 1000, // radius in meter
  //         },
  //       };
  //     }

  //     // ---------------------------------------------------
  //     // 4. Filter by Boundary Wall
  //     // ---------------------------------------------------
  //     if (
  //       minLat != null &&
  //       maxLat != null &&
  //       minLng != null &&
  //       maxLng != null
  //     ) {
  //       gymFilter.location = {
  //         $geoWithin: {
  //           $box: [
  //             [minLng, minLat],
  //             [maxLng, maxLat],
  //           ],
  //         },
  //       };
  //     }

  //     // ---------------------------------------------------
  //     // 5. Get Gym IDs
  //     // ---------------------------------------------------
  //     if (Object.keys(gymFilter).length > 0) {
  //       const gyms = await this.trainingHomeGymModel
  //         .find(gymFilter)
  //         .select("_id");

  //       gymIds = gyms.map((gym) => gym._id);
  //     }

  //     // ---------------------------------------------------
  //     // 6. Get Buddy IDs
  //     // ---------------------------------------------------
  //     const buddyRelations = await this.buddyModel.find({
  //       status: "accepted",
  //       deleted_at: null,
  //       $or: [{ sender: userObjectId }, { receiver: userObjectId }],
  //     });

  //     const myBuddyIds = buddyRelations.map((buddy: any) => {
  //       if (buddy.sender.toString() === userObjectId.toString()) {
  //         return buddy.receiver;
  //       }

  //       return buddy.sender;
  //     });

  //     // ---------------------------------------------------
  //     // 7. Open Callouts Query
  //     // ---------------------------------------------------
  //     const openCalloutQuery: any = {
  //       deletedAt: null,

  //       created_by: {
  //         $ne: userObjectId,
  //       },

  //       joined_users: {
  //         $ne: userObjectId,
  //       },

  //       $or: [
  //         {
  //           visible_to: "community",
  //         },
  //         {
  //           visible_to: "buddies",
  //           created_by: {
  //             $in: myBuddyIds,
  //           },
  //         },
  //       ],
  //     };

  //     // ---------------------------------------------------
  //     // 8. Joined Callouts Query
  //     // ---------------------------------------------------
  //     const joinedCalloutQuery: any = {
  //       deletedAt: null,

  //       joined_users: {
  //         $in: [userObjectId],
  //       },

  //       created_by: {
  //         $ne: userObjectId,
  //       },
  //     };

  //     // ---------------------------------------------------
  //     // 9. Created Callouts Query
  //     // ---------------------------------------------------
  //     const createdCalloutQuery: any = {
  //       deletedAt: null,

  //       created_by: userObjectId,
  //     };

  //     // ---------------------------------------------------
  //     // 10. Apply Username Filter
  //     // ---------------------------------------------------
  //     if (userFilterIds.length > 0) {
  //       openCalloutQuery.created_by = {
  //         $in: userFilterIds,
  //         $ne: userObjectId,
  //       };

  //       joinedCalloutQuery.created_by = {
  //         $in: userFilterIds,
  //         $ne: userObjectId,
  //       };

  //       createdCalloutQuery.created_by = {
  //         $in: userFilterIds,
  //       };
  //     }

  //     // ---------------------------------------------------
  //     // 11. Apply Gym Filter
  //     // ---------------------------------------------------
  //     if (gymIds.length > 0) {
  //       openCalloutQuery.gym_id = {
  //         $in: gymIds,
  //       };

  //       joinedCalloutQuery.gym_id = {
  //         $in: gymIds,
  //       };

  //       createdCalloutQuery.gym_id = {
  //         $in: gymIds,
  //       };
  //     }

  //     // ---------------------------------------------------
  //     // 12. Populate Function
  //     // ---------------------------------------------------
  //     const populateCallout = (query: any) => {
  //       return query
  //         .populate("created_by", "user_name profile_image")
  //         .populate("gym_id", "name address location")
  //         .sort({ createdAt: -1 });
  //     };

  //     // ---------------------------------------------------
  //     // 13. Fetch Open Callouts
  //     // ---------------------------------------------------
  //     const openCallouts = await populateCallout(
  //       this.trainingCalloutModel.find(openCalloutQuery),
  //     );

  //     // ---------------------------------------------------
  //     // 14. Fetch Joined Callouts
  //     // ---------------------------------------------------
  //     const joinedCallouts = await populateCallout(
  //       this.trainingCalloutModel.find(joinedCalloutQuery),
  //     );

  //     // ---------------------------------------------------
  //     // 15. Fetch Created Callouts
  //     // ---------------------------------------------------
  //     const createdCallouts = await populateCallout(
  //       this.trainingCalloutModel.find(createdCalloutQuery),
  //     );

  //     return this.resService.success(
  //       res,
  //       "Training callouts fetched successfully",
  //       req,
  //       {
  //         openCallouts,
  //         joinedCallouts,
  //         createdCallouts,
  //       },
  //     );
  //   } catch (error) {
  //     console.log(error, "Error in getTrainingCallouts");

  //     return this.resService.serverError(
  //       res,
  //       "Error fetching training callouts",
  //       req,
  //     );
  //   }
  // }

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

      // Search by username
      if (userName) {
        const result = await this.searchCalloutsByUsername(userId, body, lang);

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

      // Search by gym name
      if (gymName) {
        const result = await this.searchCalloutsByGym(userId, body, lang);

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

      // Search by location / boundary / primary gym fallback
      const result = await this.searchCalloutsByLocation(userId, body, lang);

      return this.resService.success(
        res,
        "Training callouts fetched successfully",
        req,
        result,
      );
    } catch (error) {
      console.log(error, "Error in getTrainingCallouts");

      return this.resService.serverError(
        res,
        "Error fetching training callouts",
        req,
      );
    }
  }

  // private async searchCalloutsByUsername(
  //   userId: string,
  //   body: GetTrainingCalloutDto,
  //   lang: string,
  // ) {
  //   const users = await this.userModel
  //     .find({
  //       user_name: { $regex: `^${body.userName}`, $options: "i" },
  //       deleted_at: null,
  //     })
  //     .select("_id");

  //   const userIds = users.map((user) => user._id);

  //   if (!userIds.length) {
  //     return {
  //       openCallouts: [],
  //       joinedCallouts: [],
  //       // createdCallouts: [],
  //     };
  //   }

  //   return this.findCallouts(
  //     userId,
  //     {
  //       createdByIds: userIds,
  //     },
  //     lang,
  //   );
  // }

  private async searchCalloutsByUsername(
    userId: string,
    body: GetTrainingCalloutDto,
    lang: string,
  ) {
    const users = await this.userModel
      .find({
        user_name: { $regex: `^${body.userName}`, $options: "i" },
        deleted_at: null,
      })
      .select("_id");

    const userIds = users.map((user) => user._id);

    // If no user found, only createdCallouts should still come
    if (!userIds.length) {
      return this.findCallouts(
        userId,
        {
          createdByIds: [new Types.ObjectId(userId)],
        },
        lang,
      );
    }

    return this.findCallouts(
      userId,
      {
        createdByIds: userIds,
      },
      lang,
    );
  }

  // private async searchCalloutsByGym(
  //   userId: string,
  //   body: GetTrainingCalloutDto,
  //   lang: string,
  // ) {
  //   const gyms = await this.trainingHomeGymModel
  //     .find({
  //       "name.en": { $regex: `^${body.gymName}`, $options: "i" },
  //       status: 1,
  //       deleted_at: null,
  //     })
  //     .select("_id");

  //   const gymIds = gyms.map((gym) => gym._id);

  //   if (!gymIds.length) {
  //     return {
  //       openCallouts: [],
  //       joinedCallouts: [],
  //       // createdCallouts: [],
  //     };
  //   }

  //   return this.findCallouts(
  //     userId,
  //     {
  //       gymIds,
  //     },
  //     lang,
  //   );
  // }

  private async searchCalloutsByGym(
    userId: string,
    body: GetTrainingCalloutDto,
    lang: string,
  ) {
    const gyms = await this.trainingHomeGymModel
      .find({
        "name.en": { $regex: `^${body.gymName}`, $options: "i" },
        status: 1,
        deleted_at: null,
      })
      .select("_id");

    const gymIds = gyms.map((gym) => gym._id);

    // If no gym found, still return my created callouts
    if (!gymIds.length) {
      return this.findCallouts(
        userId,
        {
          createdByIds: [new Types.ObjectId(userId)],
        },
        lang,
      );
    }

    return this.findCallouts(
      userId,
      {
        gymIds,
      },
      lang,
    );
  }

  // private async searchCalloutsByLocation(
  //   userId: string,
  //   body: GetTrainingCalloutDto,
  //   lang: string,
  // ) {

  //   console.log("callout search by location with body:", body);
  //   let gymIds: Types.ObjectId[] = [];

  //   // 1. Search by 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],
  //               [northEastLng, northEastLat],
  //             ],
  //           },
  //         },
  //         status: 1,
  //         deleted_at: null,
  //       },
  //       { _id: 1 },
  //     );

  //     gymIds = gyms.map((gym) => gym._id);
  //   }

  //   // 2. Search by nearby lat/lng
  //   else if (body.location?.lat != null && body.location?.lng != null) {
  //     const { lat, lng } = body.location;
  //     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 },
  //     );

  //     gymIds = gyms.map((gym) => gym._id);
  //   }

  //   // 3. Fallback to user's primary gym
  //   else {
  //     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 {
  //           openCallouts: [],
  //           joinedCallouts: [],
  //           // createdCallouts: [],
  //         };
  //       }

  //       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 },
  //       );

  //       gymIds = gyms.map((gym) => gym._id);
  //     } 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 {
  //       openCallouts: [],
  //       joinedCallouts: [],
  //       // createdCallouts: [],
  //     };
  //   }

  //   return this.findCallouts(
  //     userId,
  //     {
  //       gymIds,
  //     },
  //     lang,
  //   );
  // }

  private async searchCalloutsByLocation(
    userId: string,
    body: GetTrainingCalloutDto,
    lang: string,
  ) {
    console.log("callout search by location with body:", body);

    let gymIds: Types.ObjectId[] = [];

    // 1. Search by 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],
                [northEastLng, northEastLat],
              ],
            },
          },
          status: 1,
          deleted_at: null,
        },
        { _id: 1 },
      );

      gymIds = gyms.map((gym) => gym._id);
    }

    // 2. Search by nearby lat/lng
    else if (body.location?.lat != null && body.location?.lng != null) {
      const { lat, lng } = body.location;
      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 },
      );

      gymIds = gyms.map((gym) => gym._id);
    }

    // 3. Fallback to user's primary gym
    else {
      const user = await this.userModel
        .findById(userId)
        .select("home_gyms")
        .lean();

      const radiusKm = body.radius ?? 20;

      let lat: number;
      let lng: number;

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

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

        if (primaryGym?.location?.coordinates?.length) {
          [lng, lat] = primaryGym.location.coordinates;
        } else {
          // Default map center if primary gym not found
          lat = 53.55811699336884;
          lng = 9.998501206772588;
        }
      } else {
        // Default map center if user has no gym
        lat = 53.55811699336884;
        lng = 9.998501206772588;
      }

      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((gym) => gym._id);
    }

    // If no gyms found, still return my created callouts
    if (!gymIds.length) {
      return this.findCallouts(
        userId,
        {
          createdByIds: [new Types.ObjectId(userId)],
        },
        lang,
      );
    }

    return this.findCallouts(
      userId,
      {
        gymIds,
      },
      lang,
    );
  }

  private async findCallouts(
    userId: string,
    filters: {
      createdByIds?: Types.ObjectId[];
      gymIds?: Types.ObjectId[];
    },
    lang: string,
  ) {
    const userObjectId = new Types.ObjectId(userId);

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

    const buddyRelations = await this.buddyModel.find({
      status: "accepted",
      deleted_at: null,
      $or: [{ sender: userObjectId }, { receiver: userObjectId }],
    });

    const myBuddyIds = buddyRelations.map((buddy: any) => {
      if (buddy.sender.toString() === userObjectId.toString()) {
        return buddy.receiver;
      }

      return buddy.sender;
    });

    const openCalloutQuery: any = {
      deleted_at: null,
      created_by: {
        $ne: userObjectId,
      },
      joined_users: {
        $ne: userObjectId,
      },
      $or: [
        {
          visible_to: "community",
        },
        {
          visible_to: "buddies",
          created_by: {
            $in: myBuddyIds,
          },
        },
      ],
      // Exclude callouts that have reached max participant limit (participants only, not including creator)
      $expr: {
        $or: [
          { $eq: [{ $ifNull: ["$max_participants", null] }, null] },
          {
            $lt: [
              { $size: { $ifNull: ["$joined_users", []] } },
              "$max_participants" // Only participant slots, creator not counted
            ]
          }
        ]
      },
      // Exclude callouts where training date has already passed
      training_date: {
        $gte: new Date(),
      },
    };

    const joinedCalloutQuery: any = {
      deleted_at: null,
      joined_users: {
        $in: [userObjectId],
      },
      created_by: {
        $ne: userObjectId,
      },
    };

    const createdCalloutQuery: any = {
      deleted_at: null,
      created_by: userObjectId,
    };

    // const formatCallouts = (callouts: any[]) => {
    //   return callouts.map((callout: any) => {
    //     const obj = callout.toObject();

    //     obj.gym = obj.gym_id
    //       ? {
    //           _id: obj.gym_id._id,
    //           name: obj.gym_id.name?.[safeLang] || "",
    //           address: obj.gym_id.address?.[safeLang] || "",
    //           location: obj.gym_id.location,
    //         }
    //       : null;

    //     delete obj.gym_id;

    //     return obj;
    //   });
    // };

    const formatCallouts = (callouts: any[]) => {
      return callouts.map((callout: any) => {
        const obj = callout.toObject();

        obj.created_by = obj.created_by
          ? {
              _id: obj.created_by._id,
              user_name: obj.created_by.user_name ?? "",
              profile_image: obj.created_by.profile_image ?? null,
            }
          : {
              _id: null,
              user_name: "",
              profile_image: null,
            };

        obj.gym = obj.gym_id
          ? {
              _id: obj.gym_id._id,
              name: obj.gym_id.name?.[safeLang] || "",
              address: obj.gym_id.address?.[safeLang] || "",
              location: obj.gym_id.location,
            }
          : null;
        delete obj.gym_id;
        return obj;
      });
    };

    // Apply created_by filter
    if (filters.createdByIds?.length) {
      openCalloutQuery.created_by = {
        $in: filters.createdByIds,
        $ne: userObjectId,
      };

      joinedCalloutQuery.created_by = {
        $in: filters.createdByIds,
        $ne: userObjectId,
      };
    }

    // Apply gym filter
    if (filters.gymIds?.length) {
      openCalloutQuery.gym_id = {
        $in: filters.gymIds,
      };

      joinedCalloutQuery.gym_id = {
        $in: filters.gymIds,
      };
    }

    const populateCallout = (query: any) => {
      return query
        .populate("created_by", "user_name profile_image is_profile_visible")
        .populate("gym_id", "name address location")
        .sort({ createdAt: -1 });
    };

    const openCalloutsRaw = await populateCallout(
      this.trainingCalloutModel.find(openCalloutQuery),
    );

    const joinedCalloutsRaw = await populateCallout(
      this.trainingCalloutModel.find(joinedCalloutQuery),
    );

    const createdCalloutsRaw = await populateCallout(
      this.trainingCalloutModel.find(createdCalloutQuery),
    );

    // Filter out callouts where creator's profile is not visible (for open and joined)
    const openCallouts = openCalloutsRaw.filter(
      (callout: any) => callout.created_by?.is_profile_visible !== false
    );

    const joinedCallouts = joinedCalloutsRaw.filter(
      (callout: any) => callout.created_by?.is_profile_visible !== false
    );

    const createdCallouts = createdCalloutsRaw;

    // Get gym location statistics for open callouts (only visible creators)
    const gymLocationStats = await this.trainingCalloutModel.aggregate([
      {
        $match: openCalloutQuery,
      },
      {
        $lookup: {
          from: "users",
          localField: "created_by",
          foreignField: "_id",
          as: "creator",
        },
      },
      {
        $unwind: {
          path: "$creator",
          preserveNullAndEmptyArrays: false,
        },
      },
      {
        $match: {
          "creator.is_profile_visible": { $ne: false },
        },
      },
      {
        $lookup: {
          from: "traininghomegyms",
          localField: "gym_id",
          foreignField: "_id",
          as: "gym",
        },
      },
      {
        $unwind: {
          path: "$gym",
          preserveNullAndEmptyArrays: false,
        },
      },
      {
        $group: {
          _id: "$gym_id",
          gymName: { $first: `$gym.name.${safeLang}` },
          location: { $first: "$gym.location" },
          address: { $first: `$gym.address.${safeLang}` },
          calloutCount: { $sum: 1 },
        },
      },
      {
        $project: {
          _id: 1,
          gymName: 1,
          location: 1,
          address: 1,
          calloutCount: 1,
        },
      },
      {
        $sort: {
          calloutCount: -1,
        },
      },
    ]);

    return {
      openCallouts: formatCallouts(openCallouts),
      joinedCallouts: formatCallouts(joinedCallouts),
      createdCallouts: formatCallouts(createdCallouts),
      gymLocationStats,
    };
  }

  async quitTrainingCallout(
    userId: string,
    calloutId: string,
    req: Request,
    res: Response,
  ) {
    try {
      const callout = await this.trainingCalloutModel.findById(calloutId);

      if (!callout) {
        return this.resService.notFound(
          res,
          "Training call out not found",
          req,
        );
      }

      const alreadyJoined = callout.joined_users?.some(
        (user: any) => user.toString() === userId.toString(),
      );

      if (!alreadyJoined) {
        return this.resService.badRequest(
          res,
          "You have not joined this training call out",
          req,
        );
      }

      // Prevent creator from quitting their own callout
      if (
        callout.created_by &&
        callout.created_by.toString() === userId.toString()
      ) {
        return this.resService.badRequest(
          res,
          "Creator cannot quit their own training call out",
          req,
        );
      }

      await this.trainingCalloutModel.findByIdAndUpdate(calloutId, {
        $pull: {
          joined_users: new Types.ObjectId(userId),
        },
      });

      return this.resService.success(
        res,
        "Training call out quit successfully",
        req,
      );
    } catch (error) {
      console.log(error, "Error in quitTrainingCallout");

      return this.resService.serverError(
        res,
        "Error quitting training call out",
        req,
      );
    }
  }

  async getBuddySuggestionsFromCallouts(
    userId: string,
    req: Request,
    res: Response,
    lang: string,
  ) {
    try {
      const safeLang = SUPPORTED_LANGUAGES.includes(lang)
        ? lang
        : DEFAULT_LANGUAGE;
      const userObjectId = new Types.ObjectId(userId);

      // Find all accepted buddies of current user
      const buddyRelations = await this.buddyModel.find({
        status: "accepted",
        deleted_at: null,
        $or: [{ sender: userObjectId }, { receiver: userObjectId }],
      });

      console.log(buddyRelations, "Buddy Relations");
      const buddyIds = buddyRelations.map((buddy: any) => {
        return buddy.sender.toString() === userId.toString()
          ? buddy.receiver.toString()
          : buddy.sender.toString();
      });
      console.log(buddyIds, "Buddy IDs");

      // Get current user to check suggestion_sent flag
      const currentUser = await this.userModel
        .findById(userId)
        .select("suggestion_sent");
      const suggestionSent = currentUser?.suggestion_sent || false;

      // Check if suggestions should be sent (only when new callout joined)
      if (!suggestionSent) {
        console.log("No new callout joined, returning empty list");
        return this.resService.success(
          res,
          "Buddy suggestions fetched successfully",
          req,
          [],
        );
      }

      // Get all past callouts joined by current user (before current time, at least 2 hours old)
      const now = new Date();
      const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000); // 2 hours ago

      const allPastCallouts = await this.trainingCalloutModel
        .find({
          visible_to: "community",
          deleted_at: null,
          $or: [
            { joined_users: { $in: [userObjectId] } }, // User joined the callout
            { created_by: userObjectId }, // User created the callout
          ],
          training_date: {
            $lte: twoHoursAgo, // Only callouts that are at least 2 hours old
          },
        })
        .sort({ training_date: -1 }); // Sort by most recent first

      // Get only the nearest previous callout that is at least 2 hours old
      const joinedCallouts =
        allPastCallouts.length > 0 ? [allPastCallouts[0]] : [];

      console.log(joinedCallouts, "Nearest Previous Joined Callout");

      const suggestedUserIds = new Set<string>();

      joinedCallouts.forEach((callout: any) => {
        callout.joined_users.forEach((joinedUserId: any) => {
          const joinedUserString = joinedUserId.toString();

          const isCurrentUser = joinedUserString === userId.toString();
          const isAlreadyBuddy = buddyIds.includes(joinedUserString);

          console.log(
            isCurrentUser,
            isAlreadyBuddy,
            "User Check",
            joinedUserString,
          );

          if (!isCurrentUser && !isAlreadyBuddy) {
            suggestedUserIds.add(joinedUserString);
          }
        });

        // Also include creator if not current user and not already buddy
        const creatorId = callout.created_by?.toString();

        if (
          creatorId &&
          creatorId !== userId.toString() &&
          !buddyIds.includes(creatorId)
        ) {
          suggestedUserIds.add(creatorId);
        }
      });

      console.log(suggestedUserIds, "Suggested User IDs");

      // Get users with their training goals populated
      const users = await this.userModel
        .find({
          _id: {
            $in: Array.from(suggestedUserIds).map(
              (id) => new Types.ObjectId(id),
            ),
          },
          deleted_at: null,
          status: 1,
          is_profile_visible: true, // Only suggest users with visible profiles
        })
        .populate("training_goals", `name.${safeLang}`)
        .select(
          "_id user_name first_name last_name profile_image training_experience training_time_week training_time_weekend training_goals home_gyms",
        )
        .sort({ user_name: 1 });

      // Get buddy requests to calculate buddy_status
      const buddyRequests = await this.buddyModel.find({
        deleted_at: null,
        $or: [
          { sender: userObjectId, receiver: { $in: users.map((u) => u._id) } },
          { sender: { $in: users.map((u) => u._id) }, receiver: userObjectId },
        ],
      });

      // Collect all primary gym IDs to fetch names in bulk
      const primaryGymIds = users
        .map((user: any) => user.home_gyms?.[0])
        .filter((id) => id)
        .map((id) => new Types.ObjectId(id));

      // Fetch primary gym names
      const gyms = await this.trainingHomeGymModel
        .find({ _id: { $in: primaryGymIds } })
        .select(`name.${safeLang} name.en`);

      const gymMap = new Map(gyms.map((g: any) => [g._id.toString(), g]));

      // Format users with buddy status and training goals with safeLang
      const formattedUsers = users.map((user: any) => {
        const userObj = user.toObject();

        // Find buddy request for this user
        const buddyRequest = buddyRequests.find((req: any) => {
          const senderId = req.sender?.toString();
          const receiverId = req.receiver?.toString();
          const userIdStr = user._id?.toString();
          return senderId === userIdStr || receiverId === userIdStr;
        });

        let buddyStatus = "request";
        if (buddyRequest) {
          if (buddyRequest.status === "accepted") {
            buddyStatus = "chat";
          } else if (buddyRequest.status === "pending") {
            const isReceiver =
              buddyRequest.receiver?.toString() === user._id?.toString();
            buddyStatus = isReceiver ? "review" : "pending";
          } else {
            buddyStatus = buddyRequest.status;
          }
        }

        // Check if primary gym (0 index from home_gyms)
        const homeGyms = userObj.home_gyms || [];
        const primaryGymId =
          homeGyms.length > 0 ? homeGyms[0].toString() : null;
        const primaryGym = primaryGymId ? gymMap.get(primaryGymId) : null;
        const primaryGymName = primaryGym
          ? primaryGym.name?.[safeLang] || primaryGym.name?.en || ""
          : "";

        // Format training goals with safeLang
        const formattedTrainingGoals =
          userObj.training_goals?.map((goal: any) => ({
            _id: goal._id,
            name: goal.name?.[safeLang] || goal.name?.en || "",
          })) || [];

        return {
          _id: userObj._id,
          user_name: userObj.user_name,
          name: `${userObj.first_name || ""} ${userObj.last_name || ""}`.trim(),
          profile_image: userObj.profile_image,
          training_experience: userObj.training_experience,
          training_time_week: userObj.training_time_week,
          training_time_weekend: userObj.training_time_weekend,
          training_goals: formattedTrainingGoals,
          buddy_status: buddyStatus,
          primary_gym_name: primaryGymName,
          total_gym_count: homeGyms.length,
        };
      });

      // Update user's suggestion_sent flag to false after sending suggestions
      if (users.length > 0) {
        await this.userModel.updateOne(
          { _id: userObjectId },
          { $set: { suggestion_sent: false } },
        );
        console.log("Reset suggestion_sent flag for user:", userId);
      }

      return this.resService.success(
        res,
        "Buddy suggestions fetched successfully",
        req,
        formattedUsers,
      );
    } catch (error) {
      console.log(error, "Error in getBuddySuggestionsFromCallouts");

      return this.resService.serverError(
        res,
        "Error fetching buddy suggestions",
        req,
      );
    }
  }

  async getMyJoinedCallout(
    userId: string,
    page = 1,
    limit = 10,
    req: Request,
    res: Response,
  ) {
    try {
      page = Number(page) || 1;
      limit = Number(limit) || 10;
      const skip = (page - 1) * limit;

      const filter = {
        joined_users: { $in: [new Types.ObjectId(userId)] },
        deleted_at: null,
      };

      const [items, total] = await Promise.all([
        this.trainingCalloutModel
          .find(filter)
          .populate("created_by", "user_name profile_image")
          .populate("gym_id", "gymName address")
          .sort({ createdAt: -1 })
          .skip(skip)
          .limit(limit)
          .lean(),

        this.trainingCalloutModel.countDocuments(filter),
      ]);

      return this.resService.success(
        res,
        "Buddy requests fetched successfully",
        req,
        {
          items,
          ...this.paginationMeta(total, page, limit),
        },
      );
    } catch (error) {
      console.log(error, "Error in getMyJoinedCalled");
      return this.resService.serverError(
        res,
        "Error fetching joined training call outs",
        req,
      );
    }
  }
}
