import { Types } from 'mongoose';
import QRCodeModel from '../../models/QrCode';
import MediaModel from '../../models/MediaSchema';
import LocationModel from '../../models/Location';
import QRCodeUploadsModel from '../../models/QrCodeUpload';
import { QRCodeHelper } from '../../utils/qrcodeHelper';
import { PinEncryption } from '../../utils/pinEncryption';
import { MedicalEncryption } from '../../utils/medicalEncryption';
import {
  CreateQRCodeRequest,
  UpdateQRCodeRequest,
  QRCodeResponse,
  QRCodeListResponse,
  QRCodeStatus,
  QRCodeType,
  QRCodeAccessType,
  MemoryTagResponse,
} from '../../types/qrcode.types';
import { insertMedia, updateMedia, replaceMediaArrayForQrCode, deleteMedia } from '../media/index.repositry';
import { NotificationUtils } from '../../utils/NotificationUtils';
import { canCreateQRCode, getStorageLimit } from '../../config/abilities';
import { SubscriptionTier, SUBSCRIPTION_TIERS } from '../../config/subscriptionTiers';
import * as QRCodeRepository from './QrCodeRepositry';
import { AuditLogger } from '../../utils/AuditLogger';
import AdminDocumentModel from '../../models/AdminDocument';
import UserModel from '../../models/User';
import ScanModel from '../../models/Scan';
import { ILocationModel } from '../../types';

export class QRCodeService {
  /**
   * Find all admin users
   */
  private static async findAdminUsers(): Promise<any[]> {
    try {
      const adminUsers = await UserModel.find({ role: 'ADMIN' })
        .select('_id email first_name last_name')
        .lean();
      return adminUsers;
    } catch (error) {
      console.error('Error finding admin users:', error);
      return [];
    }
  }

  /**
   * Find admin users with admin notifications enabled
   */
  private static async findAdminUsersWithNotificationsEnabled(): Promise<any[]> {
    try {
      const adminUsers = await UserModel.find({
        role: 'ADMIN',
        adminNotificationsEnabled: { $ne: false } // true or undefined (defaults to true)
      })
        .select('_id email first_name last_name adminNotificationsEnabled')
        .lean();
      return adminUsers;
    } catch (error) {
      console.error('Error finding admin users with notifications enabled:', error);
      return [];
    }
  }

  /**
   * Send notifications to admin users about medical documents
   */
  private static async notifyAdminUsersAboutMedicalDocuments(
    userId: string,
    qrcodeId: string,
    medicalDocuments: any[]
  ): Promise<void> {
    try {
      // Send notification to admin users with notifications enabled
      if (medicalDocuments.length > 0) {
        const adminUsers = await this.findAdminUsersWithNotificationsEnabled();

        if (adminUsers.length > 0) {
          const adminIds = adminUsers.map(admin => admin._id.toString());

          await NotificationUtils.createBulkNotifications(adminIds, {
            title: 'Medical Document Uploaded',
            message: `${medicalDocuments.length} medical document(s) uploaded by user for medical QR code`,
            type: 'DEFAULT',
            data: {
              qrcode_id: qrcodeId,
              user_id: userId,
              document_count: medicalDocuments.length,
              document_type: 'MEDICAL_QR',
              timestamp: new Date().toISOString()
            },
            sendPush: true
          });

          console.log(`Notified ${adminUsers.length} admin users about medical document upload`);
        } else {
          console.log('No admin users with notifications enabled found');
        }
      }
    } catch (error) {
      console.error('Error notifying admin users about medical documents:', error);
      // Don't throw error - QR code creation should still succeed
    }
  }

  /**
   * Check if user can create a new QR code based on their subscription tier
   * @param userId - User ID to check
   * @returns Object with permission status and limit information
   * @throws Error if user not found
   */
  public static async checkQRCodeCreationLimit(userId: string): Promise<{
    canCreate: boolean;
    limit: number;
    current: number;
    remaining: number;
    subscriptionTier: SubscriptionTier;
  }> {
    // Get user's subscription tier
    const user = await QRCodeRepository.findUserById(userId);

    if (!user) {
      throw new Error('User not found');
    }

    const subscriptionTier = (user.subscription_tier || 'free') as SubscriptionTier;

    // Count user's active QR codes (non-deleted)
    const currentQRCodeCount = await QRCodeRepository.countUserQrCodes(userId);

    // Check if user can create more QR codes
    const limitCheck = canCreateQRCode(subscriptionTier, currentQRCodeCount);

    return {
      ...limitCheck,
      subscriptionTier,
    };
  }

  /**
   * Calculate current storage usage for a user (in bytes)
   * Includes all media from QR codes (images and memory_tag.images)
   * @param userId - User ID to calculate storage for
   * @returns Total storage usage in bytes
   */
  public static async calculateUserStorageUsage(userId: string): Promise<number> {
    try {
      // Find all QR codes for the user (not deleted)
      const qrcodes = await QRCodeModel.find({
        created_by: new Types.ObjectId(userId),
        isDelete: false,
      }).select('images memory_tag.images').lean();

      // Collect all media IDs from QR codes
      const mediaIds: string[] = [];
      qrcodes.forEach(qr => {
        // Add images from main images array
        if (Array.isArray(qr.images)) {
          qr.images.forEach((imageId: any) => {
            const id = typeof imageId === 'object' ? imageId._id?.toString() : imageId?.toString();
            if (id) {
              mediaIds.push(id);
            }
          });
        }
        // Add images from memory_tag.images array
        if (qr.memory_tag?.images && Array.isArray(qr.memory_tag.images)) {
          qr.memory_tag.images.forEach((imageId: any) => {
            const id = typeof imageId === 'object' ? imageId._id?.toString() : imageId?.toString();
            if (id) {
              mediaIds.push(id);
            }
          });
        }
      });

      // If no media found, return 0
      if (mediaIds.length === 0) {
        return 0;
      }

      // Find all media documents and calculate total size
      const media = await MediaModel.find({ _id: { $in: mediaIds } }).select('size').lean();
      const totalSize = media.reduce((acc: number, file: any) => {
        return acc + (file.size || 0);
      }, 0);

      return totalSize;
    } catch (error) {
      console.error('Error calculating user storage usage:', error);
      throw new Error('Failed to calculate storage usage');
    }
  }

  /**
   * Check if user can create a new QR code based on storage limits
   * @param userId - User ID to check
   * @param newQRCodeSize - Size of new QR code images in bytes
   * @returns Object with permission status and storage limit information
   * @throws Error if user not found or tier not found
   */
  public static async checkStorageLimitForQRCodeCreation(
    userId: string,
    newQRCodeSize: number = 0
  ): Promise<{
    canCreate: boolean;
    limit: number;
    current: number;
    remaining: number;
    subscriptionTier: SubscriptionTier;
  }> {
    // Get user's subscription tier
    const user = await QRCodeRepository.findUserById(userId);

    if (!user) {
      throw new Error('User not found');
    }

    const subscriptionTier = (user.subscription_tier || 'free') as SubscriptionTier;

    // Check if tier exists in SUBSCRIPTION_TIERS
    if (!SUBSCRIPTION_TIERS[subscriptionTier]) {
      throw new Error(`Subscription tier '${subscriptionTier}' not found in configuration`);
    }

    // Get storage limit for the tier (in bytes)
    const limit = getStorageLimit(subscriptionTier);

    // Calculate current storage usage (in bytes)
    const current = await this.calculateUserStorageUsage(userId);

    // Calculate total storage after adding new QR code
    const totalAfterCreation = current + newQRCodeSize;

    // Check if user can create the QR code
    const canCreate = totalAfterCreation <= limit;

    // Calculate remaining storage
    const remaining = Math.max(0, limit - current);

    return {
      canCreate,
      limit,
      current,
      remaining,
      subscriptionTier,
    };
  }

  /**
   * Create a new QR code
   */
  public static async createQRCode(
    userId: string,
    data: CreateQRCodeRequest,
    baseUrl?: string
  ): Promise<QRCodeResponse> {
    try {
      console.log(data, 'Qr Code Data ');

      console.log('Starting QR code creation process...');

      // Validation for LINK type
      if (data.type === 'LINK' && (!data.type_details || !data.type_details.link)) {
        throw new Error('URL is required for LINK type QR code');
      }

      // Validation for WIFI type
      if (data.type === 'WIFI' && (!data.type_details || !data.type_details.ssid)) {
        throw new Error('SSID is required for WIFI type QR code');
      }

      // ---------- Memory Tag handling (unchanged) ----------
      let memoryTagData: any = null;
      if (data.type === QRCodeType.MEMORY_TAG && data.memory_tag) {
        memoryTagData = await QRCodeRepository.processMemoryTagData(data.memory_tag, baseUrl);
      }

      // ---------- Handle images for all QR code types ----------
      let imageIds: Types.ObjectId[] = [];
      if (data.images && Array.isArray(data.images)) {
        imageIds = await QRCodeRepository.processGeneralImages(data.images, baseUrl);

        //   // ---------- Add medical documents directly to AdminDocument during insertion ----------
        //   if (data?.type?.toLowerCase() === 'MEDICAL_QR'?.toLowerCase()) {
        //     console.log(`Processing medical documents during media insertion for Medical QR Code`);

        //     for (const image of data.images) {
        //       // Check if the file is a document (.docx or .pdf)
        //       const originalName = image.originalName || '';
        //       const mimeType = image.mimeType || '';

        //       const isDocument = 
        //         originalName.toLowerCase().endsWith('.docx') ||
        //         originalName.toLowerCase().endsWith('.pdf') ||
        //         mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
        //         mimeType === 'application/pdf';

        //       if (isDocument && image._id) {
        //         try {
        //           // Add directly to AdminDocument collection
        //           const adminDoc = new AdminDocumentModel({
        //             media_id: image._id,
        //             userId: new Types.ObjectId(userId),
        //             downloads: 0
        //           });
        //           await adminDoc.save();

        //           console.log(`Medical document saved to AdminDocument: ${originalName} (ID: ${adminDoc._id})`);
        //         } catch (error) {
        //           console.error(`Error saving medical document to AdminDocument: ${originalName}`, error);
        //         }
        //       }
        //     }
        //   }
        // }

      }


      // ---------- Handle location for all QR code types ----------
      let locationId: Types.ObjectId | null = null;
      if (data.location && data.location.latitude && data.location.longitude) {
        const savedLocation = await (LocationModel as unknown as ILocationModel).createWithGeocode(
          data.location.latitude,
          data.location.longitude
        );
        locationId = savedLocation._id as Types.ObjectId;
      }

      // ---------- Handle type-specific details ----------
      let typeDetailsData: any = null;
      if (data.type_details) {
        // For medical QR codes, encryption will be handled by the pre-save middleware
        typeDetailsData = data.type_details;
      }

      // ---------- Assemble qrcodeData ----------
      const qrcodeData: any = {
        ...data,
      };

      // Remove sub-objects already handled separately so we don't save duplicates
      delete qrcodeData.memory_tag;
      delete qrcodeData.images;
      delete qrcodeData.location;

      // Add processed fields
      if (imageIds.length > 0) {
        qrcodeData.images = imageIds;
      }
      if (locationId) {
        qrcodeData.location = locationId;
      }
      if (typeDetailsData) {
        qrcodeData.type_details = typeDetailsData;
      }

      // Ensure WIFI QR codes do not expire
      if (qrcodeData.type === 'WIFI') {
        qrcodeData.expired_at = null;
      }

      // ---------- Save PIN and PIN hash if provided ----------
      if (data.pin && data.pin.trim() !== '') {
        // Save original PIN to pin field
        qrcodeData.pin = data.pin;
        // Save hash to pinHash field
        qrcodeData.pinHash = await PinEncryption.hashPin(data.pin);
      }

      if (qrcodeData?.item_description == undefined) {
        qrcodeData.item_description = "No Description for this Qrcode"
      }

      // Debug logging
      console.log('QR Code Data being saved:', JSON.stringify(qrcodeData, null, 2));
      console.log('Privacy Controls:', qrcodeData.privacy_controls);

      // Create and save the QRCode document (include memory_tag only when present)
      const savedQRCode = await QRCodeRepository.createQrCode({
        ...qrcodeData,
        created_by: new Types.ObjectId(userId),
        ...(memoryTagData && { memory_tag: memoryTagData }),
      });
      console.log('Saved QR Code:', JSON.stringify(savedQRCode, null, 2));






      // ---------- Handle medical documents for MEDICAL_QR type ----------
      let medicalDocuments: any[] = [];
      if (data?.type === 'MEDICAL_QR') {

        console.log(`Medical QR Code Data: ${JSON.stringify(data, null, 2)}`);

        // Count medical documents for notification (already saved during media insertion)
        if (data.images && Array.isArray(data.images) && Array.isArray(imageIds)) {
          for (const image of data.images) {
            // Check if the file is a document (.docx or .pdf)
            const originalName = image.originalName || '';
            const mimeType = image.mimeType || '';

            const isDocument =
              originalName.toLowerCase().endsWith('.docx') ||
              originalName.toLowerCase().endsWith('.pdf') ||
              mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
              mimeType === 'application/pdf';


            console.log(`=========== IS DOCUMENT ==================`)
            console.log(isDocument)
            console.log(`=========== IS DOCUMENT ==================`)





            if (isDocument) {



              const document = new AdminDocumentModel({
                media_id: [imageIds],
                userId: userId,
                qrcode_id: savedQRCode._id
              })

              await document.save()


              // medicalDocuments.push({
              //   media_id: image._id,
              //   document_type: originalName.toLowerCase().endsWith('.pdf') ? 'medical_report_pdf' : 'medical_report_docx',
              //   document_name: originalName || 'Medical Document'
              // });
            }
          }
        }
      }




      console.log(`================ DATAS ======================`)
      console.log(data?.type)
      console.log(`================ DATAS ======================`)


      // ---------- Notify admin users about medical documents ----------
      if (savedQRCode?.type?.toLowerCase() === 'MEDICAL_QR'?.toLowerCase()) {
        console.log(`Notifying admins about ${medicalDocuments.length} medical documents for QR code: ${savedQRCode._id}`);
        await this.notifyAdminUsersAboutMedicalDocuments(
          userId,
          savedQRCode._id.toString(),
          medicalDocuments
        );
        console.log(`Admin notifications sent for medical documents in QR code: ${savedQRCode._id}`);
      }

      // Populate created_by, memory_tag, images, and location relations
      await savedQRCode.populate([
        {
          path: 'created_by',
          select: 'first_name last_name email profile_picture',
          populate: {
            path: 'media_id',
            select: 'filePath originalName mimeType',
          },
        },
        {
          path: 'memory_tag.location',
          select:
            'latitude longitude City address country state postalCode street createdAt updatedAt',
        },
        {
          path: 'memory_tag.images',
          select: 'originalName filePath mimeType size',
        },
        {
          path: 'images',
          select: 'originalName filePath mimeType size',
        },
        {
          path: 'location',
          select:
            'latitude longitude City address country state postalCode street createdAt updatedAt',
        },
      ]);

      // ---------- QR generation and media linking (unchanged) ----------
      const qrContent = QRCodeHelper.generateQRCodeContent(
        savedQRCode._id.toString(),
        savedQRCode.type || 'MEMORY_TAG',
        data.title,
        savedQRCode.type_details
      );

      const filename = QRCodeHelper.generateFilename(data.title, userId);
      const { filePath, size } = await QRCodeHelper.generateQRCode(
        qrContent,
        filename,
        savedQRCode.type || 'MEMORY_TAG',
        {
          color: data.color,
          background_color: data.background_color,
          size: data.size,
        }
      );

      // Save media record
      const savedMedia = await insertMedia({
        originalName: `${filename}.png`,
        filePath,
        mimeType: 'image/png',
        size,
      }, baseUrl);

      // Link QR code with media
      await QRCodeRepository.createQrCodeUpload(savedQRCode._id, savedMedia._id);

      // Create notification for successful QR code creation
      try {
        await NotificationUtils.createNotification({
          title: 'QR Code Created Successfully',
          message: `Your QR code "${data.title}" has been created and is ready to use.`,
          type: 'DEFAULT',
          user_id: userId,
          qrcode_id: savedQRCode._id.toString(),
          link: `/dashboard/qrcode/scan/${savedQRCode._id}`,
          data: {
            qr_type: savedQRCode.type,
            qr_title: data.title,
            creation_timestamp: new Date().toISOString(),
          },
          sendPush: true,
        });
      } catch (notificationError) {
        console.error(
          'Error creating QR code creation notification:',
          notificationError
        );
        // Don't throw error, QR code creation should still succeed
      }

      // Send push notification specifically for memory tag creation
      if (data.type?.toLowerCase() === 'memory_tag'?.toLowerCase()) {
        try {
          await NotificationUtils.createNotification({
            title: 'Memory Tag Created',
            message: 'your Memory Tag has been created',
            type: 'DEFAULT',
            user_id: userId,
            qrcode_id: savedQRCode._id.toString(),
            link: `/dashboard/qrcode/scan/${savedQRCode._id}`,
            data: {
              qr_type: savedQRCode.type,
              qr_title: data.title,
              creation_timestamp: new Date().toISOString(),
            },
            sendPush: true,
          });
        } catch (memoryTagNotificationError) {
          console.error(
            'Error creating memory tag creation notification:',
            memoryTagNotificationError
          );
          // Don't throw error, QR code creation should still succeed
        }
      }

      // Log QR code creation
      await AuditLogger.logQRCodeAction({
        action: 'QRCODE_CREATE',
        user_id: userId,
        qrcode_id: savedQRCode._id.toString(),
        description: `QR code created: ${data.title} (${savedQRCode.type})`,
        status: 'SUCCESS',
        severity: 'LOW',
        metadata: {
          qr_type: savedQRCode.type,
          access_type: savedQRCode.access_type,
          location_enabled: savedQRCode.location_enabled,
          has_pin: !!data.pin,
          image_count: imageIds.length,
        },
      });

      // Remove leading slash from filePath
      const qrCodeUrl = filePath.replace(/^\/+/, '');
      return this.formatQRCodeResponse(savedQRCode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error creating QR code:', error);
      console.error(
        'Error stack:',
        error instanceof Error ? error.stack : 'No stack trace'
      );

      // Log failed QR code creation
      await AuditLogger.logQRCodeAction({
        action: 'QRCODE_CREATE',
        user_id: userId,
        description: `Failed to create QR code: ${data.title} - ${error instanceof Error ? error.message : 'Unknown error'}`,
        status: 'FAILURE',
        severity: 'MEDIUM',
        metadata: {
          qr_type: data.type,
          error_message: error instanceof Error ? error.message : 'Unknown error',
        },
      });

      throw error instanceof Error ? error : new Error('Failed to create QR code');
    }
  }

  /**
   * Get QR codes for a user with pagination and filters
   */
  public static async getQRCodes(
    userId: string,
    page: number = 1,
    limit: number = 10,
    search: string = ''
  ): Promise<QRCodeListResponse> {
    try {
      const skip = (page - 1) * limit;

      // Build match query
      const matchQuery: any = {
        created_by: new Types.ObjectId(userId),
        isDelete: false,
        type: { $ne: 'MEMORY_TAG' },
      };

      if (search && search?.trim()?.toUpperCase() !== '') {
        matchQuery.$or = [
          { item_name: { $regex: search, $options: 'i' } },
          { type: { $regex: search, $options: 'i' } },
        ];
      }

      // Get total count
      const total = await QRCodeRepository.countQrCodesWithQuery(matchQuery);

      // Use aggregation to fetch QR codes with their uploaded images
      const qrcodes = await QRCodeRepository.getQrCodesWithAggregation(
        matchQuery,
        skip,
        limit
      );


      console.log(`=======================================`)
      console.log(qrcodes, 'QRCodes');
      console.log(`=======================================`)

      // Get all QR code IDs
      const qrcodeIds = qrcodes.map(qr => qr._id);

      // Get scan counts for each QR code
      const scanCounts = await QRCodeRepository.getScanCountsByQrCodeIds(qrcodeIds);

      // Convert scan counts to a map for easy lookup
      const scanCountMap = new Map();
      scanCounts.forEach(item => {
        scanCountMap.set(item._id.toString(), item.scanCount);
      });

      // Format QR codes with scan count
      const formattedQRCodes = qrcodes.map(qrcode => {
        const scanCount = scanCountMap.get(qrcode._id.toString()) || 0;
        const formattedQR = this.formatQRCodeResponse(qrcode as any, qrcode.qr_code_url);

        // Add scan count to the response
        return {
          ...formattedQR,
          scanCount
        };
      });

      return {
        qrcodes: formattedQRCodes,
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      };
    } catch (error) {
      console.error('Error getting QR codes:', error);
      throw new Error('Failed to get QR codes');
    }
  }

  /**
   * Get a single QR code by ID
   */
  public static async getQRCodeById(
    qrcodeId: string,
    userId?: string
  ): Promise<QRCodeResponse | null> {
    try {
      const qrcode = await QRCodeRepository.findQrCodeByIdWithPopulate(
        qrcodeId,
        userId
      );

      if (!qrcode) return null;

      // Get QR code image
      const upload = await QRCodeRepository.findQrCodeUpload(qrcode._id);

      const media = upload?.media_id;
      const qrCodeUrl = media?.filePath ? media.filePath.replace(/^\/+/, '') : undefined;

      return this.formatQRCodeResponse(qrcode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error getting QR code:', error);
      throw new Error('Failed to get QR code');
    }
  }





  /**
   * Update a QR code
   */
  public static async updateQRCode(
    qrcodeId: string,
    userId: string,
    data: UpdateQRCodeRequest,
    baseUrl?: string
  ): Promise<QRCodeResponse | null> {
    try {


      // First, get existing QR code to access current images (for both general and memory tag images)
      const existingQRCode = await QRCodeModel.findById(qrcodeId);

      // Prepare update data
      let updateData: any = { ...data };

      // Handle general image uploads if present
      let imageUpdateOperation: any = null;
      if (data.images && Array.isArray(data.images) && data.images.length > 0) {
        const imageUpdateResult = await QRCodeRepository.processUpdateGeneralImages(existingQRCode, data.images, baseUrl);
        if (imageUpdateResult) {
          imageUpdateOperation = imageUpdateResult;
        }
      }

      // Remove images from the update data since we're handling it separately
      delete updateData.images;
      if (updateData.memory_tag) {
        delete updateData.memory_tag.images;
      }

      // ---------- Handle location for all QR code types ----------
      updateData = await QRCodeRepository.processLocationForUpdate(
        updateData,
        data.location,
        data.location_enabled
      );

      // ---------- Save PIN and PIN hash if provided ----------
      if (data?.pin && data.pin.trim() !== '') {
        // Save original PIN to pin field
        updateData.pin = data.pin;
        // Save hash to pinHash field
        updateData.pinHash = await PinEncryption.hashPin(data.pin);
      }

      // Ensure WIFI QR codes do not expire
      if (updateData.type === 'WIFI' || existingQRCode?.type === 'WIFI') {
        updateData.expired_at = null;
      }

      // ---------- Handle medical type_details encryption ----------
      // For medical QR codes, encryption will be handled by the pre-update middleware
      // No special handling needed here as the middleware will take care of it

      // Handle Memory Tag image uploads if present
      if (
        data.type === QRCodeType.MEMORY_TAG &&
        data.memory_tag?.images &&
        Array.isArray(data.memory_tag.images) &&
        data.memory_tag.images.length > 0
      ) {
        const memoryTagImageUpdateResult = await QRCodeRepository.processUpdateMemoryTagImages(
          existingQRCode,
          data.memory_tag.images,
          baseUrl
        );

        if (memoryTagImageUpdateResult) {
          if (imageUpdateOperation) {
            // Merge the memory tag images update with the general image update
            imageUpdateOperation = { ...imageUpdateOperation, ...memoryTagImageUpdateResult };
          } else {
            imageUpdateOperation = memoryTagImageUpdateResult;
          }
        }
      }

      // Remove images from the update data since we're handling it separately
      if (updateData.memory_tag) {
        delete updateData.memory_tag.images;
      }

      // Update QR code with all related data and regenerate QR code image
      const populatedQRCode = await QRCodeRepository.updateQrCodeWithImagesAndRegenerate(
        qrcodeId,
        userId,
        updateData,
        imageUpdateOperation
      );

      if (!populatedQRCode) return null;

      // Generate new QR code with updated content
      const qrContent = QRCodeHelper.generateQRCodeContent(
        populatedQRCode._id.toString(),
        populatedQRCode.type || 'MEMORY_TAG',
        populatedQRCode.title,
        populatedQRCode.type_details
      );

      // Get the current QR code upload to access the old file path
      const currentUpload = await QRCodeRepository.findQrCodeUpload(populatedQRCode._id);
      const oldMedia = currentUpload?.media_id as any;

      // Generate new QR code file
      const filename = QRCodeHelper.generateFilename(populatedQRCode.title, userId);
      const { filePath, size } = await QRCodeHelper.generateQRCode(
        qrContent,
        filename,
        populatedQRCode.type || 'MEMORY_TAG',
        {
          color: populatedQRCode.color,
          background_color: populatedQRCode.background_color,
          size: populatedQRCode.size,
        }
      );

      // Delete the old QR code file if it exists
      if (oldMedia && oldMedia.filePath) {
        await QRCodeHelper.deleteQRCodeFile(oldMedia.filePath);
      }

      // Save new media record for the updated QR code
      const savedMedia = await insertMedia({
        originalName: `${filename}.png`,
        filePath,
        mimeType: 'image/png',
        size,
      }, baseUrl);

      // Update the QR code upload record with the new media
      if (currentUpload) {
        // Update existing upload record
        await QRCodeUploadsModel.findByIdAndUpdate(currentUpload._id, {
          media_id: savedMedia._id
        });
      } else {
        // Create new upload record if one doesn't exist
        await QRCodeRepository.createQrCodeUpload(populatedQRCode._id, savedMedia._id);
      }

      // Get updated QR code image
      const updatedUpload = await QRCodeRepository.findQrCodeUpload(populatedQRCode._id);
      const updatedMedia = updatedUpload?.media_id as any;
      const qrCodeUrl = updatedMedia?.filePath ? updatedMedia.filePath.replace(/^\/+/, '') : undefined;

      // Log QR code update
      await AuditLogger.logQRCodeAction({
        action: 'QRCODE_UPDATE',
        user_id: userId,
        qrcode_id: qrcodeId,
        description: `QR code updated: ${populatedQRCode.title}`,
        status: 'SUCCESS',
        severity: 'LOW',
        metadata: {
          updated_fields: Object.keys(data),
          has_new_images: !!(data.images && data.images.length > 0),
        },
      });

      return this.formatQRCodeResponse(populatedQRCode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error updating QR code:', error);
      throw new Error('Failed to update QR code');
    }
  }

  /**
   * Delete a QR code
   */
  public static async deleteQRCode(qrcodeId: string, userId: string): Promise<boolean> {
    try {
      // Get QR code details before deletion for logging
      const qrcode = await QRCodeRepository.findQrCodeById(qrcodeId);

      // Soft delete QR code by setting isDelete to true
      const result = await QRCodeRepository.softDeleteQrCode(qrcodeId, userId);

      if (result && qrcode) {
        // Log QR code deletion
        await AuditLogger.logQRCodeAction({
          action: 'QRCODE_DELETE',
          user_id: userId,
          qrcode_id: qrcodeId,
          description: `QR code deleted: ${qrcode.title} (${qrcode.type})`,
          status: 'SUCCESS',
          severity: 'MEDIUM',
          metadata: {
            qr_type: qrcode.type,
            was_active: qrcode.status === 'ACTIVE',
          },
        });
      }

      return !!result;
    } catch (error) {
      console.error('Error deleting QR code:', error);
      throw new Error('Failed to delete QR code');
    }
  }

  /**
   * Upload images to Memory Tag QR code
   */
  public static async uploadMemoryTagImages(
    qrcodeId: string,
    userId: string,
    imageFiles: Array<{
      originalName: string;
      filePath: string;
      mimeType: string;
      size: number;
    }>,
    baseUrl?: string
  ): Promise<QRCodeResponse | null> {
    try {
      // Find QR code and verify it's a Memory Tag type
      const qrcode = await QRCodeRepository.findMemoryTagQrCode(qrcodeId, userId);

      if (!qrcode) {
        throw new Error('Memory Tag QR code not found');
      }

      // Create media records for uploaded images
      const mediaIds: Types.ObjectId[] = [];
      for (const file of imageFiles) {
        const savedMedia = await insertMedia({
          originalName: file.originalName,
          filePath: file.filePath,
          mimeType: file.mimeType,
          size: file.size,
        }, baseUrl);
        mediaIds.push(savedMedia._id);
      }

      // Update QR code with new images
      const updatedQRCode = await QRCodeRepository.pushMemoryTagImages(
        qrcode._id,
        mediaIds
      );

      if (!updatedQRCode) return null;

      // Get QR code image
      const upload = await QRCodeRepository.findQrCodeUpload(updatedQRCode._id);

      const media = upload?.media_id as any;
      const qrCodeUrl = media?.filePath ? media.filePath.replace(/^\/+/, '') : undefined;

      // Log memory tag image upload
      await AuditLogger.logMemoryTagAction({
        action: 'MEMORY_TAG_UPDATE',
        user_id: userId,
        memory_tag_id: qrcodeId,
        description: `Uploaded ${imageFiles.length} image(s) to Memory Tag: ${qrcode.title}`,
        status: 'SUCCESS',
        severity: 'LOW',
        metadata: {
          image_count: imageFiles.length,
        },
      });

      return this.formatQRCodeResponse(updatedQRCode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error uploading Memory Tag images:', error);
      throw new Error('Failed to upload Memory Tag images');
    }
  }

  /**
   * Update Memory Tag location
   */
  public static async updateMemoryTagLocation(
    qrcodeId: string,
    userId: string,
    locationData: {
      latitude: number;
      longitude: number;
      location_enabled?: boolean;
    }
  ): Promise<QRCodeResponse | null> {
    try {
      // Find QR code and verify it's a Memory Tag type
      const qrcode = await QRCodeRepository.findMemoryTagQrCode(qrcodeId, userId);

      if (!qrcode) {
        throw new Error('Memory Tag QR code not found');
      }

      let locationId = null;

      // Create or update location if location tracking is enabled
      if (locationData.location_enabled !== false) {
        if (qrcode.memory_tag?.location) {
          // Update existing location
          await LocationModel.findByIdAndUpdate(qrcode.memory_tag.location, {
            latitude: locationData.latitude,
            longitude: locationData.longitude,
          });
          locationId = qrcode.memory_tag.location;
        } else {
          // Create new location
          const location = new LocationModel({
            latitude: locationData.latitude,
            longitude: locationData.longitude,
          });
          const savedLocation = await location.save();
          locationId = savedLocation._id;
        }
      }

      // Update QR code with location data
      const updateData: any = {
        'memory_tag.location_enabled': locationData.location_enabled !== false,
      };

      if (locationId) {
        updateData['memory_tag.location'] = locationId;
      }

      const updatedQRCode = await QRCodeRepository.updateMemoryTagLocation(
        qrcode._id,
        updateData
      );

      if (!updatedQRCode) return null;

      // Get QR code image
      const upload = await QRCodeRepository.findQrCodeUpload(updatedQRCode._id);

      const media = upload?.media_id as any;
      const qrCodeUrl = media?.filePath ? `/${media.filePath.replace(/^\/+/, '')}` : undefined;

      return this.formatQRCodeResponse(updatedQRCode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error updating Memory Tag location:', error);
      throw new Error('Failed to update Memory Tag location');
    }
  }

  /**
   * Remove image from Memory Tag QR code
   */
  public static async removeMemoryTagImage(
    qrcodeId: string,
    userId: string,
    imageId: string
  ): Promise<QRCodeResponse | null> {
    try {
      // Find QR code and verify it's a Memory Tag type
      const qrcode = await QRCodeRepository.findMemoryTagQrCode(qrcodeId, userId);

      if (!qrcode) {
        throw new Error('Memory Tag QR code not found');
      }

      // Remove image from QR code
      const updatedQRCode = await QRCodeRepository.pullMemoryTagImage(
        qrcode._id,
        imageId
      );

      // Delete the media record and file from filesystem
      await deleteMedia(imageId);

      if (!updatedQRCode) return null;

      // Get QR code image
      const upload = await QRCodeRepository.findQrCodeUpload(updatedQRCode._id);

      const media = upload?.media_id as any;
      const qrCodeUrl = media?.filePath ? `/${media.filePath.replace(/^\/+/, '')}` : undefined;

      return this.formatQRCodeResponse(updatedQRCode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error removing Memory Tag image:', error);
      throw new Error('Failed to remove Memory Tag image');
    }
  }

  /**
   * Toggle QR code status (activate/deactivate)
   */
  public static async toggleQRCodeStatus(
    qrcodeId: string,
    userId: string
  ): Promise<QRCodeResponse | null> {
    try {
      const qrcode = await QRCodeRepository.findQrCodeByIdWithPopulate(
        qrcodeId,
        userId
      );

      if (!qrcode) return null;

      const newStatus =
        qrcode.status === QRCodeStatus.ACTIVE
          ? QRCodeStatus.MISSING
          : QRCodeStatus.ACTIVE;

      const updatedQRCode = await QRCodeRepository.updateQrCodeStatus(
        qrcode._id,
        newStatus
      );

      if (!updatedQRCode) return null;

      // Get QR code image
      const upload = await QRCodeRepository.findQrCodeUpload(updatedQRCode._id);

      const media = upload?.media_id as any;
      const qrCodeUrl = media?.filePath ? media.filePath.replace(/^\/+/, '') : undefined;

      // Log status toggle
      await AuditLogger.logQRCodeAction({
        action: 'QRCODE_STATUS_CHANGE',
        user_id: userId,
        qrcode_id: qrcodeId,
        description: `QR code status changed: ${updatedQRCode.title} - ${qrcode.status} → ${newStatus}`,
        status: 'SUCCESS',
        severity: 'MEDIUM',
        metadata: {
          old_status: qrcode.status,
          new_status: newStatus,
          qr_type: updatedQRCode.type,
        },
      });

      return this.formatQRCodeResponse(updatedQRCode as any, qrCodeUrl);
    } catch (error) {
      console.error('Error toggling QR code status:', error);
      throw new Error('Failed to toggle QR code status');
    }
  }

  /**
   * Verify PIN for private QR code access
   */
  public static async verifyQRCodePin(qrcodeId: string, pin: string): Promise<boolean> {
    try {
      const qrcode = await QRCodeRepository.findQrCodeById(qrcodeId);

      if (!qrcode) {
        throw new Error('QR code not found');
      }

      // if (qrcode.access_type !== 'PRIVATE') {
      //   throw new Error('QR code is not private');
      // }

      if (!qrcode.pinHash) {
        throw new Error('No PIN set for this QR code');
      }

      const isValid = await PinEncryption.verifyPinHash(pin, qrcode.pinHash);

      // Log PIN verification attempt
      await AuditLogger.logScanAction({
        action: isValid ? 'QRCODE_PIN_VERIFY' : 'QRCODE_PIN_VERIFY_FAILED',
        qrcode_id: qrcodeId,
        description: `PIN verification ${isValid ? 'successful' : 'failed'} for QR code: ${qrcode.title}`,
        status: isValid ? 'SUCCESS' : 'FAILURE',
        severity: isValid ? 'LOW' : 'MEDIUM',
        metadata: {
          qr_type: qrcode.type,
          access_type: qrcode.access_type,
        },
      });

      return isValid;
    } catch (error) {
      console.error('Error verifying QR code PIN:', error);
      return false;
    }
  }

  /**
   * Decrypt medical QR code type_details if needed
   */
  private static decryptMedicalData(qrcode: any): any {
    if (
      qrcode.type === 'MEDICAL_QR' &&
      qrcode.type_details &&
      typeof qrcode.type_details === 'string' &&
      MedicalEncryption.isEncrypted(qrcode.type_details)
    ) {
      try {
        return MedicalEncryption.decryptMedicalData(qrcode.type_details);
      } catch (error) {
        console.error('Error decrypting medical data:', error);
        // Return null or empty object if decryption fails
        return null;
      }
    }
    return qrcode.type_details;
  }

  /**
   * Format QR code response
   */
  private static formatQRCodeResponse(qrcode: any, qrCodeUrl?: string): QRCodeResponse {
    // Format created_by user data
    const createdBy = qrcode.created_by;
    const formattedCreatedBy =
      typeof createdBy === 'object' && createdBy !== null
        ? {
          _id: createdBy._id?.toString() || createdBy.toString(),
          first_name: createdBy.first_name,
          last_name: createdBy.last_name,
          email: createdBy.email,
          profile_picture: createdBy.profile_picture,
          profile_picture_url: createdBy.media_id?.filePath
            ? `/${createdBy.media_id.filePath.replace(/^.*[\/\\]uploads[\/\\]/, '').replace(/^\/+/, '')}`
            : null,
        }
        : createdBy?.toString() || createdBy;

    // Format Memory Tag data if it exists
    let memoryTagResponse: MemoryTagResponse | undefined;
    if (qrcode.type === QRCodeType.MEMORY_TAG && qrcode.memory_tag) {
      memoryTagResponse = {
        location_enabled: qrcode.memory_tag.location_enabled || false,
        images: (qrcode.memory_tag.images || []).map((image: any) => ({
          _id: image._id?.toString() || image.toString(),
          originalName: image.originalName,
          filePath: image.filePath,
          mimeType: image.mimeType,
          size: image.size,
          url: image.filePath ? `/${image.filePath.replace(/^.*[\/\\]uploads[\/\\]/, '').replace(/^\/+/, '')}` : undefined,
        })),
      };

      // Add location data if it exists
      if (qrcode.memory_tag.location) {
        const location = qrcode.memory_tag.location;
        memoryTagResponse.location = {
          _id: location._id?.toString() || location.toString(),
          latitude: location.latitude,
          longitude: location.longitude,
          City: location.City,
          address: location.address,
          country: location.country,
          state: location.state,
          postalCode: location.postalCode,
          street: location.street,
          createdAt: location.createdAt,
          updatedAt: location.updatedAt,
        };
      }
    }

    // Format images data if it exists
    let imagesResponse: any[] = [];
    if (qrcode.images && Array.isArray(qrcode.images)) {
      imagesResponse = qrcode.images.map((image: any) => ({
        _id: image._id?.toString() || image.toString(),
        originalName: image.originalName,
        filePath: image.filePath,
        mimeType: image.mimeType,
        size: image.size,
        url: image.filePath ? `/${image.filePath.replace(/^.*[\/\\]uploads[\/\\]/, '').replace(/^\/+/, '')}` : undefined,
      }));
    }

    // For MEMORY_TAG type, also include memory_tag images in the top-level images array
    if (qrcode.type === QRCodeType.MEMORY_TAG && memoryTagResponse && memoryTagResponse.images) {
      // Create a set of existing image IDs to avoid duplicates
      const existingImageIds = new Set(imagesResponse.map(img => img._id));

      // Add memory_tag images to imagesResponse (avoid duplicates)
      memoryTagResponse.images.forEach((image: any) => {
        if (!existingImageIds.has(image._id)) {
          imagesResponse.push(image);
          existingImageIds.add(image._id);
        }
      });
    }

    // Format location data if it exists
    let locationResponse;
    if (qrcode.location) {
      const location = qrcode.location;
      locationResponse = {
        _id: location._id?.toString() || location.toString(),
        latitude: location.latitude,
        longitude: location.longitude,
        City: location.City,
        address: location.address,
        country: location.country,
        state: location.state,
        postalCode: location.postalCode,
        street: location.street,
        createdAt: location.createdAt,
        updatedAt: location.updatedAt,
      };
    }

    // Determine if password protection is needed for medical QR codes with PRIVATE access
    const passwordProtected =
      qrcode.type === QRCodeType.MEDICAL_QR &&
      qrcode.access_type === QRCodeAccessType.PRIVATE;

    // Decrypt medical data if needed
    const decryptedTypeDetails = this.decryptMedicalData(qrcode);

    // PIN is now stored as plain text in pin field, no decryption needed


    return {
      _id: qrcode._id.toString(),
      title: qrcode?.title,
      type: qrcode?.type,
      // Standardized fields for all QR code types
      item_name: qrcode?.item_name,
      item_description: qrcode.item_description,
      images: imagesResponse,
      // Location support for all QR code types
      location: locationResponse,
      location_enabled: qrcode.location_enabled,
      // Legacy fields (kept for backward compatibility)
      name: qrcode.name,
      description: qrcode.description,
      lastKnownLocation: qrcode.lastKnownLocation,
      notes: qrcode.notes,
      memory_tag: memoryTagResponse,
      type_details: decryptedTypeDetails,
      expired_at: qrcode.expired_at,
      status: qrcode.status,
      created_by: formattedCreatedBy,
      access_type: qrcode.access_type,
      pin: qrcode.pin,
      color: qrcode.color,
      background_color: qrcode.background_color,
      size: qrcode.size,
      privacy_controls: qrcode.privacy_controls,
      qr_code_url: qrCodeUrl,
      created_at: qrcode.created_at,
      updated_at: qrcode.updated_at,
      ...(passwordProtected && { password_protected: true }),
    };
  }
}
