import { Injectable, Logger } from '@nestjs/common';
import { SeekerAddressService } from '../seeker_address/seeker_address.service';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from '../user/entities/user.entity';
import { JobPosts } from '../job_posts/entities/job_posts.entity';
import { JobApplications } from '../job_applications/entities/job_applications.entity';
import { JobAlert } from '../job_alerts/entities/job_alerts.entity';
import { Messages } from '../messages/entities/messages.entity';
import { SavedCandidate } from '../saved_candidates/entities/saved_candidates.entity';
import { Education } from '../education/entities/education.entity';
import { Wallet } from '../wallet/entities/wallet.entity';
import { JobSaved } from '../job_saved/entities/job_saved.entity';
import { SeekerProfiles } from '../seeker_profiles/entities/seeker_profiles.entity';
import { SeekerResume } from '../seeker_resumes/entities/seeker_resumes.entity';
import { SeekerExperience } from '../seeker_experience/entities/seeker_experience.entity';
import { SeekerEducation } from '../seeker_education/entities/seeker_education.entity';
import { SocialPlatform } from '../social_platform/entities/social_platform.entity';
import { SeekerSocialLinks } from '../seeker_social_links/entities/seeker_social_links.entity';
import { DataSource } from 'typeorm';
import { SeekerAddress } from '../seeker_address/entities/seeker_address.entity';
import { SeekerSkill } from '../seeker_skills/entities/seeker_skills.entity';
import { SeekerLanguage } from '../seeker_language/entities/seeker_language.entity';
import { Companies } from '../companies/entities/companies.entity';
import { CompanySocialLink } from '../company_social_links/entities/company_social_links.entity';
import { Franchises } from '../franchises/entities/franchises.entity';
import { OtpRequests } from '../otp_requests/entities/otp_requests.entity';
import { ImageProcessorService } from '../../utility/image-processor.service';
import * as fs from 'fs';
import * as path from 'path';
import { Associates } from '../associates/entities/associates.entity';
import { CompanyKeyPeople } from '../companies/entities/company-key-people.entity';
import { ActivityLogsService } from '../activity_logs/activity_logs.service';
import { AiInterviewSchedulesService } from '../ai_interview_schedules/ai_interview_schedules.service';
import { JobPostAiSettings } from '../job_post_ai_settings/entities/job_post_ai_settings.entity';
import { Roles } from '../roles/entities/roles.entity';
import { SeekerRoleExperience } from '../seeker_role_experience/entities/seeker_role_experience.entity';
import { WhatsAppNotificationService } from '../notifications/whatsapp-notification.service';
import { EmailService } from '../email/email.service';
import { UserNotificationsService } from '../user_notifications/user_notifications.service';

@Injectable()
export class DashboardService {
  constructor(
    @InjectRepository(SavedCandidate)
    private savedCandidateRepository: Repository<SavedCandidate>,
    @InjectRepository(Messages)
    private messageRepository: Repository<Messages>,
    @InjectRepository(JobAlert)
    private jobAlertRepository: Repository<JobAlert>,
    @InjectRepository(JobApplications)
    private applicationRepository: Repository<JobApplications>,
    @InjectRepository(JobPosts)
    private jobRepository: Repository<JobPosts>,
    @InjectRepository(UserEntity)
    private userRepository: Repository<UserEntity>,
    @InjectRepository(Education)
    private educationLevelRepository: Repository<Education>,
    @InjectRepository(Wallet)
    private walletRepository: Repository<Wallet>,
    @InjectRepository(JobSaved)
    private jobSavedRepository: Repository<JobSaved>,
    @InjectRepository(SeekerProfiles)
    private seekerProfileRepository: Repository<SeekerProfiles>,
    @InjectRepository(SeekerResume)
    private seekerResumeRepository: Repository<SeekerResume>,
    @InjectRepository(SeekerExperience)
    private seekerExperienceRepository: Repository<SeekerExperience>,
    @InjectRepository(SeekerEducation)
    private seekerEducationRepository: Repository<SeekerEducation>,
    @InjectRepository(SocialPlatform)
    private socialPlatformRepository: Repository<SocialPlatform>,
    @InjectRepository(SeekerSocialLinks)
    private seekerSocialLinksRepository: Repository<SeekerSocialLinks>,
    @InjectRepository(SeekerAddress)
    private seekerAddressRepository: Repository<SeekerAddress>,
    @InjectRepository(SeekerSkill)
    private seekerSkillRepository: Repository<SeekerSkill>,
    @InjectRepository(SeekerLanguage)
    private seekerLanguageRepository: Repository<SeekerLanguage>,
    @InjectRepository(Companies)
    private companyRepository: Repository<Companies>,
    @InjectRepository(CompanySocialLink)
    private companySocialLinksRepository: Repository<CompanySocialLink>,
    @InjectRepository(Franchises)
    private franchiseRepository: Repository<Franchises>,
    @InjectRepository(OtpRequests)
    private otpRepository: Repository<OtpRequests>,
    @InjectRepository(Associates)
    private associatesRepository: Repository<Associates>,
    private readonly dataSource: DataSource,
    private readonly imageProcessorService: ImageProcessorService,
    private readonly activityLogsService: ActivityLogsService,
    @InjectRepository(JobPostAiSettings)
    private jobPostAiSettingsRepository: Repository<JobPostAiSettings>,
    @InjectRepository(Roles)
    private roleRepository: Repository<Roles>,
    @InjectRepository(SeekerRoleExperience)
    private seekerRoleExperienceRepository: Repository<SeekerRoleExperience>,
    @InjectRepository(CompanyKeyPeople)
    private companyKeyPeopleRepository: Repository<CompanyKeyPeople>,
    private readonly aiInterviewSchedulesService: AiInterviewSchedulesService,
    private readonly notificationService: WhatsAppNotificationService,
    private readonly seekerAddressService: SeekerAddressService,
    private readonly emailService: EmailService,
    private readonly userNotificationsService: UserNotificationsService,
  ) { }

  // Seeker Dashboard Methods
  async getMobileHomeSeekerSummary(userId: number) {
    const numericUserId = Number(userId);
    if (!numericUserId || Number.isNaN(numericUserId)) {
      return { success: false, message: 'Invalid userId' };
    }

    const [user, alertCount, completion] = await Promise.all([
      this.userRepository.findOne({
        where: { id: numericUserId },
        select: ['id', 'fullName', 'profilePicture', 'selectedRoleCategory'],
      }),
      this.jobAlertRepository.count({
        where: { user_id: numericUserId, is_active: true },
      }),
      this.getSeekerProfileCompletion(numericUserId),
    ]);

    if (!user) {
      return { success: false, message: 'User not found' };
    }

    const completionPct = completion?.success ? completion.completion || 0 : 0;

    return {
      success: true,
      user: {
        id: numericUserId,
        name: user.fullName || 'User',
        profileCompletion: completionPct,
        profileImage:
          user.profilePicture || 'https://cdn.jobvumi.com/users/default.png',
        notificationCount: alertCount,
      },
      isBlueCollar: user.selectedRoleCategory === 'blue_collar',
    };
  }
  async getSeekerOverview(userId: string) {
    try {
      // console.log('Received userId:', userId);

      const numericUserId = Number(userId);
      // console.log('Converted numericUserId:', numericUserId);

      if (isNaN(numericUserId)) {
        // console.log('userId is not a valid number!');
        return { success: false, message: 'Invalid userId' };
      }

      const seekerProfile = await this.seekerProfileRepository.findOne({
        where: { user_id: numericUserId },
      });
      // console.log('Found seekerProfile:', seekerProfile);

      if (!seekerProfile) {
        // console.log('No seeker profile found for userId:', numericUserId);
        return { success: false, message: 'Seeker profile not found' };
      }
      const seekerId = seekerProfile.id;
      // console.log('Mapped seekerId:', seekerId);

      // Get current month start date
      const currentDate = new Date();
      const currentMonthStart = new Date(
        currentDate.getFullYear(),
        currentDate.getMonth(),
        1,
      );

      // Use seekerId for job_applications and job_saved
      const totalApplied = await this.applicationRepository
        .createQueryBuilder('app')
        .where('app.seeker_id = :seekerId', { seekerId })
        .getCount();

      const monthlyApplied = await this.applicationRepository
        .createQueryBuilder('app')
        .where('app.seeker_id = :seekerId', { seekerId })
        .andWhere('app.applied_at >= :monthStart', {
          monthStart: currentMonthStart,
        })
        .getCount();

      const totalFavorites = await this.jobSavedRepository
        .createQueryBuilder('saved')
        .where('saved.seeker_id = :seekerId', { seekerId })
        .andWhere('saved.is_deleted = :isDeleted', { isDeleted: false })
        .getCount();

      const monthlyFavorites = await this.jobSavedRepository
        .createQueryBuilder('saved')
        .where('saved.seeker_id = :seekerId', { seekerId })
        .andWhere('saved.is_deleted = :isDeleted', { isDeleted: false })
        .andWhere('saved.saved_at >= :monthStart', {
          monthStart: currentMonthStart,
        })
        .getCount();

      // Use userId for job_alerts (if that table uses user_id)
      const totalAlerts = await this.jobAlertRepository
        .createQueryBuilder('alert')
        .where('alert.user_id = :userId', { userId: numericUserId })
        .andWhere('alert.is_active = :isActive', { isActive: true })
        .getCount();

      const monthlyAlerts = await this.jobAlertRepository
        .createQueryBuilder('alert')
        .where('alert.user_id = :userId', { userId: numericUserId })
        .andWhere('alert.is_active = :isActive', { isActive: true })
        .andWhere('alert.created_at >= :monthStart', {
          monthStart: currentMonthStart,
        })
        .getCount();

      const recentApplications = await this.applicationRepository
        .createQueryBuilder('app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .leftJoin('companies', 'company', 'company.id = job.company_id')
        .select([
          'app.id AS id',
          'app.job_post_id AS job_post_id',
          'app.seeker_id AS seeker_id',
          'app.cover_letter AS cover_letter',
          'app.status AS status',
          'app.applied_at AS applied_at',
          'job.title AS title',
          'job.expires_at AS expires_at',
          'company.name AS company_name',
          'company.logo AS company_logo',
        ])
        .where('app.seeker_id = :seekerId', { seekerId })
        .orderBy(
          'CASE WHEN job.expires_at IS NULL OR job.expires_at > NOW() THEN 0 ELSE 1 END',
          'ASC',
        ) // Non-expired first
        .addOrderBy('app.applied_at', 'DESC') // Then by application date
        .take(5)
        .getRawMany();

      const overview = {
        totalApplied,
        monthlyApplied,
        totalFavorites,
        monthlyFavorites,
        totalAlerts,
        monthlyAlerts,
        recentApplications,
      };

      return {
        success: true,
        data: overview,
      };
    } catch (error) {
      // console.log('Error in getSeekerOverview:', error);
      return {
        success: false,
        message: 'Failed to fetch seeker overview',
        error: error.message,
      };
    }
  }

  async getSeekerAppliedJobs(
    userId: string,
    options: { page: number; limit: number; status?: string },
  ) {
    // console.log('userId', userId);
    try {
      // 1. Find the seeker's profile using userId
      const seekerProfile = await this.seekerProfileRepository.findOne({
        where: { user_id: Number(userId) },
      });
      if (!seekerProfile) {
        return { success: false, message: 'Seeker profile not found' };
      }
      const seekerId = seekerProfile.id;

      // 2. Use seekerId in your applications query
      const { page, limit, status } = options;
      const offset = (page - 1) * limit;

      let query = this.applicationRepository
        .createQueryBuilder('app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .leftJoin('companies', 'company', 'company.id = job.company_id')
        .leftJoin('experiences', 'exp', 'exp.id = job.experience_id')
        .leftJoin('job_type', 'jt', 'jt.id = job.job_type_id')
        .leftJoin('city', 'ci', 'ci.city_id = job.city_id')
        .leftJoin('state', 'st', 'st.state_id = job.state_id')
        .leftJoin('country', 'co', 'co.country_id = job.country_id')
        .select([
          'app.id AS id',
          'app.job_post_id AS job_post_id',
          'app.seeker_id AS seeker_id',
          'app.cover_letter AS cover_letter',
          'app.status AS status',
          'app.status AS application_status',
          'app.applied_at AS applied_at',
          'job.title AS title',
          'job.title AS job_title',
          'job.expires_at AS expires_at',
          'company.name AS company_name',
          'company.logo AS company_logo',
          'job.salary_min AS salary_min',
          'job.salary_max AS salary_max',
          'jt.name AS job_type',
          `COALESCE(
            CASE
              WHEN exp.duration IS NOT NULL AND exp.duration != '' THEN CONCAT(exp.level, ' (', exp.duration, ')')
              ELSE exp.level
            END,
            'Any Experience'
          ) AS experience_level`,
          `CONCAT(
            COALESCE(ci.city_name,'') ,
            CASE WHEN st.state_name IS NOT NULL THEN CONCAT(', ', st.state_name) ELSE '' END,
            CASE WHEN co.country_name IS NOT NULL THEN CONCAT(', ', co.country_name) ELSE '' END
          ) AS location`,
        ])
        .where('app.seeker_id = :seekerId', { seekerId })
        .orderBy(
          'CASE WHEN job.expires_at IS NULL OR job.expires_at > NOW() THEN 0 ELSE 1 END',
          'ASC',
        ) // Non-expired first
        .addOrderBy('app.applied_at', 'DESC'); // Then by application date // Then by application date

      if (status) {
        query = query.andWhere('app.status = :status', { status });
      }

      const applications = await query.skip(offset).take(limit).getRawMany();

      const total = await query.getCount();

      return {
        success: true,
        data: {
          applications,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch applied jobs',
        error: error.message,
      };
    }
  }

  // Get aggregated stats for seeker's applied jobs (accurate counts from database)
  async getSeekerAppliedJobsStats(userId: string) {
    try {
      const seekerProfile = await this.seekerProfileRepository.findOne({
        where: { user_id: Number(userId) },
      });
      if (!seekerProfile) {
        return { success: false, message: 'Seeker profile not found' };
      }
      const seekerId = seekerProfile.id;

      // Use raw SQL for efficient aggregation
      const stats = await this.dataSource.query(
        `
        SELECT 
          COUNT(*) as total,
          SUM(CASE WHEN jp.expires_at IS NOT NULL AND jp.expires_at < NOW() THEN 1 ELSE 0 END) as expired,
          SUM(CASE WHEN (jp.expires_at IS NULL OR jp.expires_at >= NOW()) AND LOWER(ja.status) = 'applied' THEN 1 ELSE 0 END) as applied,
          SUM(CASE WHEN (jp.expires_at IS NULL OR jp.expires_at >= NOW()) AND LOWER(ja.status) = 'shortlisted' THEN 1 ELSE 0 END) as shortlisted,
          SUM(CASE WHEN (jp.expires_at IS NULL OR jp.expires_at >= NOW()) AND LOWER(ja.status) = 'interview' THEN 1 ELSE 0 END) as interview,
          SUM(CASE WHEN (jp.expires_at IS NULL OR jp.expires_at >= NOW()) AND LOWER(ja.status) = 'hired' THEN 1 ELSE 0 END) as hired,
          SUM(CASE WHEN (jp.expires_at IS NULL OR jp.expires_at >= NOW()) AND LOWER(ja.status) = 'rejected' THEN 1 ELSE 0 END) as rejected
        FROM job_applications ja
        LEFT JOIN job_posts jp ON jp.id = ja.job_post_id
        WHERE ja.seeker_id = ?
      `,
        [seekerId],
      );

      const total = parseInt(stats[0]?.total || 0);
      const expired = parseInt(stats[0]?.expired || 0);

      return {
        success: true,
        data: {
          total: total,
          expired: expired,
          active: total - expired,
          applied: parseInt(stats[0]?.applied || 0),
          shortlisted: parseInt(stats[0]?.shortlisted || 0),
          interview: parseInt(stats[0]?.interview || 0),
          hired: parseInt(stats[0]?.hired || 0),
          rejected: parseInt(stats[0]?.rejected || 0),
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch applied jobs stats',
        error: error.message,
      };
    }
  }

  async getSeekerFavoriteJobs(
    userId: string,
    options: { page: number; limit: number },
  ) {
    try {
      // Step 1: Get seeker_profile.id
      const seekerProfile = await this.seekerProfileRepository.findOne({
        where: { user_id: Number(userId) },
      });
      if (!seekerProfile) {
        return { success: false, message: 'Seeker profile not found' };
      }
      const seekerId = seekerProfile.id;

      // Step 2: Build the custom query
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      const query = this.jobSavedRepository.manager
        .createQueryBuilder()
        .select([
          'js.id AS saved_id',
          'js.saved_at AS saved_at',
          'jp.id AS job_id',
          'jp.title AS job_title',
          'jp.salary_min',
          'jp.salary_max',
          'jp.company_id',
          'jp.city_id AS jp_city_id',
          'jp.state_id AS jp_state_id',
          'jp.country_id AS jp_country_id',
          'jp.expires_at AS jp_expires_at',
          'jp.status AS jp_status',
          'jt.name AS job_type',
          'ci.city_name AS city_name',
          'st.state_name AS state_name',
          'co.country_name AS country_name',
          'c.name AS company_name',
          'c.logo AS company_logo',
          'ja.status AS application_status',
          'DATEDIFF(jp.expires_at, NOW()) AS days_remaining',
          'CASE WHEN jp.expires_at < NOW() THEN 1 ELSE 0 END AS is_expired',
        ])
        .from('job_saved', 'js')
        .innerJoin('job_posts', 'jp', 'jp.id = js.job_post_id')
        .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
        .leftJoin('state', 'st', 'st.state_id = jp.state_id')
        .leftJoin('country', 'co', 'co.country_id = jp.country_id')
        .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
        .leftJoin('companies', 'c', 'c.id = jp.company_id')
        .leftJoin(
          'job_applications',
          'ja',
          'ja.job_post_id = jp.id AND ja.seeker_id = :seekerId',
          { seekerId },
        )
        .where('js.seeker_id = :seekerId', { seekerId })
        .andWhere('js.is_deleted = false')
        .orderBy('js.saved_at', 'DESC')
        .offset(offset)
        .limit(limit);

      const savedJobs = await query.getRawMany();

      // For pagination, get total count
      const total = await this.jobSavedRepository.manager
        .createQueryBuilder()
        .from('job_saved', 'js')
        .where('js.seeker_id = :seekerId', { seekerId })
        .andWhere('js.is_deleted = false')
        .getCount();

      return {
        success: true,
        data: {
          favorites: savedJobs,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch favorite jobs',
        error: error.message,
      };
    }
  }
  //--check if seeker apply job status like applied, shortlisted, rejected, pending, etc.
  async checkIfSeekerAppliedForJob(userId: number, jobPostId: number) {
    const seekerId = await this.getSeekerIdByUserId(userId);
    if (!seekerId) return { success: false, message: 'Seeker not found' };
    const application = await this.applicationRepository.findOne({
      where: { seeker_id: seekerId, job_post_id: jobPostId },
    });

    // Check if there's an AI interview schedule for this application
    let aiInterviewScheduleId = null;
    if (application) {
      const schedule = await this.dataSource.query(
        `SELECT id FROM ai_interview_schedules WHERE application_id = ? LIMIT 1`,
        [application.id],
      );
      if (schedule && schedule.length > 0) {
        aiInterviewScheduleId = schedule[0].id;
      }
    }

    return {
      success: true,
      applied: !!application,
      status: application ? application.status : null,
      application_id: application ? application.id : null,
      ai_interview_schedule_id: aiInterviewScheduleId,
    };
  }

  // Get Seeker Resumes
  async getSeekerResumesByUserId(userId: number) {
    // Find the seeker profile for this user
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: userId },
    });
    if (!seekerProfile) {
      return { success: false, message: 'Seeker profile not found' };
    }
    // Now use the real seekerId
    const resumes = await this.seekerResumeRepository.find({
      where: { seeker_id: seekerProfile.id, is_deleted: false },
    });
    return { success: true, resumes };
  }

  // Seeker Resumes
  async uploadResume(
    seekerId: number,
    file: Express.Multer.File,
    name?: string,
    req?: Request,
  ) {
    try {
      const resumeName = name || file.originalname;

      // Note // - Actual file on disk uses unique timestamp prefix, so original filename is just metadata

      // Check for duplicates in 3 cases:
      // 1. Both name and filename match
      // 2. Only filename matches
      // 3. Only name matches

      // Normalize strings for comparison (case-insensitive, trim whitespace)
      const normalize = (str: string | null | undefined): string => {
        return (str || '').trim().toLowerCase();
      };

      // Get all existing resumes for this seeker to check duplicates more thoroughly
      // This helps catch case-insensitive matches that exact DB queries might miss
      const allExistingResumes = await this.seekerResumeRepository.find({
        where: { seeker_id: seekerId, is_deleted: false },
      });

      const normalizedFileName = normalize(file.originalname);
      const normalizedResumeName = normalize(resumeName);

      // Find resumes that match filename (case-insensitive)
      const existingResumeByFileName = allExistingResumes.find(
        (r) => normalize(r.file_name) === normalizedFileName,
      );

      // Find resumes that match name (case-insensitive)
      const existingResumeByName = allExistingResumes.find(
        (r) => normalize(r.name) === normalizedResumeName,
      );

      // Case 1: Both name and filename match the SAME resume
      // Check if a single resume exists with BOTH matching filename AND name (case-insensitive)
      // We need to check all resumes to find one that matches BOTH criteria
      const resumeWithBothMatches = allExistingResumes.find((r) => {
        const fileNameMatches = normalize(r.file_name) === normalizedFileName;
        const nameMatches = normalize(r.name) === normalizedResumeName;
        return fileNameMatches && nameMatches;
      });

      // Debug logging
      // console.log('Duplicate Check Debug:', {
      //   seekerId,
      //   uploadFileName: file.originalname,
      //   uploadResumeName: resumeName,
      //   normalizedFileName,
      //   normalizedResumeName,
      //   totalExistingResumes: allExistingResumes.length,
      //   existingResumes: allExistingResumes.map((r) => ({
      //     id: r.id,
      //     file_name: r.file_name,
      //     name: r.name,
      //     normalized_file_name: normalize(r.file_name),
      //     normalized_name: normalize(r.name),
      //   })),
      //   resumeWithBothMatches: resumeWithBothMatches
      //     ? {
      //         id: resumeWithBothMatches.id,
      //         file_name: resumeWithBothMatches.file_name,
      //         name: resumeWithBothMatches.name,
      //       }
      //     : null,
      // });

      if (resumeWithBothMatches) {
        const error: any = new Error(
          `The file name "${file.originalname}" and resume name already exists in your profile. Please upload a new filename and resume name.`,
        );
        error.code = 'DUPLICATE_BOTH';
        error.existingResumeId = resumeWithBothMatches.id;
        throw error;
      }

      // Case 2: Only filename matches (name doesn't match on that resume)
      // This should only trigger if filename exists but name doesn't match on that SAME resume
      // IMPORTANT: Only check this if Case 1 didn't already catch a match (resumeWithBothMatches is null)
      if (!resumeWithBothMatches && existingResumeByFileName) {
        // The resume with matching filename exists, but the name doesn't match → filename-only duplicate
        const error: any = new Error(
          `The file name "${file.originalname}" already exists in your profile. Please upload a new resume with a different file name.`,
        );
        error.code = 'DUPLICATE_FILENAME';
        error.existingResumeId = existingResumeByFileName.id;
        throw error;
      }

      // Case 3: Only name matches (filename doesn't match on that resume)
      // This should only trigger if name exists but filename doesn't match on that SAME resume
      // IMPORTANT: Only check this if Case 1 and Case 2 didn't already catch a match
      if (
        !resumeWithBothMatches &&
        !existingResumeByFileName &&
        existingResumeByName
      ) {
        // The resume with matching name exists, but the filename doesn't match → name-only duplicate
        const error: any = new Error(
          `A resume with the name "${resumeName}" already exists in your profile. Please choose a different name.`,
        );
        error.code = 'DUPLICATE_NAME';
        error.existingResumeId = existingResumeByName.id;
        throw error;
      }

      // Create a new resume
      const resume = await this.seekerResumeRepository.save({
        seeker_id: seekerId,
        file_url: file.path,
        file_name: file.originalname,
        name: resumeName,
        uploaded_at: new Date(),
        is_deleted: false,
      });
      return {
        success: true,
        resume_id: resume.id,
        file_url: file.path,
        created: true,
      };
    } catch (error) {
      // Check if this is a duplicate error with a code (preserve it)
      if (error instanceof Error && (error as any).code) {
        const errorCode = (error as any).code;
        // Re-throw duplicate errors as-is to preserve the error code
        if (
          errorCode === 'DUPLICATE_BOTH' ||
          errorCode === 'DUPLICATE_FILENAME' ||
          errorCode === 'DUPLICATE_NAME'
        ) {
          // Re-throw duplicate errors immediately - logging will happen in background
          // This ensures the error is returned quickly to the user
          throw error;
        }
      }

      // Log detailed error for developers
      const detailedError = {
        errorType: error?.constructor?.name || 'Unknown',
        errorMessage: error?.message || 'Unknown error',
        stack: error instanceof Error ? error.stack : undefined,
        seekerId,
        fileName: file?.originalname,
        fileSize: file?.size,
        name,
        timestamp: new Date().toISOString(),
      };
      console.error('Resume upload service error (detailed):', detailedError);

      // Try to log to activity logs (non-blocking)
      try {
        if (req) {
          // Fetch seeker profile to get the correct user_id (not seekerId)
          let userId = seekerId; // Fallback to seekerId if fetch fails
          try {
            const seekerProfile = await this.seekerProfileRepository.findOne({
              where: { id: seekerId },
            });
            userId = seekerProfile?.user_id || seekerId;
          } catch (profileError) {
            // If fetching profile fails, use seekerId as fallback
            console.error(
              'Failed to fetch seeker profile for logging:',
              profileError,
            );
          }

          const ipAddress =
            (req as any).ip ||
            req.headers['x-forwarded-for'] ||
            (req as any).connection?.remoteAddress;
          await this.activityLogsService.log({
            userId: userId, // Use actual user_id from seeker profile
            userType: 'seeker',
            actionType: 'seeker_action',
            actionName: 'Resume Upload Failed (Server)',
            pageUrl: req.url || '/dashboard/seeker/upload_resume',
            actionDetails: detailedError,
            status: 'failed',
            errorMessage: error?.message || 'Database error',
            ipAddress: Array.isArray(ipAddress) ? ipAddress[0] : ipAddress,
            deviceType: 'desktop', // Could be enhanced to detect from headers
            browser: req.headers['user-agent'] || 'Unknown',
          });
        }
      } catch (logError) {
        // Don't fail if logging fails
        console.error('Failed to log activity:', logError);
      }

      // Throw user-friendly error
      throw new Error('Failed to save resume');
    }
  }

  // Validate Seeker CV exists (lightweight check without reading file)
  async validateSeekerCV(seekerId: number) {
    try {
      // Get seeker's most recent resume from database
      const resume = await this.seekerResumeRepository.findOne({
        where: { seeker_id: seekerId, is_deleted: false },
        order: { uploaded_at: 'DESC' },
      });

      if (!resume) {
        return {
          success: false,
          error: 'No resume found for this candidate',
        };
      }

      // Construct full file path from relative path
      const fullFilePath = path.join(process.cwd(), resume.file_url);

      // IMPORTANT: Check if file exists on the server
      if (!fs.existsSync(fullFilePath)) {
        return {
          success: false,
          error:
            'CV file not found on server. The file may have been deleted or moved.',
        };
      }

      return {
        success: true,
        data: {
          filename: resume.name || 'candidate_cv.pdf',
        },
      };
    } catch (error: any) {
      return {
        success: false,
        error: error.message || 'Error validating CV',
      };
    }
  }

  // Download Seeker CV with validation
  async downloadSeekerCV(seekerId: number) {
    try {
      // Get seeker's most recent resume from database
      const resume = await this.seekerResumeRepository.findOne({
        where: { seeker_id: seekerId, is_deleted: false },
        order: { uploaded_at: 'DESC' },
      });

      if (!resume) {
        return {
          success: false,
          error: 'No resume found for this candidate',
        };
      }

      // Construct full file path from relative path
      const fullFilePath = path.join(process.cwd(), resume.file_url);

      // IMPORTANT: Check if file exists on the server
      if (!fs.existsSync(fullFilePath)) {
        return {
          success: false,
          error:
            'CV file not found on server. The file may have been deleted or moved.',
        };
      }

      // Read the file
      const fileBuffer = fs.readFileSync(fullFilePath);

      // Get file extension from the actual file path
      const ext = path.extname(fullFilePath).toLowerCase();

      // Use custom name (resume.name) + original extension for download
      // This gives us the user-friendly name like "Frontend Resume.pdf"
      let filename = resume.name || 'candidate_cv';

      // If custom name doesn't already have an extension, add it
      if (!filename.toLowerCase().endsWith(ext)) {
        filename += ext;
      }

      // Determine content type based on file extension
      let contentType = 'application/pdf';
      if (ext === '.doc') {
        contentType = 'application/msword';
      } else if (ext === '.docx') {
        contentType =
          'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
      }

      return {
        success: true,
        data: {
          fileBuffer,
          filename,
          contentType,
        },
      };
    } catch (error: any) {
      return {
        success: false,
        error: error.message || 'Error downloading CV',
      };
    }
  }

  // Get Seeker Profile
  async getSeekerProfile(userId: number) {
    const profile = await this.seekerProfileRepository.findOne({
      where: { user_id: userId },
    });

    if (profile) {
      // Fetch role experiences
      const roleExperiences = await this.seekerRoleExperienceRepository.find({
        where: { seeker_id: profile.id },
      });
      (profile as any).role_experiences = roleExperiences;

      // Fetch Preferred City Name
      if (profile.preferred_work_city_id) {
        const city = await this.dataSource.query(
          `SELECT city_name FROM city WHERE city_id = ?`,
          [profile.preferred_work_city_id],
        );
        if (city && city.length > 0) {
          (profile as any).preferred_city_name = city[0].city_name;
        }
      }

      // Fetch Preferred Area Name
      if (profile.preferred_work_locality_area_id) {
        const area = await this.dataSource.query(
          `SELECT area FROM area WHERE id = ?`,
          [profile.preferred_work_locality_area_id],
        );
        if (area && area.length > 0) {
          (profile as any).preferred_area_name = area[0].area;
        }
      }
    }

    // Fetch User category for role-specific dashboard rendering
    const userEntity = await this.userRepository.findOne({
      where: { id: userId },
      select: ['selectedRoleCategory'],
    });
    if (userEntity && profile) {
      (profile as any).selected_role_category = userEntity.selectedRoleCategory;
    }

    return { success: true, profile };
  }

  // Seeker Job Alerts
  async getSeekerJobAlerts(
    userId: number,
    options: { page: number; limit: number },
  ) {
    try {
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      const alerts = await this.jobAlertRepository
        .createQueryBuilder('alert')
        .where('alert.user_id = :userId', { userId })
        .andWhere('alert.is_active = :isActive', { isActive: true })
        .skip(offset)
        .take(limit)
        .getMany();

      const total = await this.jobAlertRepository
        .createQueryBuilder('alert')
        .where('alert.user_id = :userId', { userId })
        .andWhere('alert.is_active = :isActive', { isActive: true })
        .getCount();

      return {
        success: true,
        data: {
          alerts,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch job alerts',
        error: error.message,
      };
    }
  }
  //fetch job alerts job for seeker matched by skills and role
  async getJobAlertsjoblistForSeeker(
    userId: number,
    options: { page: number; limit: number },
  ) {
    const logger = new Logger('DashboardService');
    logger.log(`Fetching job alerts for userId=${userId}`);
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile) {
      return {
        success: false,
        message: 'Seeker profile not found',
        data: {
          alerts: [],
          pagination: {
            page: options.page,
            limit: options.limit,
            total: 0,
            totalPages: 0,
          },
        },
      };
    }

    try {
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      const alerts = await this.jobAlertRepository
        .createQueryBuilder('alert')
        .innerJoin('job_posts', 'jp', 'jp.id = alert.job_post_id')
        .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
        .leftJoin('state', 'st', 'st.state_id = jp.state_id')
        .leftJoin('country', 'co', 'co.country_id = jp.country_id')
        .leftJoin('companies', 'c', 'c.id = jp.company_id')
        .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
        .leftJoin(
          'job_applications',
          'ja',
          'ja.job_post_id = jp.id AND ja.seeker_id = :seekerId',
        )
        .where('alert.user_id = :userId', { userId: Number(userId) })
        .andWhere('alert.is_active = true')
        .setParameter('seekerId', seekerProfile.id)
        .distinct(true)
        .select([
          'alert.id AS alert_id',
          'alert.created_at AS alert_created_at',
          'jp.id AS job_id',
          'jp.title AS job_title',
          'jp.salary_min AS jp_salary_min',
          'jp.salary_max AS jp_salary_max',
          'jp.company_id',
          'jp.city_id AS jp_city_id',
          'jp.state_id AS jp_state_id',
          'jp.country_id AS jp_country_id',
          'jp.expires_at AS jp_expires_at',
          'jp.status AS jp_status',
          'ci.city_name AS city_name',
          'st.state_name AS state_name',
          'co.country_name AS country_name',
          'c.name AS company_name',
          'c.logo AS company_logo',
          'jt.name AS job_type_name',
          'ja.status AS application_status',
          'ja.applied_at',
          'ja.cover_letter',
        ])
        .orderBy('alert.created_at', 'DESC')
        .skip(offset)
        .take(limit)
        .getRawMany();

      // Get total count for pagination
      const total = await this.jobAlertRepository
        .createQueryBuilder('alert')
        .innerJoin('job_posts', 'jp', 'jp.id = alert.job_post_id')
        .where('alert.user_id = :userId', { userId: Number(userId) })
        .andWhere('alert.is_active = true')
        .getCount();

      logger.log(`Found ${alerts.length} job alerts for userId=${userId}`);
      return {
        success: true,
        data: {
          alerts,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      logger.error(
        `Error fetching job alerts for userId=${userId}: ${error.message}`,
      );
      return {
        success: false,
        message: 'Failed to fetch job alerts',
        error: error.message,
      };
    }
  }

  async createJobAlertsForNewPost(jobPostId: number): Promise<void> {
    const logger = new Logger('DashboardService');
    const now = new Date();

    //  Fetch details for the job post
    const jobPost = await this.dataSource
      .createQueryBuilder()
      .select(['job.role_id', 'job.title', 'job.city_id', 'job.state_id'])
      .from('job_posts', 'job')
      .where('job.id = :jobPostId', { jobPostId })
      .getRawOne<{
        role_id: number | null;
        title: string;
        city_id: number | null;
        state_id: number | null;
      }>();

    const roleId = jobPost?.role_id ?? null;
    const jobTitle = jobPost?.title || 'Job';

    //  Fetch skill_ids for the job post
    const jobSkillRows = await this.dataSource
      .createQueryBuilder()
      .select('skill_id')
      .from('job_post_skills', 'jps')
      .where('jps.job_post_id = :jobPostId', { jobPostId })
      .getRawMany<{ skill_id: number }>();

    const jobSkillIds = jobSkillRows.map((row) => row.skill_id);

    //  Find matching users by skills (from seeker_preferences)
    let skillUserIds: number[] = [];
    if (jobSkillIds.length > 0) {
      const skillUsers = await this.dataSource
        .createQueryBuilder()
        .select('DISTINCT sp.user_id', 'user_id')
        .from('seeker_preferred_skills', 'sps')
        .innerJoin(
          'seeker_preferences',
          'sp',
          'sp.id = sps.seeker_preference_id',
        )
        .where('sps.skill_id IN (:...jobSkillIds)', { jobSkillIds })
        .getRawMany<{ user_id: number }>();
      skillUserIds = skillUsers.map((row) => row.user_id);
    }

    //  Find matching users by role (from seeker_preferences)
    let roleUserIds: number[] = [];
    if (roleId !== null) {
      const roleUsers = await this.dataSource
        .createQueryBuilder()
        .select('DISTINCT sp.user_id', 'user_id')
        .from('seeker_preferred_roles', 'spr')
        .innerJoin(
          'seeker_preferences',
          'sp',
          'sp.id = spr.seeker_preference_id',
        )
        .where('spr.role_id = :roleId', { roleId })
        .getRawMany<{ user_id: number }>();
      roleUserIds = roleUsers.map((row) => row.user_id);
    }

    //  Union of user IDs (distinct)
    const allUserIds = Array.from(new Set([...skillUserIds, ...roleUserIds]));

    if (allUserIds.length === 0) {
      logger.log(`No users matched for job post ${jobPostId}`);
      return;
    }

    //  Filter out users who already have a job_alert for this job_post
    const existingAlerts = await this.dataSource
      .createQueryBuilder()
      .select('user_id')
      .from('job_alerts', 'ja')
      .where('ja.job_post_id = :jobPostId', { jobPostId })
      .andWhere('ja.user_id IN (:...allUserIds)', { allUserIds })
      .getRawMany<{ user_id: number }>();

    const existingUserIds = new Set(existingAlerts.map((row) => row.user_id));
    const toInsertUserIds = allUserIds.filter((id) => !existingUserIds.has(id));

    //Insert job_alerts for new users
    if (toInsertUserIds.length > 0) {
      const insertValues = toInsertUserIds.map((userId) => ({
        user_id: userId,
        job_post_id: jobPostId,
        is_active: true,
        created_at: now,
        updated_at: now,
      }));

      await this.dataSource
        .createQueryBuilder()
        .insert()
        .into('job_alerts')
        .values(insertValues)
        .execute();

      logger.log(
        `Job alerts created for job_post_id=${jobPostId}: matched users=${allUserIds.length}, inserted alerts=${toInsertUserIds.length}`,
      );

      // --- WhatsApp Notification Trigger ---
      try {
        let locationText = '';
        if (jobPost.city_id) {
          const c = await this.dataSource.query(
            'SELECT city_name FROM city WHERE city_id = ?',
            [jobPost.city_id],
          );
          if (c && c.length > 0) locationText = c[0].city_name;
        }
        if (jobPost.state_id) {
          const s = await this.dataSource.query(
            'SELECT state_name FROM state WHERE state_id = ?',
            [jobPost.state_id],
          );
          if (s && s.length > 0)
            locationText = locationText
              ? `${locationText}, ${s[0].state_name}`
              : s[0].state_name;
        }

        for (const userId of toInsertUserIds) {
          const user = await this.userRepository.findOne({
            where: { id: userId },
            select: ['id', 'fullName', 'username'],
          });

          const candidateName = user?.fullName || user?.username || 'Candidate';

          const payload = {
            candidate_name: candidateName,
            job_title: jobTitle,
            job_location: locationText || 'India',
          };

          this.notificationService
            .createEvent(
              'candidate_job_alert',
              userId,
              'job_post',
              jobPostId,
              payload,
            )
            .catch((err) =>
              logger.error(
                `Failed to create WhatsApp notification event for userId=${userId}`,
                err,
              ),
            );
        }
      } catch (whatsappError) {
        logger.error(
          'Error triggering WhatsApp notifications in createJobAlertsForNewPost',
          whatsappError,
        );
      }
    } else {
      logger.log(
        `All matched users already have alerts for job_post_id=${jobPostId}`,
      );
    }
  }

  async deleteJobAlert(userId: string, alertId: string) {
    try {
      await this.jobAlertRepository
        .createQueryBuilder()
        .delete()
        .where('id = :alertId AND user_id = :userId', { alertId, userId })
        .execute();

      return {
        success: true,
        message: 'Job alert deleted successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to delete job alert',
        error: error.message,
      };
    }
  }

  // Allow a seeker to apply for a selected job
  async applyForJob(
    userId: string,
    jobPostId: string,
    coverLetter?: string,
    resumeId?: string,
  ) {
    const logger = new Logger('DashboardService');
    // console.log('applyForJob called with:', {
    //   userId,
    //   jobPostId,
    //   coverLetter,
    //   resumeId,
    // });
    //  Get seeker_profile.id
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    // console.log('seekerProfile:', seekerProfile);
    if (!seekerProfile) {
      // console.log('Seeker profile not found for userId:', userId);
      return { success: false, message: 'Seeker profile not found' };
    }
    const seekerId = seekerProfile.id;
    // console.log('seekerId:', seekerId);

    //  Check if job exists
    const job = await this.jobRepository.findOne({
      where: { id: Number(jobPostId) },
    });
    // console.log('job:', job);
    if (!job) {
      // console.log('Job post not found for jobPostId:', jobPostId);
      return { success: false, message: 'Job post not found' };
    }

    //  Check if already applied
    const existingApplication = await this.applicationRepository.findOne({
      where: { seeker_id: seekerId, job_post_id: Number(jobPostId) },
    });
    // console.log('existingApplication:', existingApplication);
    if (existingApplication) {
      // console.log('Already applied to this job:', { seekerId, jobPostId });
      return { success: false, message: 'Already applied to this job' };
    }

    //  Create new application
    const newApplication = this.applicationRepository.create({
      seeker_id: seekerId,
      job_post_id: Number(jobPostId),
      cover_letter: coverLetter || '',
      status: 'Applied',
      resume_id: resumeId ? Number(resumeId) : null,
      applied_at: new Date(),
    });
    // console.log('Creating new application:', newApplication);
    await this.applicationRepository.save(newApplication);

    const candidateUser = await this.userRepository.findOne({
      where: { id: Number(userId) },
    });

    // let toEmail: string | null = null;
    // if (
    //   job.communication_preference === 'other_recruiter' &&
    //   job.other_recruiter_email
    // ) {
    //   toEmail = job.other_recruiter_email;
    // }
    // if (!toEmail) {
    //   const associtesemailcompany = await this.associatesRepository.findOne({
    //     where: { id: job.company_id },
    //   });
    //   if (associtesemailcompany) {
    //     toEmail = associtesemailcompany.email;
    //   }
    //   const company = await this.companyRepository.findOne({
    //     where: { id: job.company_id },
    //   });
    //   if (company) {
    //     const recruiterUser = await this.userRepository.findOne({
    //       where: { id: company.user_id },
    //     });
    //     toEmail = recruiterUser?.email || null;
    //   }
    // }

    let toEmail: string | null = null;

    if (
      job.communication_preference === 'other_recruiter' &&
      job.other_recruiter_email
    ) {
      toEmail = job.other_recruiter_email;
    }

    // Helper to resolve a user's email by user id
    const emailOfUser = async (uid?: number | null) => {
      if (!uid) return null;
      const u = await this.userRepository.findOne({
        where: { id: uid },
        select: ['id', 'email'],
      });
      return u?.email ?? null;
    };

    // If associate posted the job -> use that associate's linked user email
    if (!toEmail && job.associate_id) {
      const assoc = await this.associatesRepository.findOne({
        where: { id: job.associate_id },
        select: ['id', 'user_id'],
      });
      if (assoc?.user_id) {
        toEmail = await emailOfUser(assoc.user_id);
        if (!toEmail)
          logger.warn?.(`Associate ${assoc.id} has no linked user email`);
      } else {
        logger.warn?.(
          `Job ${job.id} references associate ${job.associate_id} but associate missing`,
        );
      }
    }

    // If still no toEmail -> route to company recruiter (real company)
    if (!toEmail && job.company_id) {
      const company = await this.companyRepository.findOne({
        where: { id: job.company_id },
        select: ['id', 'user_id'],
      });
      if (company?.user_id) {
        toEmail = await emailOfUser(company.user_id);
        if (!toEmail)
          logger.warn?.(`Company user ${company.user_id} has no email`);
      } else {
        logger.warn?.(`Company ${job.company_id} missing or has no user_id`);
      }
    }

    // Last fallback: job.other_recruiter_email if present
    if (!toEmail && job.other_recruiter_email) {
      toEmail = job.other_recruiter_email;
    }
    // --- NEW LOGIC: Fetch details based on collar type ---

    // --- NEW LOGIC: Fetch details based on collar type ---
    const isBlueCollar = candidateUser?.selectedRoleCategory === 'blue_collar';
    let roleName = '—';
    let experienceText = '—';
    let educationText = '—';
    let locationText = '—';

    try {
      if (isBlueCollar) {
        // Blue Collar: Role & Experience from seeker_role_experience (Fetch All Active)
        const roleExps = await this.dataSource.query(
          `
            SELECT r.name as role_name, e.duration as experience_duration
            FROM seeker_role_experience sre
            LEFT JOIN roles r ON r.id = sre.designation_id
            LEFT JOIN experiences e ON e.id = sre.experience_id
            WHERE sre.seeker_id = ?
            AND sre.status = 1
            ORDER BY sre.id DESC
          `,
          [seekerId],
        );

        if (roleExps && roleExps.length > 0) {
          // Format: "Plumber (1-2 Years), Electrician (0-5 Years)"
          roleName = roleExps
            .map((r) => {
              const name = r.role_name || 'Role';
              const duration = r.experience_duration
                ? ` (${r.experience_duration})`
                : '';
              return `${name}${duration}`;
            })
            .join(', ');
          // Clear separate experience text as it's merged into roleName
          experienceText = '';
        }
        // Education is hidden for Blue Collar
        // ... (Location fetching logic remains same)
        // Location from Preferred City (seeker_profiles)
        if (seekerProfile.preferred_work_city_id) {
          const loc = await this.dataSource.query(
            `
                SELECT c.city_name, s.state_name
                FROM city c
                LEFT JOIN state s ON s.state_id = c.state_id
                WHERE c.city_id = ?
             `,
            [seekerProfile.preferred_work_city_id],
          );
          if (loc && loc.length > 0) {
            locationText = `${loc[0].city_name}, ${loc[0].state_name}`;
          }
        }
      } else {
        // White Collar
        // Role from profession_id -> roles table
        if (seekerProfile.profession_id) {
          const roleData = await this.dataSource.query(
            `SELECT name FROM roles WHERE id = ?`,
            [seekerProfile.profession_id],
          );
          if (roleData && roleData.length > 0) {
            roleName = roleData[0].name;
          }
        }

        // Experience from seeker_profiles -> experiences (level)
        if (seekerProfile.experiences_id) {
          const exp = await this.dataSource.query(
            `SELECT level FROM experiences WHERE id = ?`,
            [seekerProfile.experiences_id],
          );
          if (exp && exp.length > 0) experienceText = exp[0].level;
        }

        // Education (remains same)
        if (seekerProfile.education_id) {
          const edu = await this.dataSource.query(
            `SELECT name FROM education_levels WHERE id = ?`,
            [seekerProfile.education_id],
          );
          if (edu && edu.length > 0) educationText = edu[0].name;
        }

        // Location from Seeker Address (remains same)
        const address = await this.seekerAddressRepository.findOne({
          where: { seeker_profile_id: seekerId },
        });
        if (address) {
          const cityId =
            address.communication_city_id || address.permanent_city_id;
          if (cityId) {
            const loc = await this.dataSource.query(
              `
                    SELECT c.city_name, s.state_name
                    FROM city c
                    LEFT JOIN state s ON s.state_id = c.state_id
                    WHERE c.city_id = ?
                 `,
              [cityId],
            );
            if (loc && loc.length > 0) {
              locationText = `${loc[0].city_name}, ${loc[0].state_name}`;
            }
          }
        }
      }
    } catch (err) {
      logger.error(`Error fetching email details: ${err.message}`);
    }
    if (toEmail) {
      const preferredName =
        candidateUser?.fullName ||
        candidateUser?.username ||
        candidateUser?.email?.split('@')[0] ||
        'Candidate';

      const roleRow =
        isBlueCollar && roleName !== '—'
          ? `<tr><td style="padding:12px; border-bottom:1px solid #e5e7eb;"><strong>Role</strong></td>
                 <td style="padding:12px; border-bottom:1px solid #e5e7eb; color:#374151;">${roleName}</td></tr>`
          : '';

      const experienceRow =
        experienceText !== '—'
          ? `<tr><td style="padding:12px; border-bottom:1px solid #e5e7eb;"><strong>Experience</strong></td>
                 <td style="padding:12px; border-bottom:1px solid #e5e7eb; color:#374151;">${experienceText}</td></tr>`
          : '';

      const educationRow =
        !isBlueCollar && educationText !== '—'
          ? `<tr><td style="padding:12px; border-bottom:1px solid #e5e7eb;"><strong>Education</strong></td>
                 <td style="padding:12px; border-bottom:1px solid #e5e7eb; color:#374151;">${educationText}</td></tr>`
          : '';

      const coverLetterSection =
        !isBlueCollar && coverLetter?.trim()
          ? `
             <p style="margin:12px 0 4px 0; font-weight:bold;">Cover Letter</p>
             <div style="background-color:#f3f4f6; padding:10px; border-radius:6px; font-style:italic;">
               "${coverLetter}"
             </div>
            `
          : '';

      const templateName = 'new_job_application';
      const toName = toEmail.split('@')[0];

      const context = {
        job_title: job.title || '—',
        preferred_name: preferredName,
        role_row: roleRow,
        experience_row: experienceRow,
        education_row: educationRow,
        location_text: locationText,
        cover_letter_section: coverLetterSection,
      };

      await this.emailService.sendTemplateEmail(
        templateName,
        toEmail,
        toName,
        context
      );
    }
    // --- WhatsApp Notification Trigger ---
    // --- WhatsApp Notification Trigger ---
    try {
      // 1. Candidate Notification
      const candidateTemplate = 'candidate_application_submitted';

      let location = '';
      try {
        if (job.city_id) {
          const c = await this.dataSource.query(
            'SELECT city_name FROM city WHERE city_id = ?',
            [job.city_id],
          );
          if (c && c.length > 0) location = c[0].city_name;
        }
        if (job.state_id) {
          const s = await this.dataSource.query(
            'SELECT state_name FROM state WHERE state_id = ?',
            [job.state_id],
          );
          if (s && s.length > 0)
            location = location
              ? `${location}, ${s[0].state_name}`
              : s[0].state_name;
        }
      } catch (e) {
        console.error('Error fetching location for notification', e);
      }

      const candidatePayload = {
        1: candidateUser?.fullName || candidateUser?.username || 'Candidate', // {{1}} Name
        2: job.title || 'Job', // {{2}} Job Title
        3: location, // {{3}} Location
      };

      if (candidateTemplate) {
        this.notificationService
          .createEvent(
            candidateTemplate,
            Number(userId), // seeker's user id
            'job_application',
            Number(newApplication.id),
            candidatePayload,
          )
          .catch((err) =>
            console.error(
              'Failed to create WhatsApp notification event (applyForJob - Candidate)',
              err,
            ),
          );

        // Wire up In-App notification for Candidate
        this.userNotificationsService
          .createNotification(
            Number(userId),
            'application_status',
            `Your application for "${job.title || 'Job'}" in "${location || 'India'}" has been successfully submitted.`
          )
          .catch((err) =>
            console.error('Failed to create in-app notification for candidate (applyForJob)', err),
          );
      }

      // 2. Employer Notification
      let employerUserId: number | null = null;
      if (job.associate_id) {
        const assoc = await this.associatesRepository.findOne({
          where: { id: job.associate_id },
          select: ['user_id'],
        });
        if (assoc?.user_id) employerUserId = assoc.user_id;
      } else if (job.company_id) {
        const comp = await this.companyRepository.findOne({
          where: { id: job.company_id },
          select: ['user_id'],
        });
        if (comp?.user_id) employerUserId = comp.user_id;
      }

      if (employerUserId) {
        const employerTemplate = 'new_job_application_detailed';

        // Calculate Age
        let age = '—';
        if (seekerProfile.date_of_birth) {
          const dob = new Date(seekerProfile.date_of_birth);
          const diffMs = Date.now() - dob.getTime();
          const ageDt = new Date(diffMs);
          age = Math.abs(ageDt.getUTCFullYear() - 1970).toString();
        }

        const employerPayload = {
          1: candidateUser?.fullName || candidateUser?.username || 'Candidate', // candidate_name
          2: age, // age
          3: seekerProfile.gender || '—', // gender
          4: educationText === '—' ? 'Not specified' : educationText, // education
          5:
            experienceText === '—'
              ? roleName === '—'
                ? 'Not specified'
                : roleName
              : experienceText, // experience
          6: locationText === '—' ? 'Not specified' : locationText, // candidate_location
          7: job.title || 'Job', // job_title
          8: location || '—', // job_location
        };

        this.notificationService
          .createEvent(
            employerTemplate,
            employerUserId,
            'job_application',
            Number(newApplication.id),
            employerPayload,
          )
          .catch((err) =>
            console.error(
              'Failed to create WhatsApp notification event (applyForJob - Employer)',
              err,
            ),
          );

        // Wire up In-App notification for Employer
        this.userNotificationsService
          .createNotification(
            employerUserId,
            'application_status',
            `A new candidate (${candidateUser?.fullName || 'Candidate'}) has applied for your job post "${job.title || 'Job'}".`
          )
          .catch((err) =>
            console.error('Failed to create in-app notification for employer (applyForJob)', err),
          );
      }
    } catch (error) {
      console.error(
        'Error triggering WhatsApp and In-App notifications in applyForJob',
        error,
      );
    }
    // Check if AI interview is enabled in settings
    const aiSettings = await this.jobPostAiSettingsRepository.findOne({
      where: { job_post_id: Number(jobPostId) },
    });
    const aiInterviewEnabled = aiSettings?.ai_enabled || false;

    // If AI interview is enabled, we return true so frontend can prompt the user.
    // We DO NOT create the schedule here anymore; the frontend handles it based on user choice (Instant vs Scheduled).
    // This prevents duplicate schedule creation (one instant here + one scheduled in frontend).
    let aiInterviewScheduleId = null;
    /*
    if (aiInterviewEnabled) {
      try {
        const schedule = await this.aiInterviewSchedulesService.createSchedule({
          application_id: newApplication.id,
          seeker_id: seekerId,
          job_id: Number(jobPostId),
          requester_user_id: Number(userId),
          scheduled_at: null, // Will be updated when user chooses
        });
        aiInterviewScheduleId = schedule.id;
      } catch (error) {
        logger.warn?.(`Failed to create AI interview schedule: ${error.message}`);
        // Don't fail the application if schedule creation fails
      }
    }
    */

    return {
      success: true,
      message: 'Job application submitted successfully',
      id: newApplication.id,
      applicationId: newApplication.id,
      ai_interview_required: aiInterviewEnabled,
      ai_interview_schedule_id: aiInterviewScheduleId,
    };
  }

  async fetchAppliedJobs(userId: string) {
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile) return [];
    return this.applicationRepository
      .createQueryBuilder('app')
      .innerJoin('job_posts', 'jp', 'jp.id = app.job_post_id')
      .leftJoin('companies', 'c', 'c.id = jp.company_id')
      .where('app.seeker_id = :seekerId', { seekerId: seekerProfile.id })
      .select([
        'app.id AS application_id',
        'app.applied_at',
        'app.status',
        'app.cover_letter',
        'jp.id AS job_id',
        'jp.title AS job_title',
        'jp.salary_min',
        'jp.salary_max',
        'jp.expires_at',
        'c.name AS company_name',
        'c.logo AS company_logo',
      ])
      .orderBy('app.applied_at', 'DESC')
      .getRawMany();
  }

  // Alias for bookmarkJob, called by mobile controllers
  async saveJob(userId: string, jobPostId: string) {
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile)
      return { success: false, message: 'Seeker profile not found' };
    const seekerId = seekerProfile.id;

    // Check for any existing bookmark (even if deleted)
    const existing = await this.jobSavedRepository.findOne({
      where: {
        seeker_id: seekerId,
        job_post_id: Number(jobPostId),
      },
    });

    if (existing && !existing.is_deleted) {
      return { success: false, message: 'Already bookmarked' };
    }

    if (existing && existing.is_deleted) {
      // Restore the bookmark
      existing.is_deleted = false;
      await this.jobSavedRepository.save(existing);
      return { success: true, message: 'Job re-bookmarked successfully' };
    }

    // No record exists, create new
    const saved = this.jobSavedRepository.create({
      seeker_id: seekerId,
      job_post_id: Number(jobPostId),
      is_deleted: false,
    });
    await this.jobSavedRepository.save(saved);

    // Create Notification
    const job = await this.jobRepository.findOne({
      where: { id: Number(jobPostId) },
      select: ['title'],
    });
    const jobTitle = job?.title || 'a job';
    
    this.userNotificationsService
      .createNotification(
        Number(userId),
        'saved',
        `You have saved "${jobTitle}" job.`
      )
      .catch((err) =>
        console.error('Failed to create saved job notification', err),
      );

    return { success: true, message: 'Job bookmarked successfully' };
  }

  // Add a job to bookmarks (favorites)
  async bookmarkJob(userId: string, jobPostId: string) {
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile)
      return { success: false, message: 'Seeker profile not found' };
    const seekerId = seekerProfile.id;

    // Check for any existing bookmark (even if deleted)
    const existing = await this.jobSavedRepository.findOne({
      where: {
        seeker_id: seekerId,
        job_post_id: Number(jobPostId),
      },
    });

    if (existing && !existing.is_deleted) {
      return { success: false, message: 'Already bookmarked' };
    }

    if (existing && existing.is_deleted) {
      // Restore the bookmark
      existing.is_deleted = false;
      await this.jobSavedRepository.save(existing);
      return { success: true, message: 'Job re-bookmarked successfully' };
    }

    // No record exists, create new
    const saved = this.jobSavedRepository.create({
      seeker_id: seekerId,
      job_post_id: Number(jobPostId),
      is_deleted: false,
    });
    await this.jobSavedRepository.save(saved);

    // Create Notification
    const job = await this.jobRepository.findOne({
      where: { id: Number(jobPostId) },
      select: ['title'],
    });
    const jobTitle = job?.title || 'a job';
    
    this.userNotificationsService
      .createNotification(
        Number(userId),
        'saved',
        `You have saved "${jobTitle}" job.`
      )
      .catch((err) =>
        console.error('Failed to create saved job notification', err),
      );

    return { success: true, message: 'Job bookmarked successfully' };
  }

  // Remove a job from bookmarks (favorites)
  async unbookmarkJob(userId: string, jobPostId: string) {
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile)
      return { success: false, message: 'Seeker profile not found' };
    const seekerId = seekerProfile.id;
    await this.jobSavedRepository
      .createQueryBuilder()
      .update()
      .set({ is_deleted: true })
      .where('seeker_id = :seekerId AND job_post_id = :jobPostId', {
        seekerId,
        jobPostId,
      })
      .execute();
    return { success: true, message: 'Job removed from bookmarks' };
  }
  // COMMENTED OUT: seeker user inactive CRUD ---
  // async updateSeekerUserInactive(userId: number) {
  //   const user = await this.userRepository.findOne({ where: { id: userId } });
  //   // console.log('user', user);
  //   if (!user) {
  //     return { success: false, error: 'User not found' };
  //   }
  //   user.status = 'inactive';
  //   user.deletedAt = new Date();
  //   await this.userRepository.save(user);
  //   return { success: true };
  // }
  async isJobBookmarked(userId: string, jobPostId: string) {
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile) return { isBookmarked: false };
    const exists = await this.jobSavedRepository.findOne({
      where: {
        seeker_id: seekerProfile.id,
        job_post_id: Number(jobPostId),
        is_deleted: false,
      },
    });
    return { isBookmarked: !!exists };
  }
  // Get Seeker Favorite Jobs List
  async fetchBookmarkedJobs(userId: string) {
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: Number(userId) },
    });
    if (!seekerProfile) return [];
    return this.jobSavedRepository
      .createQueryBuilder('js')
      .innerJoin('job_posts', 'jp', 'jp.id = js.job_post_id')
      .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
      .leftJoin('state', 'st', 'st.state_id = jp.state_id')
      .leftJoin('country', 'co', 'co.country_id = jp.country_id')
      .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
      .leftJoin('companies', 'c', 'c.id = jp.company_id')
      .leftJoin(
        'job_applications',
        'ja',
        'ja.job_post_id = jp.id AND ja.seeker_id = :seekerId',
        { seekerId: seekerProfile.id },
      )
      .where('js.seeker_id = :seekerId', { seekerId: seekerProfile.id })
      .andWhere('js.is_deleted = false')
      .orderBy('js.saved_at', 'DESC')
      .select([
        'js.id AS saved_id',
        'js.saved_at AS saved_at',
        'jp.id AS job_id',
        'jp.title AS job_title',
        'jp.salary_min',
        'jp.salary_max',
        'jp.company_id',
        'jp.city_id AS jp_city_id',
        'jp.state_id AS jp_state_id',
        'jp.country_id AS jp_country_id',
        'jp.expires_at AS jp_expires_at',
        'jp.status AS jp_status',
        'ci.city_name AS city_name',
        'st.state_name AS state_name',
        'co.country_name AS country_name',
        'jt.name AS job_type_name',
        'c.name AS company_name',
        'ja.status AS application_status',
      ])
      .getRawMany();
  }

  // Recruiter Dashboard Methods
  async getRecruiterOverview(userId: string) {
    try {
      // Get user information
      // console.log(`DashboardService: getRecruiterOverview called for userId: ${userId}`);
      const user = await this.userRepository.findOne({
        where: { id: Number(userId) },
      });

      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      // Get wallet balance for the user
      const wallet = await this.walletRepository.findOne({
        where: { user_id: Number(userId) },
      });

      // Initialize defaults
      let totalJobsPosted = 0;
      let totalApplications = 0;
      let activeJobs = 0;
      let savedCandidates = 0;
      let recentApplications = [];

      if (company) {
        // Get total jobs posted
        totalJobsPosted = await this.jobRepository
          .createQueryBuilder('job')
          .where('job.company_id = :companyId', { companyId: company.id })
          .getCount();

        // Get total applications
        totalApplications = await this.applicationRepository
          .createQueryBuilder('app')
          .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
          .where('job.company_id = :companyId', { companyId: company.id })
          .getCount();

        // Get active jobs
        activeJobs = await this.jobRepository
          .createQueryBuilder('job')
          .where('job.company_id = :companyId', { companyId: company.id })
          .andWhere('job.status = :status', { status: 'open' })
          .getCount();

        // Get recent applications
        recentApplications = await this.applicationRepository
          .createQueryBuilder('app')
          .leftJoinAndSelect('job_posts', 'job', 'job.id = app.job_post_id')
          .where('job.company_id = :companyId', { companyId: company.id })
          .orderBy('app.applied_at', 'DESC')
          .take(5)
          .getMany();
      }

      // Get top performing jobs (mock data for now)
      const topPerformingJobs = [
        {
          id: 1,
          title: 'Senior React Developer',
          applications: 45,
          views: 320,
        },
        {
          id: 2,
          title: 'Full Stack Engineer',
          applications: 32,
          views: 280,
        },
      ];

      const overview = {
        user: {
          name: user?.fullName || 'Recruiter',
          email: user?.email,
        },
        wallet: {
          jobCredits: wallet?.job_credits || 0,
          aiCredits: wallet?.ai_credits || 0,
          databaseCredits: wallet?.database_credits || 0,
        },
        totalJobsPosted,
        totalApplications,
        totalViews: 2340, // TODO: Replace with actual query
        activeJobs,
        savedCandidates,
        recentApplications,
        topPerformingJobs,
      };

      // console.log('DashboardService: Overview data prepared:', overview);

      return {
        success: true,
        data: overview,
      };
    } catch (error) {
      console.error('DashboardService: Error in getRecruiterOverview:', error);
      return {
        success: false,
        message: 'Failed to fetch recruiter overview',
        error: error.message,
      };
    }
  }

  async getRecruiterPostedJobsWithApplications(
    userId: string,
    options: {
      page: number;
      limit: number;
      status?: string;
      search?: string;
      hasApplications?: boolean;
    },
  ) {
    try {
      const { page, limit, status, search, hasApplications } = options;
      const offset = (page - 1) * limit;
      // console.log('Fetching posted jobs for userId:', userId);
      // console.log('Options:', options);
      // console.log('Offset:', offset, 'Limit:', limit);

      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();
      // console.log('company:', company);

      if (!company) {
        return {
          success: false,
          message: 'No company found for this user',
          data: { jobs: [], total: 0, page, limit },
        };
      }
      //  Automatically mark expired jobs as 'closed' in DB - REMOVED to allow filtering by expired status
      // await this.jobRepository
      //   .createQueryBuilder()
      //   .update()
      //   .set({ status: 'closed' })
      //   .where('expires_at < NOW()')
      //   .andWhere('status != :closed', { closed: 'closed' })
      //   .andWhere('company_id = :companyId', { companyId: company.id }) // 👈 limit to this recruiter only
      //   .execute();
      const query = this.jobRepository
        .createQueryBuilder('job')
        .select([
          'job.id AS job_id',
          'job.company_id AS job_company_id',
          'job.title AS job_title',
          'job.address AS job_address',
          'job.description AS job_description',
          'job.requirements AS job_requirements',
          'job.benefits AS job_benefits',
          'job.featured AS job_featured',
          'job.salary_min AS job_salary_min',
          'job.salary_max AS job_salary_max',
          'job.status AS job_status',
          'job.created_at AS job_created_at',
          'job.updated_at AS job_updated_at',
          'job.expires_at AS job_expires_at',
          'job.job_type_id AS job_job_type_id',
          'job.experience_id AS job_experience_id',
          'job.education_id AS job_education_id',
          'job.role_levels_id AS job_role_levels_id',
          'job.country_id AS job_country_id',
          'job.state_id AS job_state_id',
          'job.city_id AS job_city_id',
          'job.role_id AS job_role_id',
          'jt.name AS job_type',
          'ci.city_name AS city_name',
          'st.state_name AS state_name',
          'co.country_name AS country_name',
          'c.name AS company_name',
          'ais.ai_enabled AS ai_interview_enabled', // Include AI enabled status
        ])
        .leftJoin('job_type', 'jt', 'jt.id = job.job_type_id')
        .leftJoin('city', 'ci', 'ci.city_id = job.city_id')
        .leftJoin('state', 'st', 'st.state_id = job.state_id')
        .leftJoin('country', 'co', 'co.country_id = job.country_id')
        .leftJoin('companies', 'c', 'c.id = job.company_id')
        // Join AI Settings
        .leftJoin('job_post_ai_settings', 'ais', 'ais.job_post_id = job.id')
        .where('job.company_id = :companyId', { companyId: company.id })
        // Filter to show only jobs posted by this recruiter (not by associates)
        .andWhere('job.associate_id IS NULL');

      // Add search filter
      if (search) {
        query.andWhere('job.title LIKE :search', { search: `%${search}%` });
      }

      if (status) {
        if (status === 'active') {
          query.andWhere('job.status = :openStatus', { openStatus: 'open' });
          query.andWhere('(job.expires_at IS NULL OR job.expires_at > NOW())');
        } else if (status === 'expired') {
          query.andWhere('job.status = :openStatus', { openStatus: 'open' });
          query.andWhere('job.expires_at <= NOW()');
        } else if (status === 'inactive') {
          query.andWhere('job.status IN (:...inactiveStatuses)', {
            inactiveStatuses: ['closed', 'paused'],
          });
        } else {
          query.andWhere('job.status = :status', { status });
        }
      }

      // If hasApplications filter is enabled, only get jobs with applications
      if (hasApplications) {
        query.andWhere((qb) => {
          const subQuery = qb
            .subQuery()
            .select('app.job_post_id')
            .from('job_applications', 'app')
            .where('app.job_post_id = job.id')
            .getQuery();
          return `EXISTS ${subQuery}`;
        });

        // When showing jobs with applicants, add latest application date for sorting
        query.addSelect((subQuery) => {
          return subQuery
            .select('MAX(app.applied_at)')
            .from('job_applications', 'app')
            .where('app.job_post_id = job.id');
        }, 'latest_application_date');
      }

      // Order by logic
      if (hasApplications) {
        // For jobs with applicants tab: sort by latest application date (newest applications first)
        query
          .orderBy('latest_application_date', 'DESC')
          .addOrderBy('job.created_at', 'DESC');
      } else {
        // For all jobs tab: Active/Open jobs first, then by newest first
        query
          .orderBy(
            'CASE WHEN job.status = :openStatus THEN 0 ELSE 1 END',
            'ASC',
          )
          .addOrderBy('job.created_at', 'DESC')
          .setParameter('openStatus', 'open');
      }

      query.offset(offset).limit(limit);
      const jobs = await query.getRawMany();
      // console.log('Fetched jobs:', jobs);
      // console.log(query.getSql());

      // Get application counts for each job
      const jobsWithApplications = await Promise.all(
        jobs.map(async (job) => {
          const applicationCount = await this.applicationRepository
            .createQueryBuilder('app')
            .where('app.job_post_id = :jobId', { jobId: job.job_id })
            .getCount();

          return {
            ...job,
            applications: applicationCount,
            location:
              `${job.city_name || ''}${job.city_name && job.state_name ? ', ' : ''}${job.state_name || ''}${(job.city_name || job.state_name) && job.country_name ? ', ' : ''}${job.country_name || ''}`.trim(),
            salary:
              job.salary_min && job.salary_max
                ? `₹${job.salary_min.toLocaleString()} - ₹${job.salary_max.toLocaleString()}`
                : job.salary_min
                  ? `₹${job.salary_min.toLocaleString()}+`
                  : 'Not specified',
          };
        }),
      );

      const totalQuery = this.jobRepository
        .createQueryBuilder('job')
        .where('job.company_id = :companyId', { companyId: company.id })
        // Filter to show only jobs posted by this recruiter (not by associates)
        .andWhere('job.associate_id IS NULL');

      // Apply same search filter for total count
      if (search) {
        totalQuery.andWhere('job.title LIKE :search', {
          search: `%${search}%`,
        });
      }

      if (status) {
        if (status === 'active') {
          totalQuery.andWhere('job.status = :openStatus', {
            openStatus: 'open',
          });
          totalQuery.andWhere(
            '(job.expires_at IS NULL OR job.expires_at > NOW())',
          );
        } else if (status === 'expired') {
          totalQuery.andWhere('job.status = :openStatus', {
            openStatus: 'open',
          });
          totalQuery.andWhere('job.expires_at <= NOW()');
        } else if (status === 'inactive') {
          totalQuery.andWhere('job.status IN (:...inactiveStatuses)', {
            inactiveStatuses: ['closed', 'paused'],
          });
        } else {
          totalQuery.andWhere('job.status = :status', { status });
        }
      }

      // Apply same hasApplications filter for total count
      if (hasApplications) {
        totalQuery.andWhere((qb) => {
          const subQuery = qb
            .subQuery()
            .select('app.job_post_id')
            .from('job_applications', 'app')
            .where('app.job_post_id = job.id')
            .getQuery();
          return `EXISTS ${subQuery}`;
        });
      }

      const total = await totalQuery.getCount();

      return {
        success: true,
        data: {
          jobs: jobsWithApplications,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch posted jobs',
        error: error.message,
      };
    }
  }

  // Get AI Interview Jobs with Stats for Recruiter Dashboard
  async getRecruiterAiJobs(
    userId: string,
    options: {
      page: number;
      limit: number;
      search?: string;
    },
  ) {
    try {
      const { page, limit, search } = options;
      const offset = (page - 1) * limit;

      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        return {
          success: true,
          data: { jobs: [], total: 0, page, limit, totalPages: 0 },
        };
      }

      const query = this.jobRepository
        .createQueryBuilder('job')
        .select([
          'job.id AS id',
          'job.title AS title',
          'job.company_id AS company_id',
          'job.created_at AS created_at',
          'job.status AS status',
          'job.image_url AS image_url',
          'c.name AS company_name',
          // Counts
          `(SELECT COUNT(*) FROM job_applications ja WHERE ja.job_post_id = job.id) AS total_applicants`,
          `(SELECT COUNT(*) FROM ai_interview_schedules ais WHERE ais.job_id = job.id AND ais.status = 'completed') AS ai_completed`,
          `(SELECT COUNT(*) FROM ai_interview_schedules ais WHERE ais.job_id = job.id AND ais.status IN ('scheduled', 'in_progress', 'approved')) AS ai_scheduled`,
          `(SELECT COUNT(*) FROM ai_interview_schedules ais WHERE ais.job_id = job.id AND ais.status = 'no_show') AS ai_no_show`,
        ])
        .leftJoin('companies', 'c', 'c.id = job.company_id')
        .innerJoin(
          'job_post_ai_settings',
          'ais_settings',
          'ais_settings.job_post_id = job.id AND ais_settings.ai_enabled = 1',
        )
        .where('job.company_id = :companyId', { companyId: company.id })
        .andWhere('job.associate_id IS NULL') // specific to recruiter posted jobs
        .orderBy('job.created_at', 'DESC');

      // Add search filter
      if (search) {
        query.andWhere('job.title LIKE :search', { search: `%${search}%` });
      }

      // Pagination
      query.offset(offset).limit(limit);

      const jobs = await query.getRawMany();
      const total = await query.getCount();

      return {
        success: true,
        data: {
          jobs,
          total,
          page: Number(page),
          limit: Number(limit),
          totalPages: Math.ceil(total / limit),
        },
      };
    } catch (error) {
      console.error('Error fetching recruiter AI jobs:', error);
      throw new Error('Failed to fetch AI interview jobs');
    }
  }

  // Get Applicants for specific AI Job with Scores
  async getRecruiterAiJobApplicants(
    userId: string,
    jobId: number,
    options: {
      page: number;
      limit: number;
    },
  ) {
    try {
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      // First get the company ID for this user to ensure ownership
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        throw new Error('Company not found for this user');
      }

      const query = this.dataSource
        .createQueryBuilder()
        .select([
          'app.id AS application_id',
          'app.status AS application_status',
          'u.full_name AS candidate_name',
          'u.email AS candidate_email',
          'u.profile_picture AS profile_picture',
          'app.applied_at AS applied_at',
          'ais.status AS interview_status',
          'ais.scheduled_at AS scheduled_at',
          'uai.id AS interview_id',
          'uai.score AS ai_score',
          'uai.total_questions AS total_questions',
          'uai.completed_at AS completed_at',
          'uai.skill_scores AS skill_scores',
          'uai.weak_topics AS weak_topics',
          'uai.strong_topics AS strong_topics',
          'uai.ai_feedback_json AS ai_feedback',
        ])
        .from('job_applications', 'app')
        .innerJoin(
          'job_posts',
          'job',
          'job.id = app.job_post_id AND job.company_id = :companyId',
          { companyId: company.id },
        )
        .leftJoin(
          'users',
          'u',
          'u.id = (SELECT user_id FROM seeker_profiles WHERE id = app.seeker_id)',
        )
        .leftJoin(
          'ai_interview_schedules',
          'ais',
          'ais.application_id = app.id',
        )
        .leftJoin('user_ai_interviews', 'uai', 'uai.schedule_id = ais.id')
        .where('app.job_post_id = :jobId', { jobId })
        .orderBy('uai.score', 'DESC')
        .addOrderBy('uai.completed_at', 'ASC')
        .offset(offset)
        .limit(limit);

      const applicants = await query.getRawMany();
      const total = await query.getCount();

      return {
        success: true,
        data: {
          applicants: applicants.map((app, index) => ({
            ...app,
            rank: offset + index + 1,
            skill_scores:
              typeof app.skill_scores === 'string'
                ? JSON.parse(app.skill_scores)
                : app.skill_scores,
            weak_topics:
              typeof app.weak_topics === 'string'
                ? JSON.parse(app.weak_topics)
                : app.weak_topics,
            strong_topics:
              typeof app.strong_topics === 'string'
                ? JSON.parse(app.strong_topics)
                : app.strong_topics,
            ai_feedback:
              typeof app.ai_feedback === 'string'
                ? JSON.parse(app.ai_feedback)
                : app.ai_feedback,
          })),
          total,
          page: Number(page),
          limit: Number(limit),
          totalPages: Math.ceil(total / limit),
        },
      };
    } catch (error) {
      console.error('Error fetching AI job applicants:', error);
      throw new Error('Failed to fetch AI interview applicants');
    }
  }

  // Get Detailed Interview Report (Questions & Answers)
  async getRecruiterAiInterviewDetails(userId: string, interviewId: number) {
    try {
      // First verify ownership via company -> job -> application -> interview chain
      // Or simpler: interview -> schedule -> job -> company -> user
      const interview = await this.dataSource
        .createQueryBuilder()
        .select([
          'uai.id',
          'uai.score',
          'uai.started_at',
          'uai.completed_at',
          'uai.skill_scores',
          'uai.weak_topics',
          'uai.strong_topics',
          'uai.ai_feedback_json',
          'uai.schedule_id',
        ])
        .from('user_ai_interviews', 'uai')
        .innerJoin('ai_interview_schedules', 'ais', 'ais.id = uai.schedule_id')
        .innerJoin('job_posts', 'job', 'job.id = ais.job_id')
        .innerJoin('companies', 'c', 'c.id = job.company_id')
        .where('uai.id = :interviewId', { interviewId })
        .andWhere('c.user_id = :userId', { userId })
        .getOne();

      if (!interview) {
        throw new Error('Interview not found or unauthorized');
      }

      // Fetch submission_meta from schedule
      try {
        if (interview && (interview as any).schedule_id) {
          const scheduleData = await this.dataSource.query(
            `SELECT submission_meta FROM ai_interview_schedules WHERE id = ?`,
            [(interview as any).schedule_id],
          );

          if (
            scheduleData &&
            scheduleData.length > 0 &&
            scheduleData[0].submission_meta
          ) {
            let meta = scheduleData[0].submission_meta;
            // Handle potential stringified JSON from raw query
            if (typeof meta === 'string') {
              try {
                meta = JSON.parse(meta);
              } catch (e) {
                console.warn('Failed to parse submission_meta JSON', e);
              }
            }
            (interview as any).submission_meta = meta;
          }
        }
      } catch (e) {
        console.error('Error fetching submission meta', e);
      }

      // Fetch Answers with Question Details and Options
      const answers = await this.dataSource
        .getRepository('UserAiAnswers')
        .createQueryBuilder('ua')
        .leftJoinAndSelect('ua.question', 'q')
        .leftJoinAndSelect('q.options', 'o')
        .where('ua.interview_id = :interviewId', { interviewId })
        .select([
          'ua.id',
          'ua.question_id',
          'ua.chosen_option_ids',
          'ua.is_correct',
          'q.question_text',
          'q.difficulty',
          'o.id',
          'o.option_text',
          'o.is_correct',
        ])
        .getMany();

      return {
        success: true,
        data: {
          interview,
          answers: answers.map((ans: any) => ({
            answer_id: ans.id,
            question_id: ans.question_id,
            chosen_option_ids: ans.chosen_option_ids,
            is_correct: ans.is_correct,
            question_text: ans.question?.question_text,
            difficulty: ans.question?.difficulty,
            skills: [], // q.skills does not exist, returning empty array or we could fetch primary_skill_id if needed
            // Map options to simple structure matching frontend expectation
            options:
              ans.question?.options?.map((opt) => ({
                id: opt.id,
                text: opt.option_text,
                is_correct: opt.is_correct,
              })) || [],
          })),
        },
      };
    } catch (error) {
      console.error('Error fetching AI interview details:', error);
      throw new Error('Failed to fetch interview details');
    }
  }

  async getRecruiterApplications(
    userId: string,
    options: { page: number; limit: number; status?: string; jobId?: string },
  ) {
    try {
      const { page, limit, status, jobId } = options;
      const offset = (page - 1) * limit;

      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'No company found for this user',
          data: { applications: [], total: 0, page, limit },
        };
      }

      // Build query with proper joins for Kanban data and DISTINCT to avoid duplicates
      const query = this.dataSource
        .createQueryBuilder()
        .select([
          'DISTINCT app.id AS application_id',
          'app.status AS status',
          'app.applied_at AS applied_at',
          'app.cover_letter AS cover_letter',
          'job.id AS job_id',
          'job.title AS job_title',
          'u.id AS user_id',
          'u.full_name AS candidate_name',
          'u.email AS candidate_email',
          'u.selected_role_category AS selected_role_category', // Added
          'u.profile_picture AS profile_picture',
          'sp.id AS seeker_id',
          'sp.professional_title AS professional_title',
          'sp.experiences_id AS experience_id',
          'sp.education_id AS education_id',
          'exp.level AS experience_name',
          'edu.name AS education_name',
          'sr.file_url AS cv_url',
          'sr.name AS cv_custom_name',
          'r.category AS profession_category', // Added
        ])
        .from('job_applications', 'app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .leftJoin('seeker_profiles', 'sp', 'sp.id = app.seeker_id')
        .leftJoin('users', 'u', 'u.id = sp.user_id')
        .leftJoin('roles', 'r', 'r.id = sp.profession_id') // Added join
        .leftJoin('experiences', 'exp', 'exp.id = sp.experiences_id')
        .leftJoin('education_levels', 'edu', 'edu.id = sp.education_id')
        .leftJoin(
          'seeker_resumes',
          'sr',
          'sr.id = app.resume_id AND sr.is_deleted = 0',
        )
        .where('job.company_id = :companyId', { companyId: company.id });

      if (status) {
        query.andWhere('app.status = :status', { status });
      }

      if (jobId) {
        query.andWhere('app.job_post_id = :jobId', { jobId });
      }

      const applications = await query
        .orderBy('app.applied_at', 'DESC')
        .skip(offset)
        .take(limit)
        .getRawMany();

      // Get count using DISTINCT to match the main query
      const countQuery = this.dataSource
        .createQueryBuilder()
        .select('COUNT(DISTINCT app.id)', 'count')
        .from('job_applications', 'app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .leftJoin('seeker_profiles', 'sp', 'sp.id = app.seeker_id')
        .leftJoin('users', 'u', 'u.id = sp.user_id')
        .leftJoin('experiences', 'exp', 'exp.id = sp.experiences_id')
        .leftJoin('education_levels', 'edu', 'edu.id = sp.education_id')
        .leftJoin(
          'seeker_resumes',
          'sr',
          'sr.id = app.resume_id AND sr.is_deleted = 0',
        )
        .where('job.company_id = :companyId', { companyId: company.id });

      if (status) {
        countQuery.andWhere('app.status = :status', { status });
      }

      if (jobId) {
        countQuery.andWhere('app.job_post_id = :jobId', { jobId });
      }

      const countResult = await countQuery.getRawOne();
      const total = parseInt(countResult.count) || 0;

      // Log application view activity (non-blocking)
      try {
        if (company && applications && applications.length > 0) {
          // Log that recruiter viewed applications
          await this.activityLogsService.logCompanyActivity({
            userId: Number(userId),
            companyId: company.id,
            actionType: 'application_view',
            actionName: 'Recruiter viewed applications',
            status: 'success',
            actionDetails: {
              applicationsCount: applications.length,
              page,
              limit,
              status: status || 'all',
              jobId: jobId || null,
            },
          });
        }
      } catch (logError) {
        // Silently fail - logging should never break main functionality
        console.warn('Failed to log application view activity:', logError);
      }

      // Process applications to add proper filename (custom name + extension)
      const processedApplications = applications.map((app) => {
        let cvFilename = '';
        if (app.cv_custom_name && app.cv_url) {
          const fileExt = path.extname(app.cv_url).toLowerCase() || '.pdf';
          cvFilename = app.cv_custom_name;
          // Add extension if not already present
          if (!cvFilename.toLowerCase().endsWith(fileExt)) {
            cvFilename += fileExt;
          }
        }

        // Return application with cv_filename instead of cv_custom_name
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const { cv_custom_name, ...restApp } = app;
        return {
          ...restApp,
          cv_filename: cvFilename,
        };
      });

      return {
        success: true,
        data: {
          applications: processedApplications,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch applications',
        error: error.message,
      };
    }
  }

  async updateApplicationStatus(
    userId: string,
    applicationId: string,
    status: string,
  ) {
    try {
      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'No company found for this user',
        };
      }

      // Validate that the application belongs to a job under this company
      const application = await this.dataSource
        .createQueryBuilder()
        .select('app.id')
        .from('job_applications', 'app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .where('app.id = :applicationId', { applicationId })
        .andWhere('job.company_id = :companyId', { companyId: company.id })
        .getOne();

      if (!application) {
        return {
          success: false,
          message:
            'Application not found or you do not have permission to update it',
        };
      }

      // Update the application status
      await this.applicationRepository
        .createQueryBuilder()
        .update()
        .set({ status })
        .where('id = :applicationId', { applicationId })
        .execute();

      // Send notification email to seeker for important status updates
      const notifyStatuses = ['Shortlisted', 'Interview', 'Hired', 'Rejected'];
      if (notifyStatuses.includes(status)) {
        const details = await this.dataSource
          .createQueryBuilder()
          .select([
            'ja.id as id',
            'ja.status as status',
            'jp.title as job_title',
            'c.name as company_name',
            'sp.id as seeker_id',
            'u.email as candidate_email',
            'u.full_name as candidate_name',
            'u.username as candidate_username',
            'sp.user_id as recipient_user_id',
            'city.city_name as city_name',
            'state.state_name as state_name',
          ])
          .from('job_applications', 'ja')
          .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
          .innerJoin('companies', 'c', 'c.id = jp.company_id')
          .innerJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
          .innerJoin('users', 'u', 'u.id = sp.user_id')
          .leftJoin('city', 'city', 'jp.city_id = city.city_id')
          .leftJoin('state', 'state', 'jp.state_id = state.state_id')
          .where('ja.id = :applicationId', { applicationId })
          .limit(1)
          .getRawOne();

        if (details) {
          // --- WhatsApp Notification Logic ---
          let templateName = '';
          const location =
            details.city_name && details.state_name
              ? `${details.city_name}, ${details.state_name}`
              : details.city_name || details.state_name || '';

          // Default payload
          let payload: any = {};

          let inAppMessage = '';
          switch (status) {
            case 'Shortlisted':
              templateName = 'candidate_shortlisted';
              payload = {
                candidate_name: details.candidate_name || 'Candidate',
                job_title: details.job_title || 'Job',
                job_location: location,
              };
              inAppMessage = `Congratulations! You have been shortlisted for the role of "${details.job_title || 'Job'}" in "${location || 'India'}".`;
              break;
            case 'Interview':
              templateName = 'candidate_interview_contact_expected';
              payload = {
                candidate_name: details.candidate_name || 'Candidate',
                job_title: details.job_title || 'Job',
                job_location: location,
              };
              inAppMessage = `Interview Update: The employer has reviewed your profile for "${details.job_title || 'Job'}" in "${location || 'India'}". They may contact you soon.`;
              break;
            case 'Rejected':
              templateName = 'candidate_application_rejected';
              payload = {
                candidate_name: details.candidate_name || 'Candidate',
                job_title: details.job_title || 'Job',
              };
              inAppMessage = `Thank you for applying to "${details.job_title || 'Job'}". The employer has decided to move forward with other candidates at this time.`;
              break;
          }

          if (templateName && details.recipient_user_id) {
            // Fire and forget
            this.notificationService
              .createEvent(
                templateName,
                details.recipient_user_id,
                'job_application',
                Number(applicationId),
                payload,
              )
              .catch((err) =>
                console.error(
                  'Failed to create notification event from dashboard',
                  err,
                ),
              );
          }

          if (inAppMessage && details.recipient_user_id) {
            this.userNotificationsService
              .createNotification(
                details.recipient_user_id,
                'application_status',
                inAppMessage
              )
              .catch((err) =>
                console.error(
                  'Failed to create in-app notification for candidate (dashboard update status)',
                  err,
                ),
              );
          }

          // --- Email Notification Logic ---
          if (details.candidate_email) {
            const preferredName =
              details.candidate_name ||
              details.candidate_username ||
              String(details.candidate_email).split('@')[0] ||
              'Candidate';

            const templateName = 'application_status_update';
            const context = {
              preferred_name: preferredName,
              job_title: details.job_title || '—',
              company_name: details.company_name || '—',
              status: status,
            };

            await this.emailService.sendTemplateEmail(
              templateName,
              details.candidate_email,
              preferredName,
              context
            );
          }
        }
      }

      return {
        success: true,
        data: { applicationId, status },
        message: 'Application status updated successfully',
        error: null,
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to update application status',
        error: error.message,
      };
    }
  }

  async getRecruiterMessages(
    userId: string,
    options: { page: number; limit: number },
  ) {
    try {
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      const messages = await this.messageRepository
        .createQueryBuilder('msg')
        .leftJoinAndSelect('msg.recipient_id', 'recipient')
        .where('msg.sender_id = :userId', { userId })
        .skip(offset)
        .take(limit)
        .getMany();

      const total = await this.messageRepository
        .createQueryBuilder('msg')
        .where('msg.sender_id = :userId', { userId })
        .getCount();

      return {
        success: true,
        data: {
          messages,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch messages',
        error: error.message,
      };
    }
  }

  async sendMessage(userId: string, sendMessageData: any) {
    try {
      const newMessage = this.messageRepository.create({
        sender_id: parseInt(userId),
        ...sendMessageData,
      });
      await this.messageRepository.save(newMessage);

      return {
        success: true,
        data: newMessage,
        message: 'Message sent successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to send message',
        error: error.message,
      };
    }
  }

  async getSavedCandidates(
    userId: string,
    options: { page: number; limit: number },
  ) {
    try {
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      // Get recruiter's company ID
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId: parseInt(userId) })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'Recruiter company not found',
          data: {
            candidates: [],
            pagination: {
              page,
              limit,
              total: 0,
              totalPages: 0,
            },
          },
        };
      }

      // Get saved candidates with candidate details
      const savedCandidates = await this.savedCandidateRepository
        .createQueryBuilder('saved')
        .where('saved.recruiter_id = :recruiterId', { recruiterId: company.id })
        .andWhere('saved.is_deleted = :isDeleted', { isDeleted: false })
        .skip(offset)
        .take(limit)
        .getMany();

      // Get candidate details for each saved candidate
      const candidatesWithDetails = await Promise.all(
        savedCandidates.map(async (saved) => {
          // Get seeker profile directly since saved.candidate_id references seeker_profiles.id
          const seekerProfile = await this.seekerProfileRepository
            .createQueryBuilder('profile')
            .where('profile.id = :candidateId', {
              candidateId: saved.candidate_id,
            })
            .getOne();

          // Get user details from the seeker profile
          const candidate = seekerProfile
            ? await this.userRepository
              .createQueryBuilder('user')
              .where('user.id = :userId', {
                userId: seekerProfile.user_id,
              })
              .getOne()
            : null;

          // Get applied jobs count for this candidate to this company
          const appliedJobsCount = seekerProfile
            ? await this.applicationRepository
              .createQueryBuilder('app')
              .innerJoin('job_posts', 'job', 'job.id = app.job_post_id')
              .where('app.seeker_id = :seekerId', {
                seekerId: seekerProfile.id,
              })
              .andWhere('job.company_id = :companyId', {
                companyId: company.id,
              })
              .getCount()
            : 0;

          // Get latest application status
          const latestApplication = seekerProfile
            ? await this.applicationRepository
              .createQueryBuilder('app')
              .where('app.seeker_id = :seekerId', {
                seekerId: seekerProfile.id,
              })
              .orderBy('app.applied_at', 'DESC')
              .getOne()
            : null;

          // Get latest resume for this candidate
          const latestResume = seekerProfile
            ? await this.seekerResumeRepository
              .createQueryBuilder('resume')
              .where('resume.seeker_id = :seekerId', {
                seekerId: seekerProfile.id,
              })
              .andWhere('resume.is_deleted = :isDeleted', {
                isDeleted: false,
              })
              .orderBy('resume.uploaded_at', 'DESC')
              .getOne()
            : null;

          // Get file extension for the CV
          let cvFilename = '';
          if (latestResume) {
            const fileExt = latestResume.file_url
              ? path.extname(latestResume.file_url).toLowerCase()
              : '.pdf';
            cvFilename = latestResume.name || 'CV';
            // Add extension if not already present
            if (!cvFilename.toLowerCase().endsWith(fileExt)) {
              cvFilename += fileExt;
            }
          }

          // Check if profile is unlocked logic
          const isUnlockedResult = seekerProfile
            ? await this.dataSource.query(
              `SELECT 1 FROM unlocked_profiles 
             WHERE recruiter_user_id = ? AND seeker_user_id = ? 
             AND is_active = 1 
             AND (expires_at IS NULL OR expires_at > NOW())
             LIMIT 1`,
              [userId, seekerProfile.user_id],
            )
            : [];

          const isUnlocked = isUnlockedResult.length > 0;

          // Mask data if not unlocked
          const email = candidate?.email || '';
          const maskedEmail = isUnlocked
            ? email
            : email.replace(/(.{2})(.*)(@.*)/, '$1***$3');

          // CV logic
          const hasCv = !!latestResume;
          const cvUrl =
            isUnlocked && latestResume?.file_url ? latestResume.file_url : '';

          return {
            saved_id: saved.id,
            user_id: candidate?.id,
            seeker_id: seekerProfile?.id,
            candidate_name: candidate?.fullName || 'Unknown',
            candidate_email: maskedEmail, // Masked if locked
            professional_title: seekerProfile?.professional_title || '',
            experience_name: '', // Will be populated from experience entity if needed
            education_name: '', // Will be populated from education entity if needed
            profile_picture: candidate?.profilePicture || '',
            applied_jobs_count: appliedJobsCount,
            latest_application_status: latestApplication?.status || '',
            cv_url: cvUrl, // Empty if locked
            has_cv: hasCv, // New field to indicate if CV exists
            is_unlocked: isUnlocked, // New field
            cv_filename: cvFilename, // Use custom name (resume.name) with proper extension
            saved_at: saved.created_at,
            notes: saved.notes,
            status: saved.status,
          };
        }),
      );

      const total = await this.savedCandidateRepository
        .createQueryBuilder('saved')
        .where('saved.recruiter_id = :recruiterId', { recruiterId: company.id })
        .andWhere('saved.is_deleted = :isDeleted', { isDeleted: false })
        .getCount();

      return {
        success: true,
        data: {
          candidates: candidatesWithDetails,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch saved candidates',
        error: error.message,
      };
    }
  }

  async saveCandidate(userId: string, candidateId: string) {
    try {
      /* console.log(
        'Saving candidate - userId:',
        userId,
        'candidateId:',
        candidateId,
      );*/

      // Get recruiter's company ID
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId: parseInt(userId) })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'Recruiter company not found',
        };
      }

      // Get candidate's seeker profile ID
      const seekerProfile = await this.seekerProfileRepository
        .createQueryBuilder('profile')
        .where('profile.user_id = :candidateId', {
          candidateId: parseInt(candidateId),
        })
        .getOne();

      if (!seekerProfile) {
        return {
          success: false,
          message: 'Candidate profile not found',
        };
      }

      // Check if already saved (including soft-deleted records)
      const existing = await this.savedCandidateRepository
        .createQueryBuilder('saved')
        .where('saved.recruiter_id = :recruiterId', {
          recruiterId: company.id,
        })
        .andWhere('saved.candidate_id = :seekerId', {
          seekerId: seekerProfile.id,
        })
        .getOne();

      if (existing) {
        // If record exists but is soft-deleted, restore it
        if (existing.is_deleted) {
          //console.log('Restoring soft-deleted candidate record');
          await this.savedCandidateRepository
            .createQueryBuilder()
            .update()
            .set({ is_deleted: false })
            .where('id = :id', { id: existing.id })
            .execute();

          return {
            success: true,
            message: 'Candidate restored successfully',
          };
        } else {
          //console.log('Candidate already saved, returning success');
          return {
            success: true,
            message: 'Candidate already saved',
          };
        }
      }

      // Create new record if it doesn't exist
      const savedCandidate = this.savedCandidateRepository.create({
        recruiter_id: company.id,
        candidate_id: seekerProfile.id,
        is_deleted: false,
      });

      await this.savedCandidateRepository.save(savedCandidate);

      return {
        success: true,
        message: 'Candidate saved successfully',
      };
    } catch (error) {
      console.error('Error saving candidate:', error);
      return {
        success: false,
        message: 'Failed to save candidate',
        error: error.message,
      };
    }
  }

  async removeSavedCandidate(userId: string, candidateId: string) {
    try {
      /*console.log(
        'Removing saved candidate - userId:',
        userId,
        'candidateId:',
        candidateId,
        );*/

      // Get recruiter's company ID
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId: parseInt(userId) })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'Recruiter company not found',
        };
      }

      // Get candidate's seeker profile ID
      const seekerProfile = await this.seekerProfileRepository
        .createQueryBuilder('profile')
        .where('profile.user_id = :candidateId', {
          candidateId: parseInt(candidateId),
        })
        .getOne();

      if (!seekerProfile) {
        return {
          success: false,
          message: 'Candidate profile not found',
        };
      }

      await this.savedCandidateRepository
        .createQueryBuilder()
        .update()
        .set({ is_deleted: true })
        .where('recruiter_id = :recruiterId AND candidate_id = :seekerId', {
          recruiterId: company.id,
          seekerId: seekerProfile.id,
        })
        .execute();

      // console.log('Remove result:', result);

      return {
        success: true,
        message: 'Candidate removed from saved list',
      };
    } catch (error) {
      console.error('Error removing saved candidate:', error);
      return {
        success: false,
        message: 'Failed to remove saved candidate',
        error: error.message,
      };
    }
  }

  async isCandidateSaved(userId: string, candidateId: string) {
    try {
      // Get recruiter's company ID
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId: parseInt(userId) })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'Recruiter company not found',
          isSaved: false,
        };
      }

      // Get candidate's seeker profile ID
      const seekerProfile = await this.seekerProfileRepository
        .createQueryBuilder('profile')
        .where('profile.user_id = :candidateId', {
          candidateId: parseInt(candidateId),
        })
        .getOne();

      if (!seekerProfile) {
        return {
          success: false,
          message: 'Candidate profile not found',
          isSaved: false,
        };
      }

      const savedCandidate = await this.savedCandidateRepository
        .createQueryBuilder('saved')
        .where('saved.recruiter_id = :recruiterId', { recruiterId: company.id })
        .andWhere('saved.candidate_id = :seekerId', {
          seekerId: seekerProfile.id,
        })
        .andWhere('saved.is_deleted = :isDeleted', { isDeleted: false })
        .getOne();

      return {
        success: true,
        isSaved: !!savedCandidate,
        savedCandidateId: savedCandidate?.id || null,
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to check saved status',
        error: error.message,
      };
    }
  }

  // Analytics Methods
  async getJobViews(userId: string, jobId: string, period: string) {
    try {
      // Mock data - replace with actual QueryBuilder queries
      const views = {
        total: 320,
        daily: [45, 52, 48, 61, 55, 59],
        weekly: [320, 280, 350, 290],
        period,
      };

      return {
        success: true,
        data: views,
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch job views',
        error: error.message,
      };
    }
  }

  async getApplicationStats(userId: string, period: string) {
    try {
      // Mock data - replace with actual QueryBuilder queries
      const stats = {
        total: 127,
        byStatus: {
          'Under Review': 45,
          Interview: 28,
          Hired: 12,
          Rejected: 42,
        },
        byPeriod: [45, 52, 48, 61, 55, 59],
        period,
      };

      return {
        success: true,
        data: stats,
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch application stats',
        error: error.message,
      };
    }
  }

  async getCandidateStats(userId: string, period: string) {
    try {
      // Mock data - replace with actual QueryBuilder queries
      const stats = {
        totalApplied: 25,
        totalFavorites: 12,
        totalAlerts: 5,
        byStatus: {
          'Under Review': 8,
          Interview: 5,
          Hired: 2,
          Rejected: 10,
        },
        byPeriod: [8, 5, 7, 3, 6, 4],
        period,
      };

      return {
        success: true,
        data: stats,
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch candidate stats',
        error: error.message,
      };
    }
  }

  // Get Seeker ID (seeker_profiles.id) by userId using QueryBuilder
  async getSeekerIdByUserId(userId: number): Promise<number | null> {
    const result = await this.seekerProfileRepository
      .createQueryBuilder('profile')
      .select('profile.id', 'id')
      .where('profile.user_id = :userId', { userId })
      .getRawOne();
    return result ? result.id : null;
  }

  // Soft delete a seeker resume
  async softDeleteSeekerResume(userId: number, resumeId: number) {
    // Find the seeker profile for this user
    const seekerProfile = await this.seekerProfileRepository.findOne({
      where: { user_id: userId },
    });
    if (!seekerProfile) {
      return { success: false, message: 'Seeker profile not found' };
    }
    // Find the resume and ensure it belongs to this seeker
    const resume = await this.seekerResumeRepository.findOne({
      where: { id: resumeId, seeker_id: seekerProfile.id, is_deleted: false },
    });
    if (!resume) {
      return { success: false, message: 'Resume not found or already deleted' };
    }
    resume.is_deleted = true;
    await this.seekerResumeRepository.save(resume);
    return { success: true, message: 'Resume soft deleted successfully' };
  }

  // async updateSeekerProfileBasicInfo(profileId: number, data: any) {
  //   // Update seeker_profiles
  //   const profile = await this.seekerProfileRepository.findOne({
  //     where: { id: profileId },
  //   });
  //   if (!profile) {
  //     return { success: false, message: 'Seeker profile not found' };
  //   }
  //   profile.professional_title = data.professional_title;
  //   profile.experiences_id = data.experiences_id;
  //   profile.education_id = data.education_id;
  //   profile.personal_website = data.personal_website;
  //   profile.date_of_birth = data.date_of_birth;
  //   await this.seekerProfileRepository.save(profile);

  //   // Update full_name in users table if provided and different
  //   if (data.fullName) {
  //     const user = await this.userRepository.findOne({
  //       where: { id: profile.user_id },
  //     });
  //     if (user && user.fullName !== data.fullName) {
  //       user.fullName = data.fullName;
  //       await this.userRepository.save(user);
  //     }
  //   }

  //   return { success: true, profile };
  // }
  async updateSeekerProfileBasicInfo(profileId: number, data: any) {
    console.log(
      ' [UPDATE PROFILE] Starting profile update for profile ID:',
      profileId,
    );
    console.log(' [UPDATE PROFILE] Data received:', data);

    // Find the profile
    const profile = await this.seekerProfileRepository.findOne({
      where: { id: profileId },
    });
    if (!profile) {
      return { success: false, message: 'Seeker profile not found' };
    }

    // Update user fields in users table if provided - MOVED TO TOP to ensure execution
    if (
      'fullName' in data ||
      'phone' in data ||
      'email' in data ||
      'onboarding_status' in data ||
      'selected_role_category' in data ||
      'onboarding_step' in data
    ) {
      const user = await this.userRepository.findOne({
        where: { id: profile.user_id },
      });
      if (user) {
        let userUpdated = false;
        const previousEmail = user.email;
        if (
          'fullName' in data &&
          data.fullName &&
          user.fullName !== data.fullName
        ) {
          user.fullName = data.fullName;
          userUpdated = true;
        }
        if ('phone' in data && data.phone && user.phone !== data.phone) {
          user.phone = data.phone;
          userUpdated = true;
        }
        if ('email' in data && data.email && user.email !== data.email) {
          console.log(
            ` [UPDATE PROFILE] Updating email from ${user.email} to ${data.email}`,
          );
          user.email = data.email;
          userUpdated = true;
        }
        if ('selected_role_category' in data) {
          user.selectedRoleCategory = data.selected_role_category;
          userUpdated = true;
        }
        if ('onboarding_step' in data) {
          user.onboardingStep = data.onboarding_step;
          userUpdated = true;
        }
        if ('onboarding_status' in data && data.onboarding_status) {
          user.onboardingStatus = data.onboarding_status;
          if (data.onboarding_status === 'completed') {
            user.onboardingStep = null;
          }
          userUpdated = true;
        }
        if (userUpdated) {
          await this.userRepository.save(user);
          if (!previousEmail && user.email) {
            this.triggerWelcomeEmail(user).catch((err) =>
              console.error('Error triggering onboarding welcome email:', err),
            );
          }
        }
      }
    }

    // Only update fields that are present in data
    if ('professional_title' in data)
      profile.professional_title = data.professional_title;
    if ('experiences_id' in data) profile.experiences_id = data.experiences_id;
    if ('education_id' in data) profile.education_id = data.education_id;
    if ('personal_website' in data)
      profile.personal_website = data.personal_website;
    if ('date_of_birth' in data) profile.date_of_birth = data.date_of_birth;
    if ('gender' in data) profile.gender = data.gender;
    if ('marital_status' in data) profile.marital_status = data.marital_status;
    if ('profession_id' in data) profile.profession_id = data.profession_id;
    if ('availability' in data) profile.your_availability = data.availability;
    if ('biography' in data) profile.bio_graphy = data.biography;
    if ('skills' in data && Array.isArray(data.skills)) {
      // console.log(' [UPDATE PROFILE] Updating skills_ids JSON field to:', data.skills);
      profile.skills_ids = data.skills;
    }
    if ('languages' in data && Array.isArray(data.languages)) {
      // console.log(' [UPDATE PROFILE] Updating languages_ids JSON field to:', data.languages);
      profile.languages_ids = data.languages;
    }

    // Blue Collar Specific Fields
    // Handle both camelCase (frontend) and snake_case (sometimes sent by other endpoints)
    if ('preferredWorkCityId' in data) {
      profile.preferred_work_city_id = data.preferredWorkCityId
        ? parseInt(data.preferredWorkCityId)
        : null;
    } else if ('preferred_work_city_id' in data) {
      profile.preferred_work_city_id = data.preferred_work_city_id
        ? parseInt(data.preferred_work_city_id)
        : null;
    }

    if ('preferredWorkLocalityId' in data) {
      profile.preferred_work_locality_area_id = data.preferredWorkLocalityId
        ? parseInt(data.preferredWorkLocalityId)
        : null;
    } else if ('preferred_work_locality_area_id' in data) {
      profile.preferred_work_locality_area_id =
        data.preferred_work_locality_area_id
          ? parseInt(data.preferred_work_locality_area_id)
          : null;
    }

    await this.seekerProfileRepository.save(profile);
    // console.log(' [UPDATE PROFILE] Profile saved successfully');

    // Update skills if provided
    if ('skills' in data && Array.isArray(data.skills)) {
      // console.log(' [UPDATE PROFILE] Calling setSeekerSkills with:', data.skills);
      await this.setSeekerSkills(profile.id, data.skills);
    }
    // Update languages if provided
    if ('languages' in data && Array.isArray(data.languages)) {
      // console.log(' [UPDATE PROFILE] Calling setSeekerLanguages with:', data.languages);
      await this.setSeekerLanguages(profile.id, data.languages);
    }

    // Update experiences for white-collar applicants
    if ('experiences' in data && Array.isArray(data.experiences)) {
      console.log(
        ' [UPDATE PROFILE] Updating experiences for seeker_id:',
        profile.id,
      );
      await this.seekerExperienceRepository.delete({ seeker_id: profile.id });
      if (data.experiences.length > 0) {
        const newExperiences = data.experiences.map((exp: any) => ({
          seeker_id: profile.id,
          company: exp.company || '',
          department: exp.department || '',
          designation: exp.designation || '',
          start_date: exp.startDate || exp.start_date || null,
          end_date:
            exp.current || exp.is_current
              ? null
              : exp.endDate || exp.end_date || null,
          current: exp.current || exp.is_current ? 1 : 0,
          responsibilities: exp.responsibilities || '',
        }));
        await this.seekerExperienceRepository.save(newExperiences);
      }
    }

    // Update educations for white-collar applicants
    if ('educations' in data && Array.isArray(data.educations)) {
      console.log(
        ' [UPDATE PROFILE] Updating educations for seeker_id:',
        profile.id,
      );
      await this.seekerEducationRepository.delete({ seeker_id: profile.id });
      if (data.educations.length > 0) {
        const newEducations = data.educations.map((edu: any) => ({
          seeker_id: profile.id,
          education_id:
            edu.education_level_id ||
            edu.educationId ||
            edu.education_id ||
            null,
          degree: edu.degree || '',
          year: edu.year || null,
          notes: edu.notes || '',
        }));
        await this.seekerEducationRepository.save(newEducations);
      }
    }

    // Update role experiences for blue-collar applicants
    if ('roleExperiences' in data && Array.isArray(data.roleExperiences)) {
      console.log(
        ' [UPDATE ROLE EXP] Deleting existing role experiences for seeker_id:',
        profile.id,
      );

      // Delete existing role experiences
      const deleteResult = await this.seekerRoleExperienceRepository.delete({
        seeker_id: profile.id,
      });
      console.log(' [UPDATE ROLE EXP] Delete result:', deleteResult);

      // Insert new role experiences
      if (data.roleExperiences.length > 0) {
        const roleExperiencesToSave = data.roleExperiences.map((re: any) => {
          const rawDesignation =
            re.designation_id !== undefined && re.designation_id !== null
              ? re.designation_id
              : re.roleId !== undefined && re.roleId !== null
                ? re.roleId
                : null;
          const rawExperience =
            re.experience_id !== undefined && re.experience_id !== null
              ? re.experience_id
              : re.experienceId !== undefined && re.experienceId !== null
                ? re.experienceId
                : null;

          return {
            seeker_id: profile.id,
            designation_id:
              rawDesignation !== null && rawDesignation !== ''
                ? parseInt(String(rawDesignation))
                : null,
            experience_id:
              rawExperience !== null && rawExperience !== ''
                ? parseInt(String(rawExperience))
                : null,
            status: 1,
          };
        });
        console.log(
          ' [UPDATE ROLE EXP] Inserting new role experiences:',
          roleExperiencesToSave,
        );
        try {
          const saveResult = await this.seekerRoleExperienceRepository.save(
            roleExperiencesToSave,
          );
          console.log(' [UPDATE ROLE EXP] Save result:', saveResult);
        } catch (error) {
          console.error(
            ' [UPDATE ROLE EXP] Error saving role experiences:',
            error,
          );
        }
      }
    }

    // Update Address (Mirroring user.service.ts logic)
    // Determine category to decide if address should be saved or removed
    let currentCategory = data.selected_role_category;
    if (!currentCategory) {
      // Fallback to fetching user if not in data payload
      const userForCategory = await this.userRepository.findOne({
        where: { id: profile.user_id },
      });
      currentCategory = userForCategory?.selectedRoleCategory;
    }

    if (currentCategory === 'blue_collar') {
      // For blue-collar, remove address if it exists
      console.log(
        ' [UPDATE PROFILE] Blue collar category detected. Removing address for seeker_id:',
        profile.id,
      );
      await this.seekerAddressRepository.delete({
        seeker_profile_id: profile.id,
      });
    } else if (data.permanentAddress || data.permanentCityId) {
      console.log(
        ' [UPDATE PROFILE] Updating address for seeker_id:',
        profile.id,
      );

      // If sameAsPermanent is true, copy permanent address to communication address
      await this.seekerAddressService.upsertAddress(profile.id, data);
    }

    return { success: true, profile };
  }
  async getSeekerProfileByUserId(userId: number) {
    const profile = await this.seekerProfileRepository.findOne({
      where: { user_id: userId },
    });
    if (!profile) {
      return { success: false, message: 'Seeker profile not found' };
    }
    try {
      const roleExperiences = await this.seekerRoleExperienceRepository.find({
        where: { seeker_id: profile.id, status: 1 },
      });
      (profile as any).role_experiences = roleExperiences;
    } catch (err) {
      // Non-fatal: log and continue returning profile without role_experiences
      console.error('Failed to load seeker role experiences:', err);
    }

    return { success: true, profile };
  }

  // --- Seeker Experiences CRUD ---
  async getSeekerExperiences(seeker_id: number) {
    return this.seekerExperienceRepository.find({ where: { seeker_id } });
  }
  async addSeekerExperience(data: any) {
    const exp = this.seekerExperienceRepository.create({
      ...data,
      start_date: data.startDate || data.start_date,
      end_date: data.current ? null : data.endDate || data.end_date || null,
      current: data.current ? 1 : 0,
    });
    return this.seekerExperienceRepository.save(exp);
  }
  async updateSeekerExperience(id: number, data: any) {
    const updateData = {
      ...data,
      start_date: data.startDate || data.start_date,
      end_date: data.current ? null : data.endDate || data.end_date || null,
      current: data.current ? 1 : 0,
    };
    await this.seekerExperienceRepository.update(id, updateData);
    return this.seekerExperienceRepository.findOne({ where: { id } });
  }
  async deleteSeekerExperience(id: number) {
    await this.seekerExperienceRepository.delete(id);
  }
  // --- Seeker Educations CRUD ---
  async getSeekerEducations(seeker_id: number) {
    return this.seekerEducationRepository.find({ where: { seeker_id } });
  }
  async addSeekerEducation(data: any) {
    // Map education_level_id from API to education_id for database
    const dbData = {
      ...data,
      education_id: data.education_level_id,
    };
    // Remove education_level_id as it doesn't exist in database
    delete dbData.education_level_id;

    if ('degree_id' in dbData) {
      const degId = Number(dbData.degree_id);
      dbData.degree_id = (isNaN(degId) || degId <= 0) ? null : degId;
    }
    if ('specialization_id' in dbData) {
      const specId = Number(dbData.specialization_id);
      dbData.specialization_id = (isNaN(specId) || specId <= 0) ? null : specId;
    }

    const edu = this.seekerEducationRepository.create(dbData);
    return this.seekerEducationRepository.save(edu);
  }
  async updateSeekerEducation(id: number, data: any) {
    // Map education_level_id from API to education_id for database
    const dbData = {
      ...data,
      education_id: data.education_level_id,
    };
    // Remove education_level_id as it doesn't exist in database
    delete dbData.education_level_id;

    if ('degree_id' in dbData) {
      const degId = Number(dbData.degree_id);
      dbData.degree_id = (isNaN(degId) || degId <= 0) ? null : degId;
    }
    if ('specialization_id' in dbData) {
      const specId = Number(dbData.specialization_id);
      dbData.specialization_id = (isNaN(specId) || specId <= 0) ? null : specId;
    }

    await this.seekerEducationRepository.update(id, dbData);
    return this.seekerEducationRepository.findOne({ where: { id } });
  }
  async deleteSeekerEducation(id: number) {
    await this.seekerEducationRepository.delete(id);
  }

  // --- Social Platform CRUD ---
  async getSocialPlatforms() {
    return this.socialPlatformRepository.find();
  }
  async getSeekerSocialLinks(seeker_id: number) {
    return this.seekerSocialLinksRepository.find({ where: { seeker_id } });
  }
  async addSeekerSocialLink(data: any) {
    const link = this.seekerSocialLinksRepository.create(data);
    return this.seekerSocialLinksRepository.save(link);
  }
  async updateSeekerSocialLink(id: number, data: any) {
    await this.seekerSocialLinksRepository.update(id, data);
    return this.seekerSocialLinksRepository.findOne({ where: { id } });
  }
  async deleteSeekerSocialLink(id: number) {
    await this.seekerSocialLinksRepository.delete(id);
  }
  // ---address CRUD ---

  async getLocations(status?: number) {
    try {
      const query = this.dataSource
        .createQueryBuilder()
        .select([
          'city.city_id AS id',
          'city.city_name AS city',
          'state.state_id AS state_id',
          'state.state_name AS state',
          'country.country_id AS country_id',
          'country.country_name AS country',
          "CONCAT(city.city_name, ', ', state.state_name, ', ', country.country_name) AS full_location",
        ])
        .from('country', 'country');

      if (status !== undefined) {
        query.innerJoin(
          'state',
          'state',
          'state.country_id = country.country_id AND state.status = :status',
          { status },
        );
        query.innerJoin(
          'city',
          'city',
          'city.state_id = state.state_id AND city.status = :status',
          { status },
        );
        query.where('country.status = :status', { status });
      } else {
        query.innerJoin(
          'state',
          'state',
          'state.country_id = country.country_id AND state.status = 1',
        );
        query.innerJoin(
          'city',
          'city',
          'city.state_id = state.state_id AND city.status = 1',
        );
        query.where('country.status = 1');
      }

      const result = await query
        .orderBy('country.country_name', 'ASC')
        .addOrderBy('state.state_name', 'ASC')
        .addOrderBy('city.city_name', 'ASC')
        .getRawMany();
      return result;
    } catch (error) {
      console.error('Error fetching locations:', error);
      throw new Error(`Failed to fetch locations: ${error.message}`);
    }
  }
  //--industriesrolelevelrole crud--
  async getIndustriesRoleLevelRole() {
    try {
      const result = await this.dataSource
        .createQueryBuilder()
        .select([
          'industries.id AS industry_id',
          'industries.name AS industry_name',
          'role_levels.id AS role_level_id',
          'role_levels.name AS role_level_name',
          'roles.id AS role_id',
          'roles.name AS role_name',
          "CONCAT(industries.name, ' , ', role_levels.name, ' , ', roles.name) AS full_IndustriesRoleLevelRole",
        ])
        .from('roles', 'roles')
        .leftJoin(
          'role_levels',
          'role_levels',
          'role_levels.id = roles.role_levels_id',
        )
        .leftJoin(
          'industries',
          'industries',
          'industries.id = role_levels.industry_id',
        )
        .where('roles.status = 1')
        .andWhere('role_levels.status = 1')
        .andWhere('industries.status = 1')
        .orderBy('industries.name', 'ASC')
        .addOrderBy('role_levels.name', 'ASC')
        .addOrderBy('roles.name', 'ASC')
        .getRawMany();

      return result;
    } catch (error) {
      console.error('Error fetching industries-role-level-role:', error);
      throw new Error(
        `Failed to fetch industries-role-level-role: ${error.message}`,
      );
    }
  }

  // --- Seeker Address CRUD ---
  async getSeekerAddress(seeker_profile_id: number) {
    try {
      const address = await this.dataSource
        .createQueryBuilder()
        .select([
          'sa.*',
          'perm_area.area as permanent_area_name',
          'perm_city.city_name as permanent_city_name',
          'perm_state.state_name as permanent_state_name',
          'perm_country.country_name as permanent_country_name',
          'comm_area.area as communication_area_name',
          'comm_city.city_name as communication_city_name',
          'comm_state.state_name as communication_state_name',
          'comm_country.country_name as communication_country_name',
        ])
        .from('seeker_address', 'sa')
        .leftJoin('area', 'perm_area', 'perm_area.id = sa.permanent_area_id')
        .leftJoin(
          'city',
          'perm_city',
          'perm_city.city_id = sa.permanent_city_id',
        )
        .leftJoin(
          'state',
          'perm_state',
          'perm_state.state_id = sa.permanent_state_id',
        )
        .leftJoin(
          'country',
          'perm_country',
          'perm_country.country_id = sa.permanent_country_id',
        )
        .leftJoin(
          'area',
          'comm_area',
          'comm_area.id = sa.communication_area_id',
        )
        .leftJoin(
          'city',
          'comm_city',
          'comm_city.city_id = sa.communication_city_id',
        )
        .leftJoin(
          'state',
          'comm_state',
          'comm_state.state_id = sa.communication_state_id',
        )
        .leftJoin(
          'country',
          'comm_country',
          'comm_country.country_id = sa.communication_country_id',
        )
        .where('sa.seeker_profile_id = :seeker_profile_id', {
          seeker_profile_id,
        })
        .getRawOne();

      if (address) {
        return { success: true, address };
      } else {
        return { success: true, address: null };
      }
    } catch (error) {
      console.error('Error getting seeker address:', error);
      return { success: false, error: error.message };
    }
  }
  async addSeekerAddress(data: any) {
    try {
      const address = this.seekerAddressRepository.create({
        seeker_profile_id: data.seeker_profile_id,
        permanent_address: data.permanent_address,
        permanent_country_id: data.permanent_country_id,
        permanent_state_id: data.permanent_state_id,
        permanent_city_id: data.permanent_city_id,
        permanent_pincode: data.permanent_pincode,
        permanent_area_id: data.permanent_area_id || null,
        communication_address: data.communication_address,
        communication_country_id: data.communication_country_id,
        communication_state_id: data.communication_state_id,
        communication_city_id: data.communication_city_id,
        communication_pincode: data.communication_pincode,
        communication_area_id: data.communication_area_id || null,
        same_as_permanent: data.same_as_permanent,
      });
      const savedAddress = await this.seekerAddressRepository.save(address);
      return { success: true, address: savedAddress };
    } catch (error) {
      console.error('Error adding seeker address:', error);
      return { success: false, error: error.message };
    }
  }
  async updateSeekerAddress(id: number, data: any) {
    try {
      // Check if address exists before updating
      const existingAddress = await this.seekerAddressRepository.findOne({
        where: { id },
      });
      if (!existingAddress) {
        return { success: false, error: 'Address not found' };
      }

      // Explicitly merge and save to ensure all fields are updated
      const updatedAddress = this.seekerAddressRepository.merge(
        existingAddress,
        {
          seeker_profile_id: data.seeker_profile_id,
          permanent_address: data.permanent_address,
          permanent_country_id: data.permanent_country_id,
          permanent_state_id: data.permanent_state_id,
          permanent_city_id: data.permanent_city_id,
          permanent_pincode: data.permanent_pincode,
          permanent_area_id:
            data.permanent_area_id !== undefined
              ? data.permanent_area_id
              : existingAddress.permanent_area_id,
          communication_address: data.communication_address,
          communication_country_id: data.communication_country_id,
          communication_state_id: data.communication_state_id,
          communication_city_id: data.communication_city_id,
          communication_pincode: data.communication_pincode,
          communication_area_id:
            data.communication_area_id !== undefined
              ? data.communication_area_id
              : existingAddress.communication_area_id,
          same_as_permanent: data.same_as_permanent,
        },
      );

      const savedAddress =
        await this.seekerAddressRepository.save(updatedAddress);
      return { success: true, address: savedAddress };
    } catch (error) {
      console.error('Error updating seeker address:', error);
      return { success: false, error: error.message };
    }
  }
  async deleteSeekerAddress(id: number) {
    await this.seekerAddressRepository.delete(id);
  }
  // --- Seeker contact CRUD ---
  async updateUserContactInfo(userId: number, payload: any) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      return { success: false, error: 'User not found' };
    }

    const isSame =
      user.phone === payload.phone &&
      user.secondary_phone === payload.secondary_phone &&
      user.whatsapp_number === payload.whatsapp_number &&
      user.fullName === payload.fullName &&
      user.email === payload.email; // include email

    if (isSame) {
      return { success: true, user };
    }

    const previousEmail = user.email;

    if (payload.phone !== undefined && user.phone !== payload.phone)
      user.phone = payload.phone;
    if (
      payload.secondary_phone !== undefined &&
      user.secondary_phone !== payload.secondary_phone
    )
      user.secondary_phone = payload.secondary_phone;
    if (
      payload.whatsapp_number !== undefined &&
      user.whatsapp_number !== payload.whatsapp_number
    )
      user.whatsapp_number = payload.whatsapp_number;
    if (payload.fullName !== undefined && user.fullName !== payload.fullName)
      user.fullName = payload.fullName;
    if (payload.email !== undefined && user.email !== payload.email) {
      user.email = payload.email;
      // Set username to email value (username||email pattern)
      user.username = payload.email;
    }
    // Also handle explicit username updates if provided
    if (payload.username !== undefined && user.username !== payload.username) {
      user.username = payload.username;
    }

    await this.userRepository.save(user);

    if (!previousEmail && user.email) {
      this.triggerWelcomeEmail(user).catch((err) =>
        console.error('Error triggering onboarding welcome email:', err),
      );
    }

    return { success: true, user };
  }

  private async triggerWelcomeEmail(user: UserEntity) {
    try {
      const preferredName =
        user.fullName || user.username || user.email.split('@')[0];
      const isRecruiter = user.role === 'recruiter';

      let templateName = 'registration_welcome_seeker';
      let companyName = '';

      if (isRecruiter) {
        // Fetch linked company name for recruiter
        const [company] = await this.dataSource.query(
          'SELECT name FROM companies WHERE user_id = ? LIMIT 1',
          [user.id],
        );
        companyName = company?.name || '';
        templateName =
          user.status === 'active'
            ? 'registration_welcome_recruiter_approved'
            : 'registration_welcome_recruiter_pending';
      }

      const success = await this.emailService.sendTemplateEmail(
        templateName,
        user.email,
        preferredName,
        {
          preferredName,
          companyName,
          username: user.username || '—',
          email: user.email,
        },
      );

      // Log dispatch to database
      await this.dataSource.query(
        `INSERT INTO email_sending_logs (recipient_email, recipient_name, recipient_type, email_type, status, reason)
         VALUES (?, ?, ?, ?, ?, ?)`,
        [
          user.email,
          preferredName,
          user.role,
          templateName,
          success ? 'sent' : 'failed',
          success
            ? 'Successfully delivered via Brevo API'
            : 'Brevo email relay dispatch failed',
        ],
      );
    } catch (err) {
      console.error(
        'Error inside triggerWelcomeEmail for user ' + user.id + ':',
        err,
      );
    }
  }
  async getUserContactInfo(userId: number) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      return { success: false, error: 'User not found' };
    }
    return { success: true, user };
  }
  // --- Seeker password CRUD ---
  async updateUserPassword(userId: number, payload: any) {
    try {
      const user = await this.userRepository.findOne({ where: { id: userId } });
      if (!user) {
        return { success: false, error: 'User not found' };
      }

      // Check if OTP verification is required
      if (payload.requireOTP && !payload.otp) {
        return { success: false, error: 'OTP verification required' };
      }

      // If OTP is provided, verify it
      if (payload.otp) {
        const otpVerification = await this.verifyOTPForPasswordChange(
          userId,
          payload.otp,
        );
        if (!otpVerification.success) {
          return { success: false, error: otpVerification.message };
        }
      }

      if (!payload.newPassword || !payload.confirmPassword) {
        return {
          success: false,
          error: 'Both new and confirm password are required',
        };
      }
      if (payload.newPassword !== payload.confirmPassword) {
        return { success: false, error: 'Passwords do not match' };
      }

      user.password = payload.newPassword;
      await this.userRepository.save(user);

      // Clear OTP after successful password change
      if (payload.otp) {
        await this.clearOTPForUser(userId);
      }

      return { success: true, message: 'Password updated successfully' };
    } catch (error) {
      return { success: false, error: 'Failed to update password' };
    }
  }

  // Send OTP for password change
  async sendOTPForPasswordChange(userId: number) {
    try {
      // console.log(`[OTP] Starting OTP generation for userId: ${userId}`);

      const user = await this.userRepository.findOne({ where: { id: userId } });
      if (!user) {
        // console.log(`[OTP] User not found for userId: ${userId}`);
        return { success: false, message: 'User not found' };
      }

      // console.log(`[OTP] Found user: ${user.email}, phone: ${user.phone}`);

      // Generate 4-digit OTP
      const otp = Math.floor(1000 + Math.random() * 9000).toString();
      // OLD: const otpExpiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
      // NEW: Changed to 10 minutes
      const otpExpiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes

      // console.log(`[OTP] Generated OTP: ${otp}, expires at: ${otpExpiresAt}`);

      // Check if OTP already exists for this user
      const existingOTP = await this.otpRepository
        .createQueryBuilder('otp')
        .where('otp.user_id = :userId', { userId })
        .andWhere('otp.otp_expires_at > NOW()')
        .getOne();

      try {
        if (existingOTP) {
          // console.log(
          // `[OTP] Updating existing OTP record ID: ${existingOTP.id}`,
          // );
          // Update existing OTP
          await this.otpRepository.update(existingOTP.id, {
            otp: otp,
            otp_expires_at: otpExpiresAt,
            updated_at: new Date(),
          });
        } else {
          // console.log(`[OTP] Creating new OTP record`);
          // Create new OTP
          const newOtp = this.otpRepository.create({
            user_id: userId,
            otp: otp,
            otp_expires_at: otpExpiresAt,
            updated_at: new Date(),
          });
          await this.otpRepository.save(newOtp);
        }

        // console.log(`[OTP] OTP saved to database successfully`);

        // Send OTP via email and SMS
        // console.log(`[OTP] Sending OTP via email to: ${user.email}`);
        const emailSent = await this.sendOTPEmail(user.email, otp);
        // console.log(`[OTP] Email sent result: ${emailSent}`);

        let smsSent = false;
        if (user.phone) {
          // console.log(`[OTP] Sending OTP via SMS to: ${user.phone}`);
          smsSent = await this.sendOTPSMS(user.phone, otp);
          // console.log(`[OTP] SMS sent result: ${smsSent}`);
        } else {
          // console.log(`[OTP] No phone number found for user`);
        }

        // Determine response message
        if (emailSent && smsSent) {
          // console.log(`[OTP] Success: OTP sent to both email and phone`);
          return {
            success: true,
            message: 'OTP sent successfully to email and phone',
          };
        } else if (emailSent) {
          // console.log(`[OTP] Success: OTP sent to email only`);
          return { success: true, message: 'OTP sent successfully to email' };
        } else if (smsSent) {
          // console.log(`[OTP] Success: OTP sent to phone only`);
          return { success: true, message: 'OTP sent successfully to phone' };
        } else {
          // console.log(`[OTP] Failed: Both email and SMS failed`);
          return { success: false, message: 'Failed to send OTP' };
        }
      } catch (dbError) {
        // console.error('[OTP] Database error while saving OTP:', dbError);
        return { success: false, message: 'Database error while saving OTP' };
      }
    } catch (error) {
      // console.error('[OTP] Error sending OTP for password change:', error);
      return { success: false, message: 'Failed to send OTP' };
    }
  }

  // Verify OTP for password change
  async verifyOTPForPasswordChange(userId: number, otp: string) {
    try {
      const validOTP = await this.otpRepository
        .createQueryBuilder('otp')
        .where('otp.user_id = :userId', { userId })
        .andWhere('otp.otp = :otp', { otp })
        .andWhere('otp.otp_expires_at > NOW()')
        .getOne();

      if (!validOTP) {
        return { success: false, message: 'Invalid or expired OTP' };
      }

      return { success: true, message: 'OTP verified successfully' };
    } catch (error) {
      console.error('Error verifying OTP:', error);
      return { success: false, message: 'Error verifying OTP' };
    }
  }

  // Clear OTP for user after successful password change
  async clearOTPForUser(userId: number) {
    try {
      await this.otpRepository.delete({ user_id: userId });
      return { success: true };
    } catch (error) {
      console.error('Error clearing OTP:', error);
      return { success: false };
    }
  }

  // Send OTP via email using EmailService
  private async sendOTPEmail(email: string, otp: string): Promise<boolean> {
    try {
      const templateName = 'simple_otp';
      const toName = email.split('@')[0];
      const context = {
        subject: 'Password Change OTP - Jobvumi',
        heading: 'Password Change Verification',
        otp: otp,
        description: "This OTP will expire in 10 minutes. If you didn't request this password change, please ignore this email.",
      };

      await this.emailService.sendTemplateEmail(
        templateName,
        email,
        toName,
        context,
      );

      return true;
    } catch (error) {
      console.error('Error sending OTP email:', error);
      return false;
    }
  }

  // Send OTP via SMS using Textlocal API (same as user service)
  private async sendOTPSMS(phone: string, otp: string): Promise<boolean> {
    // try {
    //   // Authorisation details
    //   const apiKey = process.env.TEXTLOCAL_API_KEY;
    //   const sender = process.env.TEXTLOCAL_SENDER; // Sender name for the SMS
    //   const test = '0'; // Set to 1 for testing, 0 for actual messages
    //   const apiUrl = 'https://api.textlocal.in/send/'; // TextLocal API endpoint
    //   // Process the message text
    //   const baseMessage = `Dear Customer, Your One Time Password(OTP) for creating ticket in TechSquadTeam is ${otp} .It is valid for 15 mins. PLEASE DO NOT SHARE THE OTP WITH ANYONE.`;
    //   const text = baseMessage.trim();
    //   // Add extension text
    //   const ext = null; // You can modify this if needed
    //   let finalText = text;
    //   if (!ext) {
    //     finalText = text + ' TST24X7 ONCALL SERVICES PRIVATE LIMITED';
    //   } else {
    //     finalText = text + 'TST24X7';
    //   }
    //   const message = encodeURIComponent(finalText);
    //   // Prepare data to be sent in the POST request
    //   const data = {
    //     apikey: apiKey,
    //     numbers: phone,
    //     sender: sender,
    //     message: message,
    //     test: test,
    //   };
    //   // Perform the POST request
    //   const response = await fetch(apiUrl, {
    //     method: 'POST',
    //     headers: {
    //       'Content-Type': 'application/x-www-form-urlencoded',
    //     },
    //     body: new URLSearchParams(data).toString(),
    //   });
    //   // Get the result from the API response
    //   const result = await response.text();
    //   // console.log('TextLocal API Response:', result);
    //   // Parse the result to check if it was successful
    //   try {
    //     const parsedResult = JSON.parse(result);
    //     if (parsedResult.status === 'success') {
    //       return true;
    //     } else {
    //       console.error('TextLocal API Error:', parsedResult);
    //       return false;
    //     }
    //   } catch (parseError) {
    //     console.error('Error parsing SMS API response:', parseError);
    //     return false;
    //   }
    // } catch (error) {
    //   console.error('Error sending SMS OTP:', error);
    //   return false;
    // }

    // Using msg2all API
    try {
      const userId = process.env.IKONTEL_USERID;
      const password = process.env.IKONTEL_PASSWORD;
      const version = process.env.IKONTEL_VERSION;
      const senderId = process.env.IKONTEL_SENDERID;
      const apiUrl = 'https://ikontel.msg2all.com/TRANSAPI/sendsms.jsp';

      if (!userId || !password || !senderId) {
        console.error(
          'Missing Ikontel SMS credentials in environment variables.',
        );
        return false;
      }

      // Process the message text

      const finalText = `${otp} is your OTP for Jobvumi portal. Valid for 10 mins. Do not share OTP with anyone. Jobvumi representatives will never ask for it.`;

      // Prepare data to be sent in the POST request
      const data = {
        login: userId,
        passwd: password,
        version: version,
        msisdn: phone,
        msg_type: 'text',
        msg: finalText,
        sender_id: senderId,
      };

      // Perform the POST request
      const response = await fetch(apiUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams(data).toString(),
      });

      // Get the result from the API response
      const result = await response.text();

      //   console.log("IKONTEL SMS API Response: " + result)

      try {
        const parsedResult = JSON.parse(result);
        if (
          parsedResult.result &&
          parsedResult.result.status &&
          parsedResult.result.status.statusCode === '0'
        ) {
          return true;
        } else {
          console.error('IKONTEL API Error:', parsedResult);
          return false;
        }
      } catch (parseError) {
        if (
          result.includes('success') ||
          result.includes('OK') ||
          result.includes('Sent') ||
          result.includes('"statusCode":"0"')
        ) {
          //   console.log(`SMS OTP sent successfully to ${phone}`)
          return true;
        }
        console.error('Error parsing IKONTEL SMS API response:', parseError);
        // console.log("Raw response:", result)
        return false;
      }
    } catch (error) {
      console.error('Error sending SMS OTP:', error);
      return false;
    }
  }

  // Send OTP for contact info change
  async sendOTPForContactInfoChange(
    userId: number,
    changedFields: { phone?: string; email?: string },
  ) {
    try {
      //console.log(`[Contact OTP] Starting OTP generation for userId: ${userId}, changedFields:`, changedFields);

      const user = await this.userRepository.findOne({ where: { id: userId } });
      if (!user) {
        //console.log(`[Contact OTP] User not found for userId: ${userId}`);
        return { success: false, message: 'User not found' };
      }

      //console.log(`[Contact OTP] Found user: ${user.email}, phone: ${user.phone}`);

      // Generate 4-digit OTP
      const otp = Math.floor(1000 + Math.random() * 9000).toString();
      // OLD: const otpExpiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
      // NEW: Changed to 10 minutes
      const otpExpiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes

      //console.log(`[Contact OTP] Generated OTP: ${otp}, expires at: ${otpExpiresAt}`);

      // Check if OTP already exists for this user
      const existingOTP = await this.otpRepository
        .createQueryBuilder('otp')
        .where('otp.user_id = :userId', { userId })
        .andWhere('otp.otp_expires_at > NOW()')
        .getOne();

      try {
        if (existingOTP) {
          //console.log(`[Contact OTP] Updating existing OTP record ID: ${existingOTP.id}`);
          // Update existing OTP
          await this.otpRepository.update(existingOTP.id, {
            otp: otp,
            otp_expires_at: otpExpiresAt,
            updated_at: new Date(),
          });
        } else {
          // console.log(`[Contact OTP] Creating new OTP record`);
          // Create new OTP
          const newOtp = this.otpRepository.create({
            user_id: userId,
            otp: otp,
            otp_expires_at: otpExpiresAt,
            updated_at: new Date(),
          });
          await this.otpRepository.save(newOtp);
        }

        // console.log(`[Contact OTP] OTP saved to database successfully`);

        // Send OTP to the changed contact methods
        let emailSent = false;
        let smsSent = false;

        // Send to new email if email changed
        if (changedFields.email) {
          // console.log(
          //`[Contact OTP] Sending OTP via email to new email: ${changedFields.email}`,
          //   );
          emailSent = await this.sendContactInfoOTPEmail(
            changedFields.email,
            otp,
          );
          // console.log(`[Contact OTP] Email sent result: ${emailSent}`);
        }

        // Send to new phone if phone changed
        if (changedFields.phone) {
          // console.log(
          //   `[Contact OTP] Sending OTP via SMS to new phone: ${changedFields.phone}`,
          // );
          smsSent = await this.sendContactInfoOTPSMS(changedFields.phone, otp);
          // console.log(`[Contact OTP] SMS sent result: ${smsSent}`);
        }

        // Determine response message
        if (emailSent && smsSent) {
          // console.log(
          //   `[Contact OTP] Success: OTP sent to both new email and phone`,
          // );
          return {
            success: true,
            message: 'OTP sent successfully to your new email and phone number',
          };
        } else if (emailSent) {
          // console.log(`[Contact OTP] Success: OTP sent to new email only`);
          return {
            success: true,
            message: 'OTP sent successfully to your new email address',
          };
        } else if (smsSent) {
          // console.log(`[Contact OTP] Success: OTP sent to new phone only`);
          return {
            success: true,
            message: 'OTP sent successfully to your new phone number',
          };
        } else {
          // console.log(`[Contact OTP] Failed: Both email and SMS failed`);
          return { success: false, message: 'Failed to send OTP' };
        }
      } catch (dbError) {
        console.error(
          '[Contact OTP] Database error while saving OTP:',
          dbError,
        );
        return { success: false, message: 'Database error while saving OTP' };
      }
    } catch (error) {
      console.error(
        '[Contact OTP] Error sending OTP for contact info change:',
        error,
      );
      return { success: false, message: 'Failed to send OTP' };
    }
  }

  // Verify OTP for contact info change
  async verifyOTPForContactInfoChange(
    userId: number,
    otp: string,
    changedFields: { phone?: string; email?: string },
  ) {
    try {
      console.log(
        `[Contact OTP Verify] Verifying OTP for userId: ${userId}, changedFields:`,
        changedFields,
      );

      const validOTP = await this.otpRepository
        .createQueryBuilder('otp')
        .where('otp.user_id = :userId', { userId })
        .andWhere('otp.otp = :otp', { otp })
        .andWhere('otp.otp_expires_at > NOW()')
        .getOne();

      if (!validOTP) {
        // console.log(
        //   `[Contact OTP Verify] Invalid or expired OTP for userId: ${userId}`,
        // );
        return { success: false, message: 'Invalid or expired OTP' };
      }

      // console.log(
      //   `[Contact OTP Verify] OTP verified successfully for userId: ${userId}`,
      // );
      return { success: true, message: 'OTP verified successfully' };
    } catch (error) {
      console.error('[Contact OTP Verify] Error verifying OTP:', error);
      return { success: false, message: 'Error verifying OTP' };
    }
  }

  // Send OTP via email for contact info change
  private async sendContactInfoOTPEmail(
    email: string,
    otp: string,
  ): Promise<boolean> {
    try {
      const templateName = 'simple_otp';
      const toName = email.split('@')[0];
      const context = {
        subject: 'Contact Information Change OTP - Jobvumi',
        heading: 'Contact Information Change Verification',
        otp: otp,
        description: "This OTP will expire in 10 minutes. If you didn't request this contact information change, please ignore this email.",
      };

      await this.emailService.sendTemplateEmail(
        templateName,
        email,
        toName,
        context,
      );

      return true;
    } catch (error) {
      console.error('Error sending contact info OTP email:', error);
      return false;
    }
  }

  // Send OTP via SMS for contact info change
  private async sendContactInfoOTPSMS(
    phone: string,
    otp: string,
  ): Promise<boolean> {
    try {
      // Authorisation details
      const apiKey = process.env.TEXTLOCAL_API_KEY;
      const sender = process.env.TEXTLOCAL_SENDER; // Sender name for the SMS
      const test = '0'; // Set to 1 for testing, 0 for actual messages
      const apiUrl = 'https://api.textlocal.in/send/'; // TextLocal API endpoint

      // Use the exact same template as the working password change OTP
      // TextLocal templates are very strict and must match exactly
      const baseMessage = `Dear Customer, Your One Time Password(OTP) for creating ticket in TechSquadTeam is ${otp} .It is valid for 15 mins. PLEASE DO NOT SHARE THE OTP WITH ANYONE.`;
      const text = baseMessage.trim();

      // Add extension text (using the same format as working template)
      const ext = null; // You can modify this if needed
      let finalText = text;
      if (!ext) {
        finalText = text + ' TST24X7 ONCALL SERVICES PRIVATE LIMITED';
      } else {
        finalText = text + 'TST24X7';
      }

      const message = encodeURIComponent(finalText);

      // Prepare data to be sent in the POST request
      const data = {
        apikey: apiKey,
        numbers: phone,
        sender: sender,
        message: message,
        test: test,
      };

      // Perform the POST request
      const response = await fetch(apiUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams(data).toString(),
      });

      // Get the result from the API response
      const result = await response.text();

      // Parse the result to check if it was successful
      try {
        const parsedResult = JSON.parse(result);
        if (parsedResult.status === 'success') {
          return true;
        } else {
          console.error(
            'TextLocal API Error for contact info OTP:',
            parsedResult,
          );
          return false;
        }
      } catch (parseError) {
        console.error(
          'Error parsing SMS API response for contact info OTP:',
          parseError,
        );
        return false;
      }
    } catch (error) {
      console.error('Error sending SMS OTP for contact info change:', error);
      return false;
    }
  }

  // --- Seeker Skills CRUD ---
  async setSeekerSkills(
    seeker_id: number,
    skills: ({ skill_id: number; proficiency_percentage?: number } | number)[],
  ) {
    // console.log(' [SET SKILLS] Starting setSeekerSkills for seeker_id:', seeker_id);
    // console.log(' [SET SKILLS] Skills to set:', skills);

    // Normalize input to objects - handle numbers, strings, and objects
    const newSkills = skills
      .map((s) => {
        if (typeof s === 'number') {
          return {
            skill_id: s,
            proficiency_percentage: null,
          };
        } else if (typeof s === 'string') {
          // Handle string IDs (e.g., "1", "2")
          const parsedId = parseInt(s, 10);
          return {
            skill_id: isNaN(parsedId) ? null : parsedId,
            proficiency_percentage: null,
          };
        } else {
          // Handle object with skill_id property
          return {
            skill_id: s.skill_id,
            proficiency_percentage: s.proficiency_percentage || null,
          };
        }
      })
      .filter((s) => s.skill_id !== null && s.skill_id !== undefined);
    // console.log(' [SET SKILLS] Normalized skills:', newSkills);

    // Fetch existing skills from DB
    const existing = await this.seekerSkillRepository.find({
      where: { seeker_id },
    });
    // console.log(' [SET SKILLS] Existing skills in DB:', existing);

    // Find skills to add, update, and delete
    const toAdd = newSkills.filter(
      (s) => !existing.some((e) => e.skill_id === s.skill_id),
    );
    // console.log(' [SET SKILLS] Skills to add:', toAdd);
    const toUpdate = newSkills.filter((s) =>
      existing.some(
        (e) =>
          e.skill_id === s.skill_id &&
          e.proficiency_percentage !== s.proficiency_percentage,
      ),
    );
    const toDelete = existing.filter(
      (e) => !newSkills.some((s) => s.skill_id === e.skill_id),
    );
    // console.log(' [SET SKILLS] Skills to update:', toUpdate);
    // console.log(' [SET SKILLS] Skills to delete:', toDelete);

    // Add new skills
    for (const skill of toAdd) {
      // console.log(' [SET SKILLS] Adding skill to seeker_skills table:', skill);
      await this.seekerSkillRepository.save({ seeker_id, ...skill });
    }
    // console.log(' [SET SKILLS] Added', toAdd.length, 'skills');

    // Update changed skills
    for (const skill of toUpdate) {
      // console.log(' [SET SKILLS] Updating skill:', skill);
      await this.seekerSkillRepository.update(
        { seeker_id, skill_id: skill.skill_id },
        { proficiency_percentage: skill.proficiency_percentage },
      );
    }

    // Delete removed skills
    for (const skill of toDelete) {
      // console.log(' [SET SKILLS] Deleting skill:', skill);
      await this.seekerSkillRepository.delete({
        seeker_id,
        skill_id: skill.skill_id,
      });
    }

    // console.log(' [SET SKILLS] setSeekerSkills completed successfully');
    return { success: true };
  }

  async getSeekerSkills(seeker_id: number) {
    // return this.seekerSkillRepository.find({ where: { seeker_id } });
    return await this.seekerSkillRepository.query(
      `
      SELECT ss.*, s.skill_name
      FROM seeker_skills ss
      LEFT JOIN skills s ON s.id = ss.skill_id
      WHERE ss.seeker_id = ?
      `,
      [seeker_id],
    );
  }

  // --- Seeker Languages CRUD ---
  async setSeekerLanguages(
    seeker_id: number,
    languages: ({ language_id: number } | number)[],
  ) {
    // console.log(' [SET LANGUAGES] Starting setSeekerLanguages for seeker_id:', seeker_id);
    // console.log(' [SET LANGUAGES] Languages to set:', languages);

    // Normalize input to objects - handle numbers, strings, and objects
    const newLangs = languages
      .map((l: any) => {
        if (typeof l === 'number') {
          return { language_id: l, english_level: null };
        } else if (typeof l === 'string') {
          // Handle string IDs (e.g., "1", "2")
          const parsedId = parseInt(l, 10);
          return { language_id: isNaN(parsedId) ? null : parsedId, english_level: null };
        } else {
          // Handle object with language_id property
          const langId = Number(l.id || l.language_id);
          const englishLevel = langId === 13 ? (l.english_level || null) : null;
          return { language_id: langId, english_level: englishLevel };
        }
      })
      .filter((l) => l.language_id !== null && l.language_id !== undefined && !isNaN(l.language_id));
    // console.log(' [SET LANGUAGES] Normalized languages:', newLangs);

    const existing = await this.seekerLanguageRepository.find({
      where: { seeker_id },
    });
    // console.log(' [SET LANGUAGES] Existing languages in DB:', existing);

    const toAdd = newLangs.filter(
      (l) => !existing.some((e) => e.language_id === l.language_id),
    );
    const toDelete = existing.filter(
      (e) => !newLangs.some((l) => l.language_id === e.language_id),
    );
    // console.log(' [SET LANGUAGES] Languages to add:', toAdd);
    // console.log(' [SET LANGUAGES] Languages to delete:', toDelete);

    for (const lang of toAdd) {
      // console.log(' [SET LANGUAGES] Adding language to seeker_languages table:', lang);
      await this.seekerLanguageRepository.save({ seeker_id, ...lang });
    }
    // console.log(' [SET LANGUAGES] Added', toAdd.length, 'languages');

    for (const lang of toDelete) {
      // console.log(' [SET LANGUAGES] Deleting language:', lang);
      await this.seekerLanguageRepository.delete({
        seeker_id,
        language_id: lang.language_id,
      });
    }

    // console.log(' [SET LANGUAGES] setSeekerLanguages completed successfully');
    return { success: true };
  }

  async getSeekerLanguages(seeker_id: number) {
    // return this.seekerLanguageRepository.find({ where: { seeker_id } });
    return await this.seekerLanguageRepository.query(
      `
      SELECT sl.*, l.name as language_name
      FROM seeker_language sl
      LEFT JOIN languages l ON l.id = sl.language_id
      WHERE sl.seeker_id = ?
      `,
      [seeker_id],
    );
  }

  // Job Management Actions
  async promoteJob(userId: string, jobId: string) {
    try {
      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'No company found for this user',
        };
      }

      // Check if user owns this job
      const job = await this.jobRepository.findOne({
        where: { id: Number(jobId), company_id: company.id },
      });

      if (!job) {
        return {
          success: false,
          message:
            'Job not found or you do not have permission to promote this job',
        };
      }

      // Get current job to check if it's already featured
      const currentJob = await this.jobRepository.findOne({
        where: { id: Number(jobId) },
      });

      if (!currentJob) {
        return {
          success: false,
          message: 'Job not found',
          data: null,
        };
      }

      // Toggle featured status
      const newFeaturedStatus = !currentJob.featured;

      await this.jobRepository.update(Number(jobId), {
        featured: newFeaturedStatus,
        updated_at: new Date(),
      });

      return {
        success: true,
        message: newFeaturedStatus
          ? 'Job promoted successfully'
          : 'Job unpromoted successfully',
        data: { jobId, featured: newFeaturedStatus },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to promote job',
        error: error.message,
      };
    }
  }

  async cloneJob(userId: string, jobId: string) {
    try {
      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'No company found for this user',
        };
      }

      // Check if user owns this job
      const originalJob = await this.jobRepository.findOne({
        where: { id: Number(jobId), company_id: company.id },
      });

      if (!originalJob) {
        return {
          success: false,
          message:
            'Job not found or you do not have permission to clone this job',
        };
      }

      // Create a new job with the same data but different ID
      const clonedJob = this.jobRepository.create({
        ...originalJob,
        id: undefined, // Let TypeORM generate new ID
        title: `${originalJob.title} (Copy)`,
        created_at: new Date(),
        updated_at: new Date(),
      });

      const savedJob = await this.jobRepository.save(clonedJob);

      return {
        success: true,
        message: 'Job cloned successfully',
        data: { jobId: savedJob.id },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to clone job',
        error: error.message,
      };
    }
  }

  async expireJob(userId: string, jobId: string) {
    try {
      // First get the company ID for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getOne();

      if (!company) {
        return {
          success: false,
          message: 'No company found for this user',
        };
      }

      // Check if user owns this job
      const job = await this.jobRepository.findOne({
        where: { id: Number(jobId), company_id: company.id },
      });

      if (!job) {
        return {
          success: false,
          message:
            'Job not found or you do not have permission to expire this job',
        };
      }

      // Update job status to closed (expired) and set expires_at to current date
      await this.jobRepository.update(Number(jobId), {
        status: 'closed',
        expires_at: new Date(), // Set expiry date to current date
        updated_at: new Date(),
      });

      return {
        success: true,
        message: 'Job expired successfully',
        data: { jobId },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to expire job',
        error: error.message,
      };
    }
  }

  // async getSeekerFullProfile(seekerProfileId: number) {
  //   // 1. Fetch main profile using QueryBuilder and join users
  //   const profile = await this.seekerProfileRepository
  //     .createQueryBuilder('profile')
  //     .leftJoinAndSelect('profile.user_id', 'user')
  //     .where('profile.id = :id', { id: seekerProfileId })
  //     .getOne();
  //   if (!profile) {
  //     return { success: false, message: 'Seeker profile not found' };
  //   }
  //   const seeker_id = profile.id;
  //   // 2. Fetch related data in parallel
  //   const [address, education, experience, skills, languages, socialLinks] =
  //     await Promise.all([
  //       this.getSeekerAddress(seeker_id).catch(() => null),
  //       this.getSeekerEducations(seeker_id).catch(() => []),
  //       this.getSeekerExperiences(seeker_id).catch(() => []),
  //       this.getSeekerSkills(seeker_id).catch(() => []),
  //       this.getSeekerLanguages(seeker_id).catch(() => []),
  //       this.getSeekerSocialLinks(seeker_id).catch(() => []),
  //     ]);
  //   return {
  //     success: true,
  //     profile,
  //     address: address || null,
  //     education: education || [],
  //     experience: experience || [],
  //     skills: skills || [],
  //     languages: languages || [],
  //     socialLinks: socialLinks || [],
  //   };
  // }
  async getSeekerFullProfile(userId: number) {
    // 1. Find seeker profile by userId and join user fields with experience, education, and profession
    const profileRaw = await this.seekerProfileRepository
      .createQueryBuilder('profile')
      .leftJoin('users', 'user', 'user.id = profile.user_id')
      .leftJoin('experiences', 'exp', 'exp.id = profile.experiences_id')
      .leftJoin('education_levels', 'edu', 'edu.id = profile.education_id')
      .leftJoin('roles', 'roles', 'roles.id = profile.profession_id')
      // Join for Multi-Select Roles (Blue Collar)
      .leftJoin(
        'seeker_role_experience',
        'sre',
        'sre.seeker_id = profile.id AND sre.status = 1',
      )
      .leftJoin('roles', 's_roles', 's_roles.id = sre.designation_id')
      .leftJoin('experiences', 's_exp', 's_exp.id = sre.experience_id')
      .leftJoin(
        'city',
        'pref_city',
        'pref_city.city_id = profile.preferred_work_city_id',
      )
      .leftJoin(
        'area',
        'pref_area',
        'pref_area.id = profile.preferred_work_locality_area_id',
      )
      .select([
        'profile.*',
        'user.full_name as user_full_name',
        'user.email as user_email',
        'user.phone as user_phone',
        'user.profile_picture as user_profile_picture',
        'user.selected_role_category as user_selected_role_category',
        'exp.level as experience_name',
        'edu.name as education_name',
        // Prioritize new multi-select roles over legacy profession_id
        'COALESCE(GROUP_CONCAT(DISTINCT s_roles.name SEPARATOR ", "), roles.name) as profession_name',
        'pref_city.city_name as preferred_city_name',
        'pref_area.area as preferred_area_name',
      ])
      .where('profile.user_id = :userId', { userId })
      .groupBy('profile.id')
      .getRawOne();
    if (!profileRaw) {
      return { success: false, message: 'Seeker profile not found' };
    }
    // console.log('profileRaw', profileRaw);
    // Extract user info from raw result
    const userInfo = {
      name: profileRaw.user_full_name,
      full_name: profileRaw.user_full_name,
      email: profileRaw.user_email,
      phone: profileRaw.user_phone,
      profile_picture: profileRaw.user_profile_picture,
      selected_role_category: profileRaw.user_selected_role_category,
      experience_name: profileRaw.experience_name,
      education_name: profileRaw.education_name,
      profession_name: profileRaw.profession_name,
    };
    // console.log('userInfo', userInfo);
    // Remove user fields from profileRaw for the profile object
    const profile = { ...profileRaw };
    delete profile.user_full_name;
    delete profile.user_email;
    delete profile.user_phone;
    delete profile.user_profile_picture;
    delete profile.user_selected_role_category;
    // Keep the new fields: experience_name, education_name, profession_name
    const seekerId = profile.id;
    // 2. Fetch address with city, state, country information (one-to-one)
    const address = await this.seekerAddressRepository
      .createQueryBuilder('address')
      .leftJoin(
        'city',
        'permanent_city',
        'permanent_city.city_id = address.permanent_city_id',
      )
      .leftJoin(
        'state',
        'permanent_state',
        'permanent_state.state_id = address.permanent_state_id',
      )
      .leftJoin(
        'country',
        'permanent_country',
        'permanent_country.country_id = address.permanent_country_id',
      )
      .leftJoin(
        'city',
        'communication_city',
        'communication_city.city_id = address.communication_city_id',
      )
      .leftJoin(
        'state',
        'communication_state',
        'communication_state.state_id = address.communication_state_id',
      )
      .leftJoin(
        'country',
        'communication_country',
        'communication_country.country_id = address.communication_country_id',
      )
      .select([
        'address.*',
        'permanent_city.city_name as permanent_city_name',
        'permanent_state.state_name as permanent_state_name',
        'permanent_country.country_name as permanent_country_name',
        'communication_city.city_name as communication_city_name',
        'communication_state.state_name as communication_state_name',
        'communication_country.country_name as communication_country_name',
      ])
      .where('address.seeker_profile_id = :id', { id: seekerId })
      .getRawOne();
    // 3. Fetch education (one-to-many)
    const educationRaw = await this.seekerEducationRepository
      .createQueryBuilder('education')
      .where('education.seeker_id = :id', { id: seekerId })
      .getMany();

    // Map notes to institution for frontend compatibility
    const education = educationRaw.map((edu) => ({
      ...edu,
      institution: edu.notes, // Frequently used for institution/school name
    }));

    // 4. Fetch experience (one-to-many)
    const experience = await this.seekerExperienceRepository
      .createQueryBuilder('experience')
      .where('experience.seeker_id = :id', { id: seekerId })
      .getMany();
    // 5. Fetch skills (join with skills table)
    const skills = await this.seekerSkillRepository
      .createQueryBuilder('seekerSkill')
      .leftJoinAndSelect('skills', 'skill', 'skill.id = seekerSkill.skill_id')
      .where('seekerSkill.seeker_id = :id', { id: seekerId })
      .select([
        'seekerSkill.id',
        'seekerSkill.proficiency_percentage',
        'skill.id',
        'skill.skill_name',
      ])
      .getRawMany();
    // 6. Fetch languages (join with languages table)
    const languages = await this.seekerLanguageRepository
      .createQueryBuilder('seekerLanguage')
      .leftJoinAndSelect(
        'languages',
        'language',
        'language.id = seekerLanguage.language_id',
      )
      .where('seekerLanguage.seeker_id = :id', { id: seekerId })
      .select(['seekerLanguage.id', 'seekerLanguage.english_level', 'language.id', 'language.name'])
      .getRawMany();
    // 7. Fetch social links (one-to-many) with platform names
    const socialLinks = await this.dataSource
      .createQueryBuilder()
      .select([
        'sl.id as id',
        'sl.seeker_id as seeker_id',
        'sl.platform_id as platform_id',
        'sl.url as url',
        'sp.name as platform_name',
      ])
      .from('seeker_social_links', 'sl')
      .leftJoin('social_platform', 'sp', 'sp.id = sl.platform_id')
      .where('sl.seeker_id = :id', { id: seekerId })
      .getRawMany();
    // 8. Fetch role experiences (ONLY for blue-collar workers) with designation and experience level names
    let roleExperiences = [];
    if (userInfo.selected_role_category === 'blue_collar') {
      roleExperiences = await this.dataSource
        .createQueryBuilder()
        .select([
          'sre.id as id',
          'sre.seeker_id as seeker_id',
          'sre.designation_id as designation_id',
          'sre.experience_id as experience_id',
          'r.name as designation_name',
          'e.level as experience_level',
          'e.duration as experience_duration',
        ])
        .from('seeker_role_experience', 'sre')
        .leftJoin('roles', 'r', 'r.id = sre.designation_id')
        .leftJoin('experiences', 'e', 'e.id = sre.experience_id')
        .where('sre.seeker_id = :id', { id: seekerId })
        .andWhere('sre.status = 1')
        .getRawMany();
    }
    // 9. Fetch resumes (one-to-many)
    const resumes = await this.seekerResumeRepository.find({
      where: { seeker_id: seekerId, is_deleted: false },
    });

    return {
      success: true,
      profile,
      user: userInfo,
      address: address || null,
      education: education || [],
      experience: experience || [],
      skills: skills || [],
      languages: languages || [],
      socialLinks: socialLinks || [],
      roleExperiences: roleExperiences || [],
      resumes: resumes || [],
    };
  }

  // Get company details by company ID
  // async getCompanyDetails(companyId: number) {
  //   try {
  //     const company = await this.dataSource
  //       .createQueryBuilder()
  //       .select([
  //         'c.id',
  //         'c.name',
  //         'c.logo',
  //         'c.banner_url',
  //         'c.about',
  //         'c.vision',
  //         'c.founded_at as c_founded_at',
  //         'c.location',
  //         'c.website',
  //         'c.industry_id',
  //         'c.organization_type_id',
  //         'c.company_size_id',
  //         'c.country_id',
  //         'c.state_id',
  //         'c.city_id',
  //         'c.pincode',
  //         'c.area_id',
  //         'c.latitude',
  //         'c.longitude',
  //         'c.status',
  //         'c.created_at',
  //         'u.email',
  //         'u.phone',
  //         'u.full_name',
  //         'u.username',
  //         'i.name as industry_name',
  //         'ot.label as organization_type_name',
  //         'cs.label as company_size_name',
  //         'co.country_name',
  //         's.state_name',
  //         'ci.city_name',
  //         'a.area as area_name',
  //       ])
  //       .from('companies', 'c')
  //       .leftJoin('users', 'u', 'u.id = c.user_id')
  //       .leftJoin('industries', 'i', 'i.id = c.industry_id')
  //       .leftJoin('organization_types', 'ot', 'ot.id = c.organization_type_id')
  //       .leftJoin('company_sizes', 'cs', 'cs.id = c.company_size_id')
  //       .leftJoin('country', 'co', 'co.country_id = c.country_id')
  //       .leftJoin('state', 's', 's.state_id = c.state_id')
  //       .leftJoin('city', 'ci', 'ci.city_id = c.city_id')
  //       .leftJoin('area', 'a', 'a.id = c.area_id')
  //       .where('c.id = :companyId', { companyId })
  //       .andWhere('c.is_deleted = 0')
  //       .getRawOne();

  //     if (!company) {
  //       return { success: false, message: 'Company not found' };
  //     }

  //     // Fetch social links joined with platform names
  //     const socialLinks = await this.dataSource
  //       .createQueryBuilder()
  //       .select(['platform.name AS platform', 'links.url AS url'])
  //       .from('company_social_links', 'links')
  //       .leftJoin(
  //         'social_platform',
  //         'platform',
  //         'platform.id = links.platform_id',
  //       )
  //       .where('links.company_id = :companyId', { companyId })
  //       .getRawMany();

  //     // Get total job posts count (all jobs)
  //     const totalJobPostsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_posts', 'jp')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .getRawOne();

  //     // Get active job posts count (open and not expired)
  //     const activeJobPostsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_posts', 'jp')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .andWhere('jp.status = :status', { status: 'open' })
  //       .andWhere('(jp.expires_at IS NULL OR jp.expires_at > NOW())')
  //       .getRawOne();

  //     // Get expired job posts count (closed or expired)
  //     const expiredJobPostsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_posts', 'jp')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .andWhere(
  //         "(jp.status != 'open' OR (jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
  //       )
  //       .getRawOne();

  //     // Get total applications count (all applications)
  //     const totalApplicationsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_applications', 'ja')
  //       .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .getRawOne();

  //     // Get active applications count (for active jobs)
  //     const activeApplicationsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_applications', 'ja')
  //       .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .andWhere('jp.status = :status', { status: 'open' })
  //       .andWhere('(jp.expires_at IS NULL OR jp.expires_at > NOW())')
  //       .getRawOne();

  //     // Get expired applications count (for expired/closed jobs)
  //     const expiredApplicationsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_applications', 'ja')
  //       .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .andWhere(
  //         "(jp.status != 'open' OR (jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
  //       )
  //       .getRawOne();

  //     // Get views count (only for open jobs)
  //     const viewsCount = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_views', 'jv')
  //       .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .andWhere('jp.status = :status', { status: 'open' })
  //       .getRawOne();

  //     // Get recent job posts with application counts
  //     const recentJobPosts = await this.dataSource
  //       .createQueryBuilder()
  //       .select([
  //         'jp.id',
  //         'jp.title',
  //         'jp.created_at',
  //         'jp.status',
  //         'jp.expires_at',
  //         'jp.salary_min',
  //         'jp.salary_max',
  //         'jp.featured',
  //         'jp.image_url',
  //         'jt.name as job_type',
  //         'ci.city_name',
  //         'st.state_name',
  //         'co.country_name',
  //       ])
  //       .addSelect(
  //         '(SELECT COUNT(*) FROM job_applications ja WHERE ja.job_post_id = jp.id)',
  //         'application_count',
  //       )
  //       .addSelect(
  //         '(SELECT COUNT(*) FROM job_views jv WHERE jv.job_post_id = jp.id)',
  //         'view_count',
  //       )
  //       .from('job_posts', 'jp')
  //       .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
  //       .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
  //       .leftJoin('state', 'st', 'st.state_id = jp.state_id')
  //       .leftJoin('country', 'co', 'co.country_id = jp.country_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .andWhere('jp.status = :status', { status: 'open' })
  //       .andWhere('(jp.expires_at IS NULL OR jp.expires_at > NOW())')
  //       .orderBy('jp.created_at', 'DESC')
  //       .limit(5)
  //       .getRawMany();

  //     console.log('=== Recent Job Posts Debug ===');
  //     console.log('Company ID:', companyId);
  //     console.log('Recent Job Posts Count:', recentJobPosts?.length || 0);
  //     console.log(
  //       'Recent Job Posts Data:',
  //       JSON.stringify(recentJobPosts, null, 2),
  //     );

  //     // Get recent applications (all applications for the company)
  //     const recentApplications = await this.dataSource
  //       .createQueryBuilder()
  //       .select([
  //         'ja.id as ja_id',
  //         'ja.applied_at as ja_applied_at',
  //         'ja.status as ja_status',
  //         'jp.title as job_title',
  //         "COALESCE(u.full_name, 'Unknown Candidate') as candidate_name",
  //         'u.email as candidate_email',
  //       ])
  //       .from('job_applications', 'ja')
  //       .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
  //       .innerJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
  //       .innerJoin('users', 'u', 'u.id = sp.user_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .orderBy('ja.applied_at', 'DESC')
  //       .limit(5)
  //       .getRawMany();

  //     console.log('=== Recent Applications Debug ===');
  //     console.log('Company ID:', companyId);
  //     console.log('Applications Count:', recentApplications?.length || 0);
  //     console.log(
  //       'Applications Data:',
  //       JSON.stringify(recentApplications, null, 2),
  //     );

  //     return {
  //       success: true,
  //       data: {
  //         ...company,
  //         // Job posts stats
  //         totalJobPosts: parseInt(totalJobPostsCount?.count || '0'),
  //         activeJobPosts: parseInt(activeJobPostsCount?.count || '0'),
  //         expiredJobPosts: parseInt(expiredJobPostsCount?.count || '0'),
  //         // Legacy fields for backward compatibility
  //         jobPosts: parseInt(activeJobPostsCount?.count || '0'),
  //         jobPostsnoexpCount: parseInt(activeJobPostsCount?.count || '0'),
  //         // Applications stats
  //         totalApplications: parseInt(totalApplicationsCount?.count || '0'),
  //         activeApplications: parseInt(activeApplicationsCount?.count || '0'),
  //         expiredApplications: parseInt(expiredApplicationsCount?.count || '0'),
  //         // Legacy field for backward compatibility
  //         applications: parseInt(activeApplicationsCount?.count || '0'),
  //         views: parseInt(viewsCount?.count || '0'),
  //         recentJobPosts,
  //         recentApplications,
  //         social_links: socialLinks.map((link) => ({
  //           platform: link.platform,
  //           url: link.url,
  //         })),
  //       },
  //     };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to fetch company details',
  //       error: error.message,
  //     };
  //   }
  // }
  async getCompanyDetails(companyId: number) {
    try {
      // Company base info
      const company = await this.dataSource
        .createQueryBuilder()
        .select([
          'c.id',
          'c.name',
          'c.logo',
          'c.banner_url',
          'c.about',
          'c.vision',
          'c.founded_at as c_founded_at',
          'c.location',
          'c.website',
          'c.industry_id',
          'c.organization_type_id',
          'c.company_size_id',
          'c.country_id',
          'c.state_id',
          'c.city_id',
          'c.pincode',
          'c.area_id',
          'c.gst_number',
          'c.cin_number',
          'c.latitude',
          'c.longitude',
          'c.status',
          'c.status',
          'c.created_at',
          'c.is_admin_company',
          'c.email as c_email',
          'c.phone as c_phone',
          'u.email',
          'u.phone',
          'u.full_name',
          'u.username',
          'i.name as industry_name',
          'ot.label as organization_type_name',
          'cs.label as company_size_name',
          'co.country_name',
          's.state_name',
          'ci.city_name',
          'a.area as area_name',
        ])
        .from('companies', 'c')
        .leftJoin('users', 'u', 'u.id = c.user_id')
        .leftJoin('industries', 'i', 'i.id = c.industry_id')
        .leftJoin('organization_types', 'ot', 'ot.id = c.organization_type_id')
        .leftJoin('company_sizes', 'cs', 'cs.id = c.company_size_id')
        .leftJoin('country', 'co', 'co.country_id = c.country_id')
        .leftJoin('state', 's', 's.state_id = c.state_id')
        .leftJoin('city', 'ci', 'ci.city_id = c.city_id')
        .leftJoin('area', 'a', 'a.id = c.area_id')
        .where('c.id = :companyId', { companyId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      if (!company) {
        return { success: false, message: 'Company not found' };
      }

      // Social links
      const socialLinks = await this.dataSource
        .createQueryBuilder()
        .select([
          'platform.name AS platform',
          'links.platform_id AS platform_id',
          'links.url AS url',
        ])
        .from('company_social_links', 'links')
        .leftJoin(
          'social_platform',
          'platform',
          'platform.id = links.platform_id',
        )
        .where('links.company_id = :companyId', { companyId })
        .getRawMany();

      // Job posts counts (note: application counts below use strict joins to require seeker+user)
      const totalJobPostsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .where('jp.company_id = :companyId', { companyId })
        .getRawOne();

      // Override email and phone for admin companies
      // Override email and phone for admin companies
      const isAdminCompany =
        company.is_admin_company === 1 || company.c_is_admin_company === 1;

      if (isAdminCompany) {
        company.email = company.c_email;
        company.phone = company.c_phone;
      } else {
        // For regular companies, ensure email/phone are set from user data
        company.email = company.u_email || company.email;
        company.phone = company.u_phone || company.phone;
      }

      const activeJobPostsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere('jp.status = :status', { status: 'open' })
        .andWhere('(jp.expires_at IS NULL OR jp.expires_at > NOW())')
        .getRawOne();
      const pendingJobPostsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere('jp.status = :status', { status: 'pending' })
        .getRawOne();

      const expiredJobPostsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere(
          "(jp.status = 'closed' OR (jp.status = 'open' AND jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
        )
        .getRawOne();

      // APPLICATION COUNTS — strict: only count applications where seeker_profile and user exist
      const totalApplicationsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .innerJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
        .innerJoin('users', 'u', 'u.id = sp.user_id') // strict: requires user
        .where('jp.company_id = :companyId', { companyId })
        .getRawOne();

      const activeApplicationsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .innerJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
        .innerJoin('users', 'u', 'u.id = sp.user_id')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere('jp.status = :status', { status: 'open' })
        .andWhere('(jp.expires_at IS NULL OR jp.expires_at > NOW())')
        .getRawOne();

      const expiredApplicationsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .innerJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
        .innerJoin('users', 'u', 'u.id = sp.user_id')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere(
          "(jp.status != 'open' OR (jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
        )
        .getRawOne();

      // Views count only for open jobs (keeps the same logic you had)
      const viewsCount = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_views', 'jv')
        .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere('jp.status = :status', { status: 'open' })
        .getRawOne();

      // Recent job posts — include expires_at and use a subselect that counts only valid apps (seeker+user)
      const recentJobPosts = await this.dataSource
        .createQueryBuilder()
        .select([
          'jp.id',
          'jp.title',
          'jp.created_at',
          'jp.status',
          'jp.expires_at',
          'jp.salary_min',
          'jp.salary_max',
          'jp.featured',
          'jp.image_url',
          'jt.name as job_type',
          'ci.city_name',
          'st.state_name',
          'co.country_name',
        ])
        .addSelect(
          // counts only apps with seeker_profile + user
          `(SELECT COUNT(*) FROM job_applications ja 
            INNER JOIN seeker_profiles sp ON sp.id = ja.seeker_id
            INNER JOIN users u ON u.id = sp.user_id
            WHERE ja.job_post_id = jp.id)`,
          'application_count',
        )
        .addSelect(
          `(SELECT COUNT(*) FROM job_views jv WHERE jv.job_post_id = jp.id)`,
          'view_count',
        )
        .from('job_posts', 'jp')
        .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
        .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
        .leftJoin('state', 'st', 'st.state_id = jp.state_id')
        .leftJoin('country', 'co', 'co.country_id = jp.country_id')
        .where('jp.company_id = :companyId', { companyId })
        .andWhere("jp.status IN ('open','pending')") // includes pending so frontend can render pending badge
        .andWhere('(jp.expires_at IS NULL OR jp.expires_at > NOW())') // keep only non-expired for recent listing
        .orderBy('jp.created_at', 'DESC')
        .limit(5)
        .getRawMany();

      // Debug logging (helpful during development)
      // console.log('=== Recent Job Posts Debug ===');
      // console.log('Company ID:', companyId);
      // console.log('Recent Job Posts Count:', recentJobPosts?.length || 0);
      // console.log(
      //   'Recent Job Posts Data:',
      //   JSON.stringify(recentJobPosts, null, 2),
      // );

      // Recent applications — STRICT: only show applications where seeker_profile AND user exist
      const recentApplications = await this.dataSource
        .createQueryBuilder()
        .select([
          'ja.id as ja_id',
          'ja.applied_at as ja_applied_at',
          'ja.status as ja_status',
          'jp.id as job_id',
          'jp.title as job_title',
          'u.full_name as candidate_name',
          'u.email as candidate_email',
          'u.id as userId',
          'sp.id as seekerId',
          'u.id as user_id',
          'sp.id as seeker_id',
        ])
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .innerJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
        .innerJoin('users', 'u', 'u.id = sp.user_id') // strict join -> excludes orphaned apps
        .where('jp.company_id = :companyId', { companyId })
        .orderBy('ja.applied_at', 'DESC')
        .limit(5)
        .getRawMany();

      // console.log('=== Recent Applications Debug ===');
      // console.log('Company ID:', companyId);
      // console.log('Applications Count:', recentApplications?.length || 0);
      // console.log(
      //   'Applications Data:',
      //   JSON.stringify(recentApplications, null, 2),
      // );

      // Fetch key people
      const keyPeople = await this.dataSource.query(
        'SELECT id, name, designation, email, phone, visibility_level FROM company_key_people WHERE company_id = ?',
        [companyId],
      );

      // Build response
      return {
        success: true,
        data: {
          ...company,
          // Job posts stats
          totalJobPosts: parseInt(totalJobPostsCount?.count || '0'),
          activeJobPosts: parseInt(activeJobPostsCount?.count || '0'),
          expiredJobPosts: parseInt(expiredJobPostsCount?.count || '0'),
          pendingJobPosts: parseInt(pendingJobPostsCount?.count || '0'),
          // Legacy fields for backward compatibility
          jobPosts: parseInt(activeJobPostsCount?.count || '0'),
          jobPostsnoexpCount: parseInt(activeJobPostsCount?.count || '0'),
          // Applications stats (STRICT counts)
          totalApplications: parseInt(totalApplicationsCount?.count || '0'),
          activeApplications: parseInt(activeApplicationsCount?.count || '0'),
          expiredApplications: parseInt(expiredApplicationsCount?.count || '0'),
          // Legacy field
          applications: parseInt(activeApplicationsCount?.count || '0'),
          // Views
          views: parseInt(viewsCount?.count || '0'),
          recentJobPosts,
          recentApplications,
          social_links: socialLinks.map((link) => ({
            platform: link.platform,
            platform_id: link.platform_id,
            url: link.url,
          })),
          key_people: keyPeople,
        },
      };
    } catch (error) {
      console.error('getCompanyDetails error:', error);
      return {
        success: false,
        message: 'Failed to fetch company details',
        error: error.message,
      };
    }
  }

  // Get all company jobs with pagination and sorting
  // async getCompanyJobs(
  //   companyId: number,
  //   page: number = 1,
  //   limit: number = 10,
  // ) {
  //   try {
  //     const offset = (page - 1) * limit;

  //     // Get all job posts for the company with application counts
  //     const jobPosts = await this.dataSource
  //       .createQueryBuilder()
  //       .select([
  //         'jp.id as jp_id',
  //         'jp.title as jp_title',
  //         'jp.created_at as jp_created_at',
  //         'jp.status as status',
  //         'jp.expires_at as expires_at',
  //         'jp.salary_min as jp_salary_min',
  //         'jp.salary_max as jp_salary_max',
  //         'jp.featured',
  //         'jp.image_url',
  //         'jt.name as job_type',
  //         'ci.city_name as ci_city_name',
  //         'st.state_name as st_state_name',
  //         'co.country_name as co_country_name',
  //       ])
  //       .addSelect(
  //         '(SELECT COUNT(*) FROM job_applications ja WHERE ja.job_post_id = jp.id)',
  //         'application_count',
  //       )
  //       .addSelect(
  //         '(SELECT COUNT(*) FROM job_views jv WHERE jv.job_post_id = jp.id)',
  //         'view_count',
  //       )
  //       .from('job_posts', 'jp')
  //       .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
  //       .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
  //       .leftJoin('state', 'st', 'st.state_id = jp.state_id')
  //       .leftJoin('country', 'co', 'co.country_id = jp.country_id')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .orderBy(
  //         "(CASE WHEN jp.status = 'open' AND (jp.expires_at IS NULL OR jp.expires_at > NOW()) AND (SELECT COUNT(*) FROM job_applications ja WHERE ja.job_post_id = jp.id) > 0 THEN 1 ELSE 2 END)",
  //         'ASC',
  //       )
  //       .addOrderBy('jp.created_at', 'DESC')
  //       .addOrderBy("CASE WHEN jp.status = 'open' THEN 1 ELSE 2 END", 'ASC')
  //       .offset(offset)
  //       .limit(limit)
  //       .getRawMany();

  //     console.log('=== Job Posts Debug ===');
  //     console.log('Company ID:', companyId);
  //     console.log('Page:', page, 'Limit:', limit);
  //     console.log('Jobs Count:', jobPosts?.length || 0);
  //     console.log('Jobs Data:', JSON.stringify(jobPosts, null, 2));

  //     // Get total count
  //     const totalResult = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'total')
  //       .from('job_posts', 'jp')
  //       .where('jp.company_id = :companyId', { companyId })
  //       .getRawOne();

  //     const total = parseInt(totalResult?.total || '0');

  //     return {
  //       success: true,
  //       data: {
  //         jobs: jobPosts,
  //         total,
  //         totalPages: Math.ceil(total / limit),
  //         currentPage: page,
  //         limit,
  //       },
  //     };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to fetch company jobs',
  //       error: error.message,
  //     };
  //   }
  // }
  async getCompanyJobs(companyId: number, page = 1, limit = 10) {
    try {
      const offset = (page - 1) * limit;

      const jobPosts = await this.dataSource
        .createQueryBuilder()
        .select([
          'jp.id as jp_id',
          'jp.title as jp_title',
          'jp.created_at as jp_created_at',
          'jp.status as status',
          'jp.expires_at as expires_at',
          'jp.salary_min as jp_salary_min',
          'jp.salary_max as jp_salary_max',
          'jp.featured',
          'jp.image_url',
          'jt.name as job_type',
          'ci.city_name as ci_city_name',
          'st.state_name as st_state_name',
          'co.country_name as co_country_name',
        ])
        .addSelect(
          `(SELECT COUNT(*) FROM job_applications ja
              INNER JOIN seeker_profiles sp ON sp.id = ja.seeker_id
              INNER JOIN users u ON u.id = sp.user_id
           WHERE ja.job_post_id = jp.id)`,
          'application_count',
        )
        .addSelect(
          `(SELECT COUNT(*) FROM job_views jv WHERE jv.job_post_id = jp.id)`,
          'view_count',
        )
        .from('job_posts', 'jp')
        .leftJoin('job_type', 'jt', 'jt.id = jp.job_type_id')
        .leftJoin('city', 'ci', 'ci.city_id = jp.city_id')
        .leftJoin('state', 'st', 'st.state_id = jp.state_id')
        .leftJoin('country', 'co', 'co.country_id = jp.country_id')
        .where('jp.company_id = :companyId', { companyId })

        // ORDERING: open + valid application_count > 0 FIRST
        .orderBy(
          `(CASE
              WHEN jp.status = 'open'
                AND (jp.expires_at IS NULL OR jp.expires_at > NOW())
                AND ((SELECT COUNT(*) FROM job_applications ja
                        INNER JOIN seeker_profiles sp ON sp.id = ja.seeker_id
                        INNER JOIN users u ON u.id = sp.user_id
                      WHERE ja.job_post_id = jp.id) > 0)
              THEN 1 ELSE 2
           END)`,
          'ASC',
        )
        .addOrderBy('jp.created_at', 'DESC')
        .addOrderBy("CASE WHEN jp.status = 'open' THEN 1 ELSE 2 END", 'ASC')
        .offset(offset)
        .limit(limit)
        .getRawMany();

      const totalResult = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'total')
        .from('job_posts', 'jp')
        .where('jp.company_id = :companyId', { companyId })
        .getRawOne();

      return {
        success: true,
        data: {
          jobs: jobPosts,
          total: Number(totalResult.total),
          totalPages: Math.ceil(Number(totalResult.total) / limit),
          currentPage: page,
          limit,
        },
      };
    } catch (error) {
      console.error(error);
      return {
        success: false,
        message: 'Failed to fetch company jobs',
        error: error.message,
      };
    }
  }

  async getCompanyJobApplicants(
    companyId: number,
    jobId: number,
    page = 1,
    limit = 20,
  ) {
    try {
      const offset = (page - 1) * limit;

      const job = await this.dataSource
        .createQueryBuilder()
        .select(['jp.id'])
        .from('job_posts', 'jp')
        .where('jp.id = :jobId', { jobId })
        .andWhere('jp.company_id = :companyId', { companyId })
        .getRawOne();

      if (!job) {
        return {
          success: false,
          message: 'Job not found for this company',
          data: {
            applications: [],
            pagination: { page, limit, total: 0, totalPages: 0 },
          },
        };
      }

      const query = this.dataSource
        .createQueryBuilder()
        .select([
          'DISTINCT app.id AS application_id',
          'app.status AS status',
          'app.applied_at AS applied_at',
          'app.cover_letter AS cover_letter',
          'job.id AS job_id',
          'job.title AS job_title',
          'u.id AS user_id',
          'u.full_name AS candidate_name',
          'u.email AS candidate_email',
          'u.selected_role_category AS selected_role_category',
          'u.profile_picture AS profile_picture',
          'sp.id AS seeker_id',
          'sp.professional_title AS professional_title',
          'sp.experiences_id AS experience_id',
          'sp.education_id AS education_id',
          'exp.level AS experience_name',
          'edu.name AS education_name',
          'sr.file_url AS cv_url',
          'sr.name AS cv_custom_name',
          'r.category AS profession_category',
        ])
        .from('job_applications', 'app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .leftJoin('seeker_profiles', 'sp', 'sp.id = app.seeker_id')
        .leftJoin('users', 'u', 'u.id = sp.user_id')
        .leftJoin('roles', 'r', 'r.id = sp.profession_id')
        .leftJoin('experiences', 'exp', 'exp.id = sp.experiences_id')
        .leftJoin('education_levels', 'edu', 'edu.id = sp.education_id')
        .leftJoin(
          'seeker_resumes',
          'sr',
          'sr.id = app.resume_id AND sr.is_deleted = 0',
        )
        .where('job.company_id = :companyId', { companyId })
        .andWhere('app.job_post_id = :jobId', { jobId });

      const applications = await query
        .orderBy('app.applied_at', 'DESC')
        .skip(offset)
        .take(limit)
        .getRawMany();

      const countQuery = this.dataSource
        .createQueryBuilder()
        .select('COUNT(DISTINCT app.id)', 'count')
        .from('job_applications', 'app')
        .leftJoin('job_posts', 'job', 'job.id = app.job_post_id')
        .where('job.company_id = :companyId', { companyId })
        .andWhere('app.job_post_id = :jobId', { jobId });

      const countResult = await countQuery.getRawOne();
      const total = parseInt(countResult?.count || '0') || 0;

      const processedApplications = applications.map((app) => {
        let cvFilename = '';
        if (app.cv_custom_name && app.cv_url) {
          const fileExt = path.extname(app.cv_url).toLowerCase() || '.pdf';
          cvFilename = app.cv_custom_name;
          if (!cvFilename.toLowerCase().endsWith(fileExt)) {
            cvFilename += fileExt;
          }
        }
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const { cv_custom_name, ...restApp } = app;
        return {
          ...restApp,
          cv_filename: cvFilename,
        };
      });

      return {
        success: true,
        data: {
          applications: processedApplications,
          pagination: {
            page,
            limit,
            total,
            totalPages: Math.ceil(total / limit),
          },
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch company job applicants',
        error: error.message,
      };
    }
  }

  // Update company details
  async updateCompany(companyId: number, updateData: any) {
    try {
      const company = await this.companyRepository.findOne({
        where: { id: companyId, is_deleted: 0 },
      });

      if (!company) {
        throw new Error('Company not found');
      }

      const wasApproved = company.is_approved === 1;
      const wasRejected = company.is_rejected === 1;

      // Update company fields
      const allowedFields = [
        'name',
        'about',
        'vision',
        'founded_at',
        'location',
        'website',
        'industry_id',
        'organization_type_id',
        'company_size_id',
        'country_id',
        'state_id',
        'city_id',
        'pincode',
        'latitude',
        'longitude',
        'gst_number',
        'cin_number',
        'logo',
        'banner_url',
        'area_id',
        'status',
        'is_approved',
        'is_rejected',
      ];

      const updateFields: any = {};
      allowedFields.forEach((field) => {
        if (updateData[field] !== undefined) {
          // Normalize empty strings to null for nullable fields
          // 'name' is NOT nullable, so we keep it as is (though it should ideally not be empty)
          if (field !== 'name' && updateData[field] === '') {
            updateFields[field] = null;
          } else {
            updateFields[field] = updateData[field];
          }
        }
      });

      // Update company
      // IF admin company and email/phone provided, add them to updateFields
      if (company.is_admin_company === 1) {
        if (updateData.email !== undefined) {
          updateFields.email =
            updateData.email === '' ? null : updateData.email;
        }
        if (updateData.phone !== undefined) {
          updateFields.phone =
            updateData.phone === '' ? null : updateData.phone;
        }
      }

      if (Object.keys(updateFields).length > 0) {
        await this.companyRepository.update(companyId, updateFields);
      }

      // Update user fields if provided AND NOT admin company
      // For Admin Companies, we update the company table directly (above), not the user table
      if (
        (updateData.email || updateData.phone) &&
        company.is_admin_company !== 1
      ) {
        const userUpdateFields: any = {};
        if (updateData.email) userUpdateFields.email = updateData.email;
        if (updateData.phone) userUpdateFields.phone = updateData.phone;

        await this.userRepository.update(company.user_id, userUpdateFields);
      }

      // Update social links if provided
      if (updateData.social_links && Array.isArray(updateData.social_links)) {
        // Delete existing links
        await this.companySocialLinksRepository.delete({ companyId });

        // Insert new links
        const socialLinks = updateData.social_links.map((link: any) => ({
          companyId,
          platformId: link.platform_id,
          url: link.url,
        }));

        if (socialLinks.length > 0) {
          await this.companySocialLinksRepository.save(socialLinks);
        }
      }

      // Update key people if provided
      if (updateData.key_people && Array.isArray(updateData.key_people)) {
        // Delete existing key people
        await this.dataSource.query(
          'DELETE FROM company_key_people WHERE company_id = ?',
          [companyId],
        );

        // Insert new key people
        if (updateData.key_people.length > 0) {
          for (const person of updateData.key_people) {
            await this.dataSource.query(
              'INSERT INTO company_key_people (company_id, name, designation, email, phone, visibility_level) VALUES (?, ?, ?, ?, ?, ?)',
              [
                companyId,
                person.name,
                person.designation,
                person.email || null,
                person.phone || null,
                person.visibility_level || 'name_designation',
              ],
            );
          }
        }
      }

      const isNewlyApproved = (updateFields.is_approved === 1 || updateFields.status === 1) && !wasApproved;
      const isNewlyRejected = updateFields.is_rejected === 1 && !wasRejected;

      // Check for approval/rejection triggers
      if (company.user_id) {
        if (isNewlyApproved) {
          (async () => {
            try {
              const recruiterUser = await this.userRepository.findOne({
                where: { id: company.user_id }
              });
              if (recruiterUser && recruiterUser.email) {
                const countResult = await this.dataSource.query(
                  `SELECT COUNT(*) as totalSeekers FROM users WHERE role = 'seeker' AND status = 'active'`
                );
                const totalSeekers = countResult[0]?.totalSeekers || 0;

                const sampleSeekers = await this.dataSource.query(
                  `SELECT u.full_name, r.name as profession_name,
                      (
                        SELECT GROUP_CONCAT(sk.skill_name SEPARATOR ', ')
                        FROM seeker_skills ss
                        INNER JOIN skills sk ON sk.id = ss.skill_id
                        WHERE ss.seeker_id = sp.id
                      ) as skills
                    FROM seeker_profiles sp 
                    INNER JOIN users u ON u.id = sp.user_id 
                    LEFT JOIN roles r ON r.id = sp.profession_id
                    WHERE u.role = 'seeker' AND u.status = 'active'
                    ORDER BY u.id DESC 
                    LIMIT 3`
                );

                await this.emailService.sendTemplateEmail(
                  'recruiter_profile_approved',
                  recruiterUser.email,
                  recruiterUser.fullName || 'Recruiter',
                  {
                    preferred_name: recruiterUser.fullName || 'Recruiter',
                    candidate_count: totalSeekers,
                    candidates: sampleSeekers.map((s: any) => ({
                      full_name: s.full_name,
                      profession_name: s.profession_name || 'Professional Seeker',
                      skills: s.skills || 'Not Specified'
                    }))
                  }
                );
              }
            } catch (err) {
              console.error('Failed to send recruiter approved email from dashboard service:', err);
            }
          })();
        } else if (isNewlyRejected) {
          (async () => {
            try {
              const recruiterUser = await this.userRepository.findOne({
                where: { id: company.user_id }
              });
              if (recruiterUser && recruiterUser.email) {
                const preferredName =
                  (recruiterUser.fullName && recruiterUser.fullName.trim().length > 0 && recruiterUser.fullName.trim()) ||
                  recruiterUser.email.split('@')[0];

                const context = {
                  preferred_name: preferredName,
                  company_name: company.name || '',
                  company_context: company.name ? ` for <strong>${company.name}</strong>` : '',
                };

                await this.emailService.sendTemplateEmail(
                  'recruiter_profile_rejected',
                  recruiterUser.email,
                  preferredName,
                  context
                );
              }
            } catch (err) {
              console.error('Failed to send recruiter rejection email from dashboard service:', err);
            }
          })();
        }
      }

      // Return updated company details
      return await this.getCompanyDetails(companyId);
    } catch (error) {
      console.error('Error updating company:', error);
      throw error;
    }
  }

  // Toggle franchise company status (activate/deactivate)
  async toggleFranchiseCompanyStatus(companyId: number) {
    try {
      //console.log('Service: Starting toggle for company ID:', companyId);

      const company = await this.companyRepository.findOne({
        where: { id: companyId },
      });

      // console.log('Service: Found company:', company);

      if (!company) {
        throw new Error('Company not found');
      }

      // Toggle status: 1 = active, 0 = inactive
      const newStatus = company.status === 1 ? 0 : 1;
      const action = newStatus === 1 ? 'activated' : 'deactivated';

      // console.log(
      //   'Service: Current status:',
      //   company.status,
      //   'New status:',
      //   newStatus,
      // );

      await this.companyRepository.update(companyId, { status: newStatus });

      // console.log('Service: Database updated successfully');

      return {
        success: true,
        message: `Company ${action} successfully`,
        companyId: companyId,
        newStatus: newStatus,
        data: {
          newStatus: newStatus,
          status: newStatus === 1 ? 'active' : 'inactive',
          c_status: newStatus,
        },
      };
    } catch (error) {
      console.error('Error toggling company status:', error);
      throw error;
    }
  }

  // Get recent activity for a company
  async getCompanyRecentActivity(companyId: number) {
    try {
      const activities = [];

      // Get recent job posts (only open jobs)
      const recentJobPosts = await this.dataSource
        .createQueryBuilder()
        .select([
          'jp.id as id',
          'jp.title as title',
          'jp.created_at as created_at',
          'jp.status as status',
        ])
        .from('job_posts', 'jp')
        .where('jp.company_id = :companyId', { companyId })
        .orderBy('jp.created_at', 'DESC')
        .limit(5)
        .getRawMany();

      // Get recent applications
      const recentApplications = await this.dataSource
        .createQueryBuilder()
        .select([
          'ja.id',
          'ja.applied_at as applied_at',
          'jp.title as job_title',
          'u.full_name as candidate_name',
          'ja.status',
          'u.id as userId',
          'sp.id as seekerId',
          'u.id as user_id',
          'sp.id as seeker_id',
        ])
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .leftJoin('seeker_profiles', 'sp', 'sp.id = ja.seeker_id')
        .leftJoin('users', 'u', 'u.id = sp.user_id')
        .where('jp.company_id = :companyId', { companyId })
        .orderBy('ja.applied_at', 'DESC')
        .limit(5)
        .getRawMany();

      // Get recent profile views
      const recentViews = await this.dataSource
        .createQueryBuilder()
        .select(['jv.id', 'jv.viewed_at', 'jp.title as job_title'])
        .from('job_views', 'jv')
        .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
        .where('jp.company_id = :companyId', { companyId })
        .orderBy('jv.viewed_at', 'DESC')
        .limit(5)
        .getRawMany();

      // Combine and sort all activities
      recentJobPosts.forEach((job) => {
        activities.push({
          type: 'job_posted',
          title: `New job posted: ${job.title}`,
          description: job.title,
          timestamp: job.created_at,
          status: job.status,
        });
      });

      recentApplications.forEach((app) => {
        // console.log('Application data:', app); // Debug log
        activities.push({
          type: 'application_received',
          title: `Application received: ${app.candidate_name}`,
          description: `${app.candidate_name} applied for ${app.job_title}`,
          timestamp: app.applied_at || new Date().toISOString(), // Fallback to current date if applied_at is null
          status: app.status,
          user_id: app.user_id,
          seeker_id: app.seeker_id,
        });
      });

      recentViews.forEach((view) => {
        activities.push({
          type: 'profile_viewed',
          title: `Profile viewed: ${view.job_title}`,
          description: `Job post "${view.job_title}" was viewed`,
          timestamp: view.viewed_at,
        });
      });

      // Sort by timestamp (most recent first) and limit to 10
      activities.sort(
        (a, b) =>
          new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
      );
      activities.splice(10);

      return {
        success: true,
        data: activities,
      };
    } catch (error) {
      console.error('Error fetching company recent activity:', error);
      console.error('Error details:', {
        message: error.message,
        stack: error.stack,
        code: error.code,
        errno: error.errno,
        sqlMessage: error.sqlMessage,
        sqlState: error.sqlState,
      });
      return {
        success: false,
        message: 'Failed to fetch recent activity',
        error: error.message,
      };
    }
  }

  //dashboard comapny settings

  // Get company settings for a recruiter
  async getCompanySettings(userId: number) {
    try {
      // Get company details with all related data
      const company = await this.dataSource
        .createQueryBuilder()
        .select([
          'c.id as c_id',
          'c.name',
          'c.logo',
          'c.banner_url',
          'c.about',
          'c.vision',
          'c.founded_at as c_founded_at',
          'c.location',
          'c.website',
          'c.industry_id',
          'c.organization_type_id',
          'c.company_size_id',
          'c.country_id',
          'c.state_id',
          'c.city_id',
          'c.pincode',
          'c.area_id',
          'c.latitude',
          'c.longitude',
          'c.gst_number',
          'c.cin_number',
          'c.is_admin_company',
          'c.email as c_email',
          'c.phone as c_phone',
          'u.email',
          'u.phone',
          'u.full_name',
          'u.username',
          'i.name as industry_name',
          'ot.label as organization_type_name',
          'cs.label as company_size_name',
          'co.country_name',
          's.state_name',
          'ci.city_name',
        ])
        .from('companies', 'c')
        .leftJoin('users', 'u', 'u.id = c.user_id')
        .leftJoin('industries', 'i', 'i.id = c.industry_id')
        .leftJoin('organization_types', 'ot', 'ot.id = c.organization_type_id')
        .leftJoin('company_sizes', 'cs', 'cs.id = c.company_size_id')
        .leftJoin('country', 'co', 'co.country_id = c.country_id')
        .leftJoin('state', 's', 's.state_id = c.state_id')
        .leftJoin('city', 'ci', 'ci.city_id = c.city_id')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .printSql()
        .getRawOne();

      // console.log('DEBUG: Fetched company for settings:', company);

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      // Override email and phone for admin companies
      // Override email and phone for admin companies
      const isAdminCompany =
        company.is_admin_company === 1 || company.c_is_admin_company === 1;

      if (isAdminCompany) {
        company.email = company.c_email;
        company.phone = company.c_phone;
      } else {
        company.email = company.u_email || company.email;
        company.phone = company.u_phone || company.phone;
      }

      // Get key people
      const keyPeople = await this.companyKeyPeopleRepository.find({
        where: { company_id: company.c_id },
      });

      // Get company social links
      const socialLinks = await this.dataSource
        .createQueryBuilder()
        .select([
          'csl.id as id',
          'csl.platform_id as platform_id',
          'csl.url as url',
          'sp.name as platform_name',
        ])
        .from('company_social_links', 'csl')
        .leftJoin('social_platform', 'sp', 'sp.id = csl.platform_id')
        .where('csl.company_id = :companyId', { companyId: company.c_id })
        .getRawMany();

      return {
        success: true,
        data: {
          company: {
            ...company,
            key_people: keyPeople,
          },
          socialLinks,
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch company settings',
        error: error.message,
      };
    }
  }

  // Update company basic information
  async updateCompanyBasicInfo(userId: number, data: any) {
    try {
      // console.log('🔍 Backend - updateCompanyBasicInfo');
      // console.log('   userId:', userId);
      // console.log('   data received:', JSON.stringify(data, null, 2));
      // console.log('   data.area_id:', data.area_id);

      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select(['c.id as id', 'c.is_admin_company as is_admin_company'])
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      // console.log('Found company:', company);

      if (!company) {
        // console.log('Error: Company not found for user ID:', userId);
        return { success: false, message: 'Company not found for this user' };
      }

      // Validate that at least one of GST or CIN is provided if both are being updated
      if (data.gst_number !== undefined && data.cin_number !== undefined) {
        const newGstNumber = data.gst_number === '' ? null : data.gst_number;
        const newCinNumber = data.cin_number === '' ? null : data.cin_number;

        if (!newGstNumber && !newCinNumber) {
          return {
            success: false,
            message: 'Either GST number or CIN number is required',
          };
        }
      }

      const updateData: any = {};

      if (data.name !== undefined)
        updateData.name = data.name === '' ? null : data.name;
      if (data.about !== undefined)
        updateData.about = data.about === '' ? null : data.about;
      if (data.vision !== undefined)
        updateData.vision = data.vision === '' ? null : data.vision;
      if (data.logo !== undefined)
        updateData.logo = data.logo === '' ? null : data.logo;
      if (data.banner_url !== undefined)
        updateData.banner_url = data.banner_url === '' ? null : data.banner_url;
      if (data.website !== undefined)
        updateData.website = data.website === '' ? null : data.website;
      if (data.founded_at !== undefined)
        updateData.founded_at = data.founded_at === '' ? null : data.founded_at;
      if (data.location !== undefined)
        updateData.location = data.location === '' ? null : data.location;
      if (data.industry_id !== undefined)
        updateData.industry_id =
          data.industry_id === '' ? null : data.industry_id;
      if (data.organization_type_id !== undefined)
        updateData.organization_type_id =
          data.organization_type_id === '' ? null : data.organization_type_id;
      if (data.company_size_id !== undefined)
        updateData.company_size_id =
          data.company_size_id === '' ? null : data.company_size_id;
      if (data.country_id !== undefined)
        updateData.country_id = data.country_id === '' ? null : data.country_id;
      if (data.state_id !== undefined)
        updateData.state_id = data.state_id === '' ? null : data.state_id;
      if (data.city_id !== undefined)
        updateData.city_id = data.city_id === '' ? null : data.city_id;
      if (data.pincode !== undefined)
        updateData.pincode = data.pincode === '' ? null : data.pincode;
      if (data.area_id !== undefined)
        updateData.area_id = data.area_id === '' ? null : data.area_id;
      if (data.latitude !== undefined)
        updateData.latitude = data.latitude === '' ? null : data.latitude;
      if (data.longitude !== undefined)
        updateData.longitude = data.longitude === '' ? null : data.longitude;
      if (data.gst_number !== undefined)
        updateData.gst_number = data.gst_number === '' ? null : data.gst_number;
      if (data.cin_number !== undefined)
        updateData.cin_number = data.cin_number === '' ? null : data.cin_number;

      // Handle Email/Phone for JV (Admin) Companies
      if (company.is_admin_company === 1) {
        if (data.email !== undefined)
          updateData.email = data.email === '' ? null : data.email;
        if (data.phone !== undefined)
          updateData.phone = data.phone === '' ? null : data.phone;
      }

      // console.log('📝 Update data to be applied:', JSON.stringify(updateData, null, 2));
      // console.log('   Company ID to update:', company.id);
      // console.log('   updateData.area_id:', updateData.area_id);

      if (Object.keys(updateData).length > 0) {
        await this.dataSource
          .createQueryBuilder()
          .update('companies')
          .set(updateData)
          .where('id = :companyId', { companyId: company.id })
          .execute();
      }

      // Handle Email/Phone for Regular Companies (Update User Table)
      if (
        company.is_admin_company !== 1 &&
        (data.email !== undefined || data.phone !== undefined)
      ) {
        const userUpdate: any = {};
        if (data.email !== undefined) userUpdate.email = data.email;
        if (data.phone !== undefined) userUpdate.phone = data.phone;

        if (Object.keys(userUpdate).length > 0) {
          await this.userRepository.update(userId, userUpdate);
        }
      }

      // Save Key People
      if (data.key_people) {
        // Delete existing key people
        await this.companyKeyPeopleRepository.delete({
          company_id: company.id,
        });

        // Insert new key people
        if (Array.isArray(data.key_people) && data.key_people.length > 0) {
          const newKeyPeople = data.key_people
            .filter((p: any) => p.name && p.name.trim() !== '') // Filter out empty names
            .map((p: any) => ({
              company_id: company.id,
              name: p.name,
              designation: p.designation,
              email: p.email,
              phone: p.phone,
              visibility_level: p.visibility_level || 'full_contact',
              created_at: new Date(),
              updated_at: new Date(),
            }));

          if (newKeyPeople.length > 0) {
            await this.companyKeyPeopleRepository.save(newKeyPeople);
          }
        }
      }

      // console.log(' Database update completed successfully');

      return {
        success: true,
        message: 'Company information updated successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to update company information',
        error: error.message,
      };
    }
  }

  // Update company social links
  async updateCompanySocialLinks(userId: number, socialLinks: any[]) {
    try {
      // console.log('=== Backend Service Social Links Debug ===');
      // console.log('Service received userId:', userId);
      // console.log('Service received socialLinks:', socialLinks);

      // Get company for this user with more detailed query
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id as id, c.name, c.user_id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      // console.log('Found company:', company);

      // If no company found, let's check what companies exist for this user
      if (!company) {
        // const allUserCompanies = await this.dataSource
        //   .createQueryBuilder()
        //   .select('c.id, c.name, c.user_id, c.is_deleted')
        //   .from('companies', 'c')
        //   .where('c.user_id = :userId', { userId })
        //   .getRawMany();

        // console.log(
        //   'All companies for user (including deleted):',
        //   allUserCompanies,
        // );
        return { success: false, message: 'Company not found for this user' };
      }

      // // Let's also verify the company exists in the companies table
      // const companyExists = await this.dataSource
      //   .createQueryBuilder()
      //   .select('c.id')
      //   .from('companies', 'c')
      //   .where('c.id = :companyId', { companyId: company?.id })
      //   .getRawOne();

      // // console.log('Company exists check:', companyExists);

      // // Let's also check what companies exist for this user
      // const userCompanies = await this.dataSource
      //   .createQueryBuilder()
      //   .select('c.id, c.name, c.user_id')
      //   .from('companies', 'c')
      //   .where('c.user_id = :userId', { userId })
      //   .getRawMany();

      // console.log('All companies for user:', userCompanies);

      if (!company) {
        // console.log('Error: Company not found for user ID:', userId);
        return { success: false, message: 'Company not found for this user' };
      }

      // console.log('Company ID to update:', company.id);
      // console.log('Company ID type:', typeof company.id);

      // Update existing social links or insert new ones
      if (socialLinks && socialLinks.length > 0) {
        // console.log('Updating social links:', socialLinks.length);
        for (const link of socialLinks) {
          // console.log('Processing link:', link);
          // console.log('Update values:', {
          //   company_id: company.id,
          //   platform_id: link.platform_id,
          //   url: link.url,
          // });
          try {
            // Check if social link exists for this company and platform
            const existingLink = await this.dataSource.query(
              'SELECT id FROM company_social_links WHERE company_id = ? AND platform_id = ?',
              [company.id, link.platform_id],
            );

            if (existingLink.length > 0) {
              // Update existing record
              await this.dataSource.query(
                'UPDATE company_social_links SET url = ? WHERE company_id = ? AND platform_id = ?',
                [link.url, company.id, link.platform_id],
              );
              // console.log('Successfully updated existing link');
            } else {
              // Insert new record
              await this.dataSource.query(
                'INSERT INTO company_social_links (company_id, platform_id, url) VALUES (?, ?, ?)',
                [company.id, link.platform_id, link.url],
              );
              // console.log('Successfully inserted new link');
            }
          } catch (error) {
            // console.error('Error processing link:', error);
            throw error;
          }
        }
      } else {
        // console.log('No social links to update');
      }

      // console.log('Social links update completed successfully');
      return { success: true, message: 'Social links updated successfully' };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to update social links',
        error: error.message,
      };
    }
  }

  // Update user contact information
  async updateRecruiterContactInfo(userId: number, data: any) {
    try {
      // Check if user exists
      const user = await this.dataSource
        .createQueryBuilder()
        .select('u.id')
        .from('users', 'u')
        .where('u.id = :userId', { userId })
        .getRawOne();

      if (!user) {
        return { success: false, message: 'User not found' };
      }

      // Build update query
      let updateQuery = this.dataSource
        .createQueryBuilder()
        .update('users')
        .where('id = :userId', { userId });

      const updateData: any = {};

      if (data.email !== undefined) updateData.email = data.email;
      if (data.phone !== undefined) updateData.phone = data.phone;
      if (data.username !== undefined) updateData.username = data.username;
      if (data.full_name !== undefined) updateData.full_name = data.full_name;

      if (Object.keys(updateData).length > 0) {
        updateQuery = updateQuery.set(updateData);
        await updateQuery.execute();
      }

      return {
        success: true,
        message: 'Contact information updated successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to update contact information',
        error: error.message,
      };
    }
  }

  // Update user password
  async updateRecruiterPassword(userId: number, data: any) {
    try {
      // Check if user exists
      const user = await this.dataSource
        .createQueryBuilder()
        .select('u.id')
        .from('users', 'u')
        .where('u.id = :userId', { userId })
        .getRawOne();

      if (!user) {
        return { success: false, message: 'User not found' };
      }

      // Check if OTP verification is required
      if (data.requireOTP && !data.otp) {
        return { success: false, message: 'OTP verification required' };
      }

      // If OTP is provided, verify it
      if (data.otp) {
        const otpVerification = await this.verifyOTPForPasswordChange(
          userId,
          data.otp,
        );
        if (!otpVerification.success) {
          return { success: false, message: otpVerification.message };
        }
      }

      if (!data.newPassword || !data.confirmPassword) {
        return {
          success: false,
          message: 'Both new and confirm password are required',
        };
      }

      if (data.newPassword !== data.confirmPassword) {
        return { success: false, message: 'Passwords do not match' };
      }

      // Update password using QueryBuilder
      await this.dataSource
        .createQueryBuilder()
        .update('users')
        .set({ password: data.newPassword })
        .where('id = :userId', { userId })
        .execute();

      // Clear OTP after successful password change
      if (data.otp) {
        await this.clearOTPForUser(userId);
      }

      return { success: true, message: 'Password updated successfully' };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to update password',
        error: error.message,
      };
    }
  }

  // Delete company account
  async deleteCompanyAccount(userId: number) {
    try {
      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id as id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      // Soft delete the company and set status to inactive
      await this.dataSource
        .createQueryBuilder()
        .update('companies')
        .set({
          is_deleted: 1,
          status: 0, // Set company status to inactive
        })
        .where('id = :companyId', { companyId: company.id })
        .execute();

      // Deactivate the user
      await this.dataSource
        .createQueryBuilder()
        .update('users')
        .set({
          status: 'inactive',
          deletedAt: new Date(),
        })
        .where('id = :userId', { userId })
        .execute();

      return { success: true, message: 'Company account deleted successfully' };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to delete company account',
        error: error.message,
      };
    }
  }

  // Get all organization types
  async getOrganizationTypes() {
    try {
      const organizationTypes = await this.dataSource
        .createQueryBuilder()
        .select(['id', 'label'])
        .from('organization_types', 'ot')
        .orderBy('ot.label', 'ASC')
        .getRawMany();

      return { success: true, data: organizationTypes };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch organization types',
        error: error.message,
      };
    }
  }

  // Get all company sizes
  async getCompanySizes() {
    try {
      const companySizes = await this.dataSource
        .createQueryBuilder()
        .select(['id', 'label'])
        .from('company_sizes', 'cs')
        .orderBy('cs.label', 'ASC')
        .getRawMany();

      return { success: true, data: companySizes };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch company sizes',
        error: error.message,
      };
    }
  }

  // Get all industries
  async getIndustries() {
    try {
      const industries = await this.dataSource
        .createQueryBuilder()
        .select(['id', 'name'])
        .from('industries', 'i')
        .orderBy('i.name', 'ASC')
        .getRawMany();

      return { success: true, data: industries };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch industries',
        error: error.message,
      };
    }
  }

  // Get social platforms for company settings
  async getCompanySocialPlatforms() {
    try {
      const platforms = await this.dataSource
        .createQueryBuilder()
        .select(['id', 'name'])
        .from('social_platform', 'sp')
        .orderBy('sp.name', 'ASC')
        .getRawMany();

      return { success: true, data: platforms };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch social platforms',
        error: error.message,
      };
    }
  }

  // Upload company logo
  async uploadCompanyLogo(userId: number, file: Express.Multer.File) {
    try {
      // console.log('Uploading logo for userId:', userId);
      // console.log('File details:', {
      //   filename: file.filename,
      //   path: file.path,
      //   size: file.size,
      // });

      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id as id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      // console.log('Found company:', company);

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      // Process and resize the logo image using Sharp
      let processedFilePath: string;
      try {
        processedFilePath = await this.imageProcessorService.processCompanyLogo(
          file.path,
        );
      } catch (processError) {
        console.error('Error processing logo image:', processError);
        // If image processing fails, use the original file
        processedFilePath = file.path.replace(/\\/g, '/');
      }

      // Store the processed file path in the database
      const filePath = processedFilePath.replace(/\\/g, '/');

      // Defensive check: Ensure we're not saving a directory path
      if (
        filePath.endsWith('/') ||
        filePath.split('/').pop() === '' ||
        (fs.existsSync(processedFilePath) &&
          fs.lstatSync(processedFilePath).isDirectory())
      ) {
        return {
          success: false,
          message: 'Invalid file path: expected a file, got a directory',
        };
      }

      // Update company logo
      await this.dataSource
        .createQueryBuilder()
        .update('companies')
        .set({ logo: filePath })
        .where('id = :companyId', { companyId: company.id })
        .execute();

      // console.log('Update result:', updateResult);

      // Also update user's profile picture for header display (company logo serves as profile picture for recruiters)
      await this.dataSource
        .createQueryBuilder()
        .update('users')
        .set({ profilePicture: filePath })
        .where('id = :userId', { userId })
        .execute();

      // console.log('Updated user profile picture for userId:', userId);

      return {
        success: true,
        message: 'Logo uploaded and processed successfully',
        data: { logo: filePath },
      };
    } catch (error) {
      console.error('Error uploading logo:', error);
      return {
        success: false,
        message: 'Failed to upload logo',
        error: error.message,
      };
    }
  }

  // Upload company banner
  async uploadCompanyBanner(userId: number, file: Express.Multer.File) {
    try {
      //console.log('Uploading banner for userId:', userId);
      //console.log('File details:', {
      // filename: file.filename,
      // path: file.path,
      // size: file.size,
      //});

      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id as id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      //console.log('Found company:', company);

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      // Process and resize the banner image using Sharp
      let processedFilePath: string;
      try {
        processedFilePath =
          await this.imageProcessorService.processCompanyBanner(file.path);
      } catch (processError) {
        console.error('Error processing banner image:', processError);
        // If image processing fails, use the original file
        processedFilePath = file.path.replace(/\\/g, '/');
      }

      // Store the processed file path in the database
      const filePath = processedFilePath.replace(/\\/g, '/');

      // Defensive check: Ensure we're not saving a directory path
      if (
        filePath.endsWith('/') ||
        filePath.split('/').pop() === '' ||
        (fs.existsSync(processedFilePath) &&
          fs.lstatSync(processedFilePath).isDirectory())
      ) {
        return {
          success: false,
          message: 'Invalid file path: expected a file, got a directory',
        };
      }

      // Update company banner
      await this.dataSource
        .createQueryBuilder()
        .update('companies')
        .set({ banner_url: filePath })
        .where('id = :companyId', { companyId: company.id })
        .execute();

      // console.log('Update result:', updateResult);

      return {
        success: true,
        message: 'Banner uploaded and processed successfully',
        data: { banner_url: filePath },
      };
    } catch (error) {
      console.error('Error uploading banner:', error);
      return {
        success: false,
        message: 'Failed to upload banner',
        error: error.message,
      };
    }
  }

  // Generic upload logo file (returns URL, no DB update)
  async uploadLogoFile(file: Express.Multer.File) {
    console.log(
      '[UploadLogo] Processing file:',
      file?.originalname,
      'at',
      file?.path,
    );
    try {
      if (!file || !file.path) {
        throw new Error('File object or path is missing');
      }

      // Process and resize the logo image using Sharp
      let processedFilePath: string;
      try {
        processedFilePath = await this.imageProcessorService.processCompanyLogo(
          file.path,
        );
      } catch (processError) {
        console.error(
          '[UploadLogo] Sharp processing failed, falling back to original:',
          processError.message,
        );
        processedFilePath = file.path;
      }

      // Store the processed file path (ensure it's a relative URL path for the web)
      let filePath = processedFilePath.replace(/\\/g, '/');

      // Force normalization to 'uploads/...'
      const logoIndex = filePath.indexOf('uploads/');
      if (logoIndex !== -1) {
        filePath = filePath.substring(logoIndex);
      }

      console.log('[UploadLogo] Success. Returning path:', filePath);
      return {
        success: true,
        data: { logo: filePath },
      };
    } catch (error) {
      console.error('[UploadLogo] Fatal Error:', error);
      return {
        success: false,
        message: 'Failed to upload logo: ' + error.message,
      };
    }
  }

  // Generic upload banner file (returns URL, no DB update)
  async uploadBannerFile(file: Express.Multer.File) {
    console.log(
      '[UploadBanner] Processing file:',
      file?.originalname,
      'at',
      file?.path,
    );
    try {
      if (!file || !file.path) {
        throw new Error('File object or path is missing');
      }

      // Process and resize the banner image using Sharp
      let processedFilePath: string;
      try {
        processedFilePath =
          await this.imageProcessorService.processCompanyBanner(file.path);
      } catch (processError) {
        console.error(
          '[UploadBanner] Sharp processing failed, falling back to original:',
          processError.message,
        );
        processedFilePath = file.path;
      }

      // Store the processed file path (ensure it's a relative URL path for the web)
      let filePath = processedFilePath.replace(/\\/g, '/');

      // Force normalization to 'uploads/...'
      const bannerIndex = filePath.indexOf('uploads/');
      if (bannerIndex !== -1) {
        filePath = filePath.substring(bannerIndex);
      }

      console.log('[UploadBanner] Success. Returning path:', filePath);
      return {
        success: true,
        data: { banner_url: filePath },
      };
    } catch (error) {
      console.error('[UploadBanner] Fatal Error:', error);
      return {
        success: false,
        message: 'Failed to upload banner: ' + error.message,
      };
    }
  }
  //remove company logo and banner

  // Helper method to safely delete a file with retry logic
  private async safeDeleteFile(
    filePath: string,
    maxRetries: number = 5,
    delayMs: number = 200,
  ): Promise<boolean> {
    // Normalize path - handle both relative and absolute paths
    const normalizedPath = path.isAbsolute(filePath)
      ? filePath
      : path.join(process.cwd(), filePath);

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        if (fs.existsSync(normalizedPath)) {
          // Wait a bit longer on Windows to ensure file handles are released
          if (attempt > 0) {
            await new Promise((resolve) =>
              setTimeout(resolve, delayMs * attempt),
            );
          }
          await fs.promises.unlink(normalizedPath);
          return true; // Success
        }
        return true; // File doesn't exist, consider it deleted
      } catch (error: any) {
        const errorCode = error?.code || error?.errno;
        if (
          errorCode === 'EPERM' ||
          errorCode === 'EBUSY' ||
          errorCode === 'ENOENT'
        ) {
          // File is locked, permission denied, or doesn't exist
          if (attempt < maxRetries - 1) {
            // Wait longer between retries
            await new Promise((resolve) =>
              setTimeout(resolve, delayMs * (attempt + 1)),
            );
            continue;
          }
          // Last attempt failed
          console.warn(
            `Unable to delete file ${normalizedPath} after ${maxRetries} attempts. File may be in use or already deleted.`,
          );
          return false;
        } else {
          // Different error
          console.error(`Error deleting file ${normalizedPath}:`, error);
          return false;
        }
      }
    }
    return false;
  }

  // Delete company logo
  async deleteCompanyLogo(userId: number) {
    try {
      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id as id, c.logo')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      if (!company.logo) {
        return { success: false, message: 'No logo found to delete' };
      }

      const logoPath = company.logo;

      // Update company logo to null first (before deleting file to avoid issues if deletion fails)
      await this.dataSource
        .createQueryBuilder()
        .update('companies')
        .set({ logo: null })
        .where('id = :companyId', { companyId: company.id })
        .execute();

      // Also clear user's profile picture since it was the company logo
      await this.dataSource
        .createQueryBuilder()
        .update('users')
        .set({ profilePicture: null })
        .where('id = :userId', { userId })
        .execute();

      // Delete the physical file (non-blocking - don't fail if file deletion fails)
      try {
        const deleted = await this.safeDeleteFile(logoPath);
        if (!deleted) {
          console.warn(
            `Failed to delete logo file: ${logoPath}. Database record updated but file may still exist.`,
          );
        }
      } catch (fileError) {
        // Log but don't fail the operation - database is already updated
        console.warn(`Error deleting logo file ${logoPath}:`, fileError);
      }

      return {
        success: true,
        message: 'Logo deleted successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to delete logo',
        error: error.message,
      };
    }
  }

  // Delete company banner
  async deleteCompanyBanner(userId: number) {
    try {
      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id as id, c.banner_url')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      if (!company.banner_url) {
        return { success: false, message: 'No banner found to delete' };
      }

      const bannerPath = company.banner_url;

      // Update company banner to null first (before deleting file to avoid issues if deletion fails)
      await this.dataSource
        .createQueryBuilder()
        .update('companies')
        .set({ banner_url: null })
        .where('id = :companyId', { companyId: company.id })
        .execute();

      // Delete the physical file (non-blocking - don't fail if file deletion fails)
      try {
        const deleted = await this.safeDeleteFile(bannerPath);
        if (!deleted) {
          console.warn(
            `Failed to delete banner file: ${bannerPath}. Database record updated but file may still exist.`,
          );
        }
      } catch (fileError) {
        // Log but don't fail the operation - database is already updated
        console.warn(`Error deleting banner file ${bannerPath}:`, fileError);
      }

      return {
        success: true,
        message: 'Banner deleted successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to delete banner',
        error: error.message,
      };
    }
  }

  // Location CRUD Operations
  // Get all countries
  async getCountries() {
    try {
      const countries = await this.dataSource
        .createQueryBuilder()
        .select([
          'country_id as id',
          'country_name as name',
          'country_code as code',
        ])
        .from('country', 'c')
        .orderBy('c.country_name', 'ASC')
        .getRawMany();

      return { success: true, data: countries };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch countries',
        error: error.message,
      };
    }
  }

  // // Get states by country
  // async getStatesByCountry(countryId: number) {
  //   try {
  //     const states = await this.dataSource
  //       .createQueryBuilder()
  //       .select(['state_id as id', 'state_name as name'])
  //       .from('state', 's')
  //       .where('s.country_id = :countryId', { countryId })
  //       .orderBy('s.state_name', 'ASC')
  //       .getRawMany();

  //     return { success: true, data: states };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to fetch states',
  //       error: error.message,
  //     };
  //   }
  // }

  // // Get cities by state
  // async getCitiesByState(stateId: number) {
  //   try {
  //     const cities = await this.dataSource
  //       .createQueryBuilder()
  //       .select(['city_id as id', 'city_name as name'])
  //       .from('city', 'c')
  //       .where('c.state_id = :stateId', { stateId })
  //       .orderBy('c.city_name', 'ASC')
  //       .getRawMany();

  //     return { success: true, data: cities };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to fetch cities',
  //       error: error.message,
  //     };
  //   }
  // }

  // // Insert new country
  // async insertCountry(data: { country_name: string; country_code: string }) {
  //   try {
  //     const result = await this.dataSource
  //       .createQueryBuilder()
  //       .insert()
  //       .into('country')
  //       .values({
  //         country_name: data.country_name,
  //         country_code: data.country_code,
  //         status: 1,
  //       })
  //       .execute();

  //     return {
  //       success: true,
  //       message: 'Country added successfully',
  //       data: { id: result.identifiers[0].country_id },
  //     };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to add country',
  //       error: error.message,
  //     };
  //   }
  // }

  // // Insert new state
  // async insertState(data: { state_name: string; country_id: number }) {
  //   try {
  //     const result = await this.dataSource
  //       .createQueryBuilder()
  //       .insert()
  //       .into('state')
  //       .values({
  //         state_name: data.state_name,
  //         country_id: data.country_id,
  //         status: 1,
  //       })
  //       .execute();

  //     return {
  //       success: true,
  //       message: 'State added successfully',
  //       data: { id: result.identifiers[0].state_id },
  //     };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to add state',
  //       error: error.message,
  //     };
  //   }
  // }

  // // Insert new city
  // async insertCity(data: { city_name: string; state_id: number }) {
  //   try {
  //     const result = await this.dataSource
  //       .createQueryBuilder()
  //       .insert()
  //       .into('city')
  //       .values({
  //         city_name: data.city_name,
  //         state_id: data.state_id,
  //         status: 1,
  //       })
  //       .execute();

  //     return {
  //       success: true,
  //       message: 'City added successfully',
  //       data: { id: result.identifiers[0].city_id },
  //     };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to add city',
  //       error: error.message,
  //     };
  //   }
  // }

  // Company Social Links CRUD Operations
  async getCompanySocialLinks(userId: number) {
    try {
      // Get company for this user
      const company = await this.dataSource
        .createQueryBuilder()
        .select('c.id')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      if (!company) {
        return { success: false, message: 'Company not found for this user' };
      }

      // Get social links for this company
      const socialLinks = await this.dataSource
        .createQueryBuilder()
        .select([
          'csl.id as id',
          'csl.company_id as company_id',
          'csl.platform_id as platform_id',
          'csl.url as url',
          'sp.name as platform_name',
        ])
        .from('company_social_links', 'csl')
        .leftJoin('social_platform', 'sp', 'sp.id = csl.platform_id')
        .where('csl.company_id = :companyId', { companyId: company.id })
        .getRawMany();

      return socialLinks;
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch social links',
        error: error.message,
      };
    }
  }

  async getCompanySocialLinksByCompanyId(companyId: number) {
    try {
      // Get social links for this company directly by company_id
      const socialLinks = await this.dataSource
        .createQueryBuilder()
        .select([
          'csl.id as id',
          'csl.company_id as company_id',
          'csl.platform_id as platform_id',
          'csl.url as url',
          'sp.name as platform_name',
        ])
        .from('company_social_links', 'csl')
        .leftJoin('social_platform', 'sp', 'sp.id = csl.platform_id')
        .where('csl.company_id = :companyId', { companyId })
        .getRawMany();

      return socialLinks;
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch social links',
        error: error.message,
      };
    }
  }

  async createCompanySocialLink(data: any) {
    try {
      const result = await this.dataSource
        .createQueryBuilder()
        .insert()
        .into('company_social_links')
        .values({
          company_id: data.company_id,
          platform_id: data.platform_id,
          url: data.url,
        })
        .execute();

      return {
        success: true,
        message: 'Social link created successfully',
        data: {
          id: result.identifiers[0]?.id || result.raw?.insertId,
          ...data,
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to create social link',
        error: error.message,
      };
    }
  }

  async updateCompanySocialLink(linkId: number, data: any) {
    try {
      await this.dataSource
        .createQueryBuilder()
        .update('company_social_links')
        .set({
          platform_id: data.platform_id,
          url: data.url,
        })
        .where('id = :linkId', { linkId })
        .execute();

      return {
        success: true,
        message: 'Social link updated successfully',
        data,
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to update social link',
        error: error.message,
      };
    }
  }

  async deleteCompanySocialLink(linkId: number) {
    try {
      await this.dataSource
        .createQueryBuilder()
        .delete()
        .from('company_social_links')
        .where('id = :linkId', { linkId })
        .execute();

      return {
        success: true,
        message: 'Social link deleted successfully',
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to delete social link',
        error: error.message,
      };
    }
  }

  // Profile Completion Methods
  async getSeekerProfileCompletion(userId: number) {
    try {
      // Get seeker profile with error handling for JSON parsing
      let seekerProfile;
      try {
        seekerProfile = await this.seekerProfileRepository.findOne({
          where: { user_id: userId },
        });
      } catch (jsonError) {
        console.error('JSON parsing error in seeker profile:', jsonError);
        // Try to get the profile using raw query to handle malformed JSON
        const rawProfile = await this.seekerProfileRepository.query(
          'SELECT * FROM seeker_profiles WHERE user_id = ?',
          [userId],
        );

        if (rawProfile && rawProfile.length > 0) {
          // Create a clean profile object, handling malformed JSON fields
          seekerProfile = {
            ...rawProfile[0],
            skills_ids: this.safeParseJson(rawProfile[0].skills_ids),
            languages_ids: this.safeParseJson(rawProfile[0].languages_ids),
          };
        }
      }

      if (!seekerProfile) {
        return {
          success: false,
          message: 'Seeker profile not found',
          completion: 0,
          missingFields: ['profile'],
        };
      }

      const seekerId = seekerProfile.id;

      // Fetch Role Category to apply conditional logic
      // Check both user's selectedRoleCategory AND profession category
      const user = await this.userRepository.findOne({
        where: { id: userId },
      });

      let isBlueCollar = false;

      // First check user's selected role category (most reliable)
      if (user?.selectedRoleCategory === 'blue_collar') {
        isBlueCollar = true;
      } else if (seekerProfile.profession_id) {
        // Fallback to checking profession category
        const role = await this.roleRepository.findOne({
          where: { id: seekerProfile.profession_id },
        });
        if (role?.category === 'blue_collar') {
          isBlueCollar = true;
        }
      }

      // Define required fields and their weights (common for both blue and white collar)
      const fieldChecks = [
        // Education ID check moved to conditional block below
        {
          field: 'date_of_birth',
          weight: 5,
          value: seekerProfile.date_of_birth,
        },
        { field: 'gender', weight: 5, value: seekerProfile.gender },
        // {
        //   field: 'marital_status',
        //   weight: 5,
        //   value: seekerProfile.marital_status,
        // },
      ];

      // Add fields conditionally based on role category
      if (isBlueCollar) {
        // Blue collar specific fields
        // Fetch role experience data for blue collar users
        const roleExperience =
          await this.seekerRoleExperienceRepository.findOne({
            where: { seeker_id: seekerId, status: 1 },
          });

        fieldChecks.push({
          field: 'role',
          weight: 10,
          value: roleExperience?.designation_id,
        });
        fieldChecks.push({
          field: 'experience_level',
          weight: 10,
          value: roleExperience?.experience_id,
        });
        fieldChecks.push({
          field: 'preferred_city',
          weight: 10,
          value: seekerProfile.preferred_work_city_id,
        });
        fieldChecks.push({
          field: 'preferred_locality',
          weight: 10,
          value: seekerProfile.preferred_work_locality_area_id,
        });
      } else {
        // White collar specific fields
        fieldChecks.push({
          field: 'profession_id',
          weight: 10,
          value: seekerProfile.profession_id,
        });
        fieldChecks.push({
          field: 'experiences_id',
          weight: 10,
          value: seekerProfile.experiences_id,
        });
        // fieldChecks.push({
        //   field: 'your_availability',
        //   weight: 5,
        //   value: seekerProfile.your_availability,
        // });
        fieldChecks.push({
          field: 'education_id',
          weight: 10,
          value: seekerProfile.education_id,
        });
        fieldChecks.push({
          field: 'professional_title',
          weight: 10,
          value: seekerProfile.professional_title,
        });
        // fieldChecks.push({
        //   field: 'bio_graphy',
        //   weight: 10,
        //   value: seekerProfile.bio_graphy,
        // });
      }

      // Check user profile picture (user already fetched above) - Skip for blue collar
      if (!isBlueCollar) {
        if (user?.profilePicture) {
          fieldChecks.push({
            field: 'profile_picture',
            weight: 5,
            value: user.profilePicture,
          });
        } else {
          fieldChecks.push({
            field: 'profile_picture',
            weight: 5,
            value: null,
          });
        }
      }

      // Check address
      // Check address - Skip for blue collar
      if (!isBlueCollar) {
        const address = await this.seekerAddressRepository.findOne({
          where: { seeker_profile_id: seekerId },
        });

        if (address) {
          fieldChecks.push({
            field: 'address',
            weight: 10,
            value: 'completed',
          });
        } else {
          fieldChecks.push({ field: 'address', weight: 10, value: null });
        }
      }

      // Check experiences
      // Check experiences - Skip for blue collar
      if (!isBlueCollar) {
        const experiences = await this.seekerExperienceRepository.count({
          where: { seeker_id: seekerId },
        });

        if (experiences > 0) {
          fieldChecks.push({
            field: 'experiences',
            weight: 10,
            value: 'completed',
          });
        } else {
          fieldChecks.push({ field: 'experiences', weight: 10, value: null });
        }
      }

      // Check education - Skip for blue collar
      if (!isBlueCollar) {
        const education = await this.seekerEducationRepository.count({
          where: { seeker_id: seekerId },
        });

        if (education > 0) {
          fieldChecks.push({
            field: 'education',
            weight: 10,
            value: 'completed',
          });
        } else {
          fieldChecks.push({ field: 'education', weight: 10, value: null });
        }
      }

      // Check skills - Skip for blue collar
      if (!isBlueCollar) {
        // console.log(' [PROFILE COMPLETION] Checking skills for seeker_id:', seekerId);
        const skillsFromTable = await this.seekerSkillRepository.count({
          where: { seeker_id: seekerId },
        });

        // Also check if skills_ids JSON field has data
        const [profileWithSkills] = await this.dataSource.query(
          'SELECT skills_ids FROM seeker_profiles WHERE id = ?',
          [seekerId],
        );
        // console.log(' [PROFILE COMPLETION] Skills from table:', skillsFromTable);
        // console.log(' [PROFILE COMPLETION] Skills JSON in seeker_profiles:', profileWithSkills?.skills_ids);

        let hasSkills = false;
        if (skillsFromTable > 0) {
          hasSkills = true;
        } else if (profileWithSkills?.skills_ids) {
          // Check if JSON field has data
          try {
            const skillsArray = JSON.parse(profileWithSkills.skills_ids);
            if (Array.isArray(skillsArray) && skillsArray.length > 0) {
              hasSkills = true;
            }
          } catch (e) {
            // Invalid JSON, treat as no skills
          }
        }

        if (hasSkills) {
          fieldChecks.push({ field: 'skills', weight: 5, value: 'completed' });
        } else {
          fieldChecks.push({ field: 'skills', weight: 5, value: null });
        }
      }

      // Check languages - Skip for blue collar
      if (!isBlueCollar) {
        // console.log(' [PROFILE COMPLETION] Checking languages for seeker_id:', seekerId);
        const languagesFromTable = await this.seekerLanguageRepository.count({
          where: { seeker_id: seekerId },
        });

        // Also check if languages_ids JSON field has data
        const [profileWithLanguages] = await this.dataSource.query(
          'SELECT languages_ids FROM seeker_profiles WHERE id = ?',
          [seekerId],
        );
        // console.log(' [PROFILE COMPLETION] Languages from table:', languagesFromTable);
        // console.log(' [PROFILE COMPLETION] Languages JSON in seeker_profiles:', profileWithLanguages?.languages_ids);

        let hasLanguages = false;
        if (languagesFromTable > 0) {
          hasLanguages = true;
        } else if (profileWithLanguages?.languages_ids) {
          // Check if JSON field has data
          try {
            const languagesArray = JSON.parse(
              profileWithLanguages.languages_ids,
            );
            if (Array.isArray(languagesArray) && languagesArray.length > 0) {
              hasLanguages = true;
            }
          } catch (e) {
            // Invalid JSON, treat as no languages
          }
        }

        if (hasLanguages) {
          fieldChecks.push({
            field: 'languages',
            weight: 5,
            value: 'completed',
          });
        } else {
          fieldChecks.push({ field: 'languages', weight: 5, value: null });
        }
      }

      // Check resumes (Only for non-blue collar)
      if (!isBlueCollar) {
        const resumes = await this.seekerResumeRepository.count({
          where: { seeker_id: seekerId, is_deleted: false },
        });

        if (resumes > 0) {
          fieldChecks.push({
            field: 'resumes',
            weight: 10,
            value: 'completed',
          });
        } else {
          fieldChecks.push({ field: 'resumes', weight: 10, value: null });
        }
      }

      // Calculate completion percentage
      let totalWeight = 0;
      let completedWeight = 0;
      const missingFields = [];

      fieldChecks.forEach((check) => {
        totalWeight += check.weight;
        if (check.value && check.value !== '') {
          completedWeight += check.weight;
        } else {
          missingFields.push(check.field);
        }
      });

      const completionPercentage =
        totalWeight > 0 ? Math.round((completedWeight / totalWeight) * 100) : 0;

      return {
        success: true,
        completion: completionPercentage,
        missingFields,
        totalFields: fieldChecks.length,
        completedFields: fieldChecks.length - missingFields.length,
      };
    } catch (error) {
      console.error('Error calculating seeker profile completion:', error);
      return {
        success: false,
        message: 'Failed to calculate profile completion',
        completion: 0,
        missingFields: [],
      };
    }
  }

  async getRecruiterProfileCompletion(userId: number) {
    try {
      // Get company profile
      const company = await this.dataSource
        .createQueryBuilder()
        .select('*')
        .from('companies', 'c')
        .where('c.user_id = :userId', { userId })
        .getRawOne();

      if (!company) {
        return {
          success: false,
          message: 'Company profile not found',
          completion: 0,
          missingFields: ['company_profile'],
        };
      }

      // Check user profile picture (for recruiters, company logo serves as profile picture)
      const user = await this.userRepository.findOne({
        where: { id: userId },
      });

      // Define required fields and their weights
      const fieldChecks = [
        { field: 'company_name', weight: 15, value: company.name },
        { field: 'about_us', weight: 15, value: company.about },
        {
          field: 'organization_type',
          weight: 10,
          value: company.organization_type_id,
        },
        { field: 'industry', weight: 10, value: company.industry_id },
        { field: 'company_size', weight: 10, value: company.company_size_id },
        { field: 'founded_at', weight: 5, value: company.founded_at },
        { field: 'website', weight: 10, value: company.website },
        { field: 'location', weight: 10, value: company.location },
        { field: 'vision', weight: 10, value: company.vision },
        // For recruiters, company logo serves as profile picture, so we only check one field
        {
          field: 'profile_picture',
          weight: 5,
          value: user?.profilePicture || company.logo,
        },
      ];

      // Check address fields
      if (company.country_id && company.state_id && company.city_id) {
        fieldChecks.push({ field: 'address', weight: 10, value: 'completed' });
      } else {
        fieldChecks.push({ field: 'address', weight: 10, value: null });
      }

      // Check social links
      const socialLinks = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('company_social_links', 'csl')
        .where('csl.company_id = :companyId', { companyId: company.id })
        .getRawOne();

      const socialLinksCount = parseInt(socialLinks?.count || '0');

      if (socialLinksCount > 0) {
        fieldChecks.push({
          field: 'social_links',
          weight: 5,
          value: 'completed',
        });
      } else {
        fieldChecks.push({ field: 'social_links', weight: 5, value: null });
      }

      // Calculate completion percentage
      let totalWeight = 0;
      let completedWeight = 0;
      const missingFields = [];

      fieldChecks.forEach((check) => {
        totalWeight += check.weight;
        if (check.value && check.value !== '') {
          completedWeight += check.weight;
        } else {
          missingFields.push(check.field);
        }
      });

      const completionPercentage =
        totalWeight > 0 ? Math.round((completedWeight / totalWeight) * 100) : 0;

      // console.log(`DashboardService: Profile completion for userId ${userId}: ${completionPercentage}%`, { missingFields });

      return {
        success: true,
        completion: completionPercentage,
        missingFields,
        totalFields: fieldChecks.length,
        completedFields: fieldChecks.length - missingFields.length,
      };
    } catch (error) {
      console.error('Error calculating recruiter profile completion:', error);
      return {
        success: false,
        message: 'Failed to calculate profile completion',
        completion: 0,
        missingFields: [],
      };
    }
  }

  // Franchise Dashboard Methods
  async getFranchiseIdByUserId(userId: number) {
    try {
      const query = `
        SELECT id as franchise_id
        FROM franchises 
        WHERE user_id = ?
      `;
      const result = await this.dataSource.query(query, [userId]);
      return result[0]?.franchise_id || null;
    } catch (error) {
      console.error('Error getting franchise ID by user ID:', error);
      return null;
    }
  }

  async getFranchiseOverview(franchiseId: number) {
    try {
      // Get total companies for this franchise
      const totalCompanies = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('companies', 'c')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      const activeCompanies = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('companies', 'c')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('c.status = 1')
        .getRawOne();

      const inactiveCompanies = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('companies', 'c')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('c.status = 0')
        .getRawOne();

      // Get total job posts for companies in this franchise
      const totalJobPosts = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      const activeJobPosts = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('jp.status = :status', { status: 'open' })
        .andWhere('jp.expires_at > NOW()')
        .getRawOne();

      const expiredJobPosts = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_posts', 'jp')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('jp.expires_at <= NOW()')
        .getRawOne();

      // Get total applications for companies in this franchise
      const totalApplications = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      // Get this month's applications
      const thisMonthApplications = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_applications', 'ja')
        .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('MONTH(ja.applied_at) = MONTH(NOW())')
        .andWhere('YEAR(ja.applied_at) = YEAR(NOW())')
        .getRawOne();

      // Get total views for companies in this franchise
      const totalViews = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_views', 'jv')
        .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .getRawOne();

      // Get this month's views
      const thisMonthViews = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('job_views', 'jv')
        .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
        .innerJoin('companies', 'c', 'c.id = jp.company_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('MONTH(jv.viewed_at) = MONTH(NOW())')
        .andWhere('YEAR(jv.viewed_at) = YEAR(NOW())')
        .getRawOne();

      return {
        success: true,
        data: {
          totalCompanies: parseInt(totalCompanies?.count || '0'),
          activeCompanies: parseInt(activeCompanies?.count || '0'),
          inactiveCompanies: parseInt(inactiveCompanies?.count || '0'),
          totalJobPosts: parseInt(totalJobPosts?.count || '0'),
          activeJobPosts: parseInt(activeJobPosts?.count || '0'),
          expiredJobPosts: parseInt(expiredJobPosts?.count || '0'),
          totalApplications: parseInt(totalApplications?.count || '0'),
          thisMonthApplications: parseInt(thisMonthApplications?.count || '0'),
          totalViews: parseInt(totalViews?.count || '0'),
          thisMonthViews: parseInt(thisMonthViews?.count || '0'),
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch franchise overview',
        error: error.message,
      };
    }
  }

  async getFranchiseCompanies(
    franchiseId: number,
    options: { page: number; limit: number } = { page: 1, limit: 10 },
  ) {
    try {
      const { page, limit } = options;
      const offset = (page - 1) * limit;

      const companies = await this.dataSource
        .createQueryBuilder()
        .select([
          'c.id',
          'c.name',
          'c.location',
          'c.status',
          'c.created_at',
          'c.country_id',
          'c.state_id',
          'c.city_id',
          'c.area_id',
          'u.email',
          'u.phone',
        ])
        .addSelect('city.city_name', 'city_name')
        .addSelect('state.state_name', 'state_name')
        .addSelect('country.country_name', 'country_name')
        .addSelect('area.area', 'area_name')
        .addSelect(
          '(SELECT COUNT(*) FROM job_posts jp WHERE jp.company_id = c.id)',
          'jobPosts',
        )
        .addSelect(
          '(SELECT COUNT(*) FROM job_applications ja INNER JOIN job_posts jp ON jp.id = ja.job_post_id WHERE jp.company_id = c.id)',
          'applications',
        )
        .addSelect(
          '(SELECT COUNT(*) FROM job_views jv INNER JOIN job_posts jp ON jp.id = jv.job_post_id WHERE jp.company_id = c.id)',
          'views',
        )
        .from('companies', 'c')
        .leftJoin('users', 'u', 'u.id = c.user_id')
        .leftJoin('city', 'city', 'city.city_id = c.city_id')
        .leftJoin('state', 'state', 'state.state_id = c.state_id')
        .leftJoin('country', 'country', 'country.country_id = c.country_id')
        .leftJoin('area', 'area', 'area.id = c.area_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .orderBy('c.status', 'DESC')
        .addOrderBy('c.created_at', 'DESC')
        .skip(offset)
        .take(limit)
        .getRawMany();

      // Get total count for pagination
      const total = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('companies', 'c')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .getRawOne();

      // Get active and inactive counts (constant across pagination)
      const activeCompanies = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('companies', 'c')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('c.status = 1')
        .getRawOne();

      const inactiveCompanies = await this.dataSource
        .createQueryBuilder()
        .select('COUNT(*)', 'count')
        .from('companies', 'c')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .andWhere('c.is_deleted = 0')
        .andWhere('c.status = 0')
        .getRawOne();

      return {
        success: true,
        data: {
          companies: companies.map((company) => ({
            id: company.c_id,
            name: company.c_name,
            email: company.u_email,
            contact: company.u_phone,
            status: company.c_status === 1 ? 'active' : 'inactive',
            c_status: company.c_status, // This is the raw database status value
            jobPosts: parseInt(company.jobPosts || '0'),
            applications: parseInt(company.applications || '0'),
            views: parseInt(company.views || '0'),
            created_at: company.c_created_at,
            location: company.c_location,
            area: company.area_name,
            city: company.city_name,
            state: company.state_name,
            country: company.country_name,
          })),
          pagination: {
            page,
            limit,
            total: parseInt(total.count),
            totalPages: Math.ceil(parseInt(total.count) / limit),
          },
          // Constant counts across all pages
          activeCompanies: parseInt(activeCompanies?.count || '0'),
          inactiveCompanies: parseInt(inactiveCompanies?.count || '0'),
        },
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch franchise companies',
        error: error.message,
      };
    }
  }

  async getFranchiseCompaniesForOverview(franchiseId: number) {
    try {
      const companies = await this.dataSource
        .createQueryBuilder()
        .select([
          'c.id',
          'c.name',
          'c.location',
          'c.status',
          'c.created_at',
          'c.country_id',
          'c.state_id',
          'c.city_id',
          'c.area_id',
          'u.email',
          'u.phone',
        ])
        .addSelect('city.city_name', 'city_name')
        .addSelect('state.state_name', 'state_name')
        .addSelect('country.country_name', 'country_name')
        .addSelect('area.area', 'area_name')
        .addSelect(
          '(SELECT COUNT(*) FROM job_posts jp WHERE jp.company_id = c.id)',
          'jobPosts',
        )
        .addSelect(
          '(SELECT COUNT(*) FROM job_applications ja INNER JOIN job_posts jp ON jp.id = ja.job_post_id WHERE jp.company_id = c.id)',
          'applications',
        )
        .addSelect(
          '(SELECT COUNT(*) FROM job_views jv INNER JOIN job_posts jp ON jp.id = jv.job_post_id WHERE jp.company_id = c.id)',
          'views',
        )
        .from('companies', 'c')
        .leftJoin('users', 'u', 'u.id = c.user_id')
        .leftJoin('city', 'city', 'city.city_id = c.city_id')
        .leftJoin('state', 'state', 'state.state_id = c.state_id')
        .leftJoin('country', 'country', 'country.country_id = c.country_id')
        .leftJoin('area', 'area', 'area.id = c.area_id')
        .where('c.franchise_id = :franchiseId', { franchiseId })
        .orderBy('c.created_at', 'DESC')
        .limit(5) // Show only 5 companies in overview
        .getRawMany();

      return {
        success: true,
        data: companies.map((company) => ({
          id: company.c_id,
          name: company.c_name,
          email: company.u_email,
          contact: company.u_phone,
          status: company.c_status === 1 ? 'active' : 'inactive',
          c_status: company.c_status, // This is the raw database status value
          jobPosts: parseInt(company.jobPosts || '0'),
          applications: parseInt(company.applications || '0'),
          views: parseInt(company.views || '0'),
          created_at: company.c_created_at,
          location: company.c_location,
          area: company.area_name,
          city: company.city_name,
          state: company.state_name,
          country: company.country_name,
        })),
      };
    } catch (error) {
      return {
        success: false,
        message: 'Failed to fetch franchise companies for overview',
        error: error.message,
      };
    }
  }

  async addCompanyForFranchise(franchiseId: number, companyData: any) {
    try {
      // Check if company name already exists
      const existingCompany = await this.dataSource
        .createQueryBuilder()
        .select(['id', 'name'])
        .from('companies', 'c')
        .where('c.name = :name', { name: companyData.name })
        .andWhere('c.is_deleted = :isDeleted', { isDeleted: 0 })
        .getOne();

      if (existingCompany) {
        throw new Error(
          'Company name already exists. Please choose a different name.',
        );
      }

      // Check if user already exists with the same email or username
      const existingUser = await this.userRepository
        .createQueryBuilder('u')
        .select(['u.id', 'u.email', 'u.username'])
        .where('u.email = :email', { email: companyData.email })
        .orWhere('u.username = :username', { username: companyData.username })
        .getOne();

      if (existingUser) {
        if (existingUser.email === companyData.email) {
          throw new Error('User with this email already exists');
        }
        if (existingUser.username === companyData.username) {
          throw new Error('Username already taken');
        }
      }

      // First, create the user account with recruiter role
      const userData = {
        username: companyData.username,
        email: companyData.email,
        password: companyData.password, // In production, this should be hashed
        role: 'recruiter' as const,
        fullName: companyData.full_name,
        phone: companyData.phone,
        status: 'active' as const,
      };

      const user = await this.userRepository.save(userData);
      //console.log('User created successfully:', user);

      // Then, create the company linked to both user_id and franchise_id
      const company = {
        name: companyData.name,
        about: companyData.about || null,
        vision: companyData.vision || null,
        website: companyData.website || null,
        location: companyData.location,
        founded_at: companyData.founded_at || null,
        company_size_id: companyData.company_size_id || null,
        organization_type_id: companyData.organization_type_id || null,
        industry_id: companyData.industry_id || null,
        user_id: user.id, // Link to the recruiter user
        country_id: companyData.country_id || null,
        state_id: companyData.state_id || null,
        city_id: companyData.city_id || null,
        pincode: companyData.pincode || null,
        latitude: companyData.latitude || null,
        longitude: companyData.longitude || null,
        featured: 0,
        status: 1, // active
        franchise_id: franchiseId, // Link to the franchise
      };

      const savedCompany = await this.companyRepository.save(company);
      //console.log('Company created successfully:', savedCompany);

      return {
        success: true,
        message: 'Company added successfully',
        data: {
          user: {
            id: user.id,
            username: user.username,
            email: user.email,
            fullName: user.fullName,
            role: user.role,
            password: companyData.password, // Return the password for display
          },
          company: {
            id: savedCompany.id,
            name: savedCompany.name,
            location: savedCompany.location,
            user_id: savedCompany.user_id,
            franchise_id: savedCompany.franchise_id,
          },
        },
      };
    } catch (error) {
      console.error('Error adding company for franchise:', error);
      console.error('Error details:', {
        message: error.message,
        stack: error.stack,
        code: error.code,
        errno: error.errno,
        sqlMessage: error.sqlMessage,
        sqlState: error.sqlState,
      });
      return {
        success: false,
        message: `Failed to add company: ${error.message}`,
      };
    }
  }

  async getCompanyCredentials(companyId: number) {
    try {
      // Get company credentials with user information
      const credentialsQuery = this.companyRepository.manager
        .createQueryBuilder()
        .select([
          'c.id',
          'c.name as company_name',
          'u.id as user_id',
          'u.username',
          'u.email',
          'u.fullName',
          'u.phone',
          'u.password',
        ])
        .from('companies', 'c')
        .leftJoin('users', 'u', 'u.id = c.user_id')
        .where('c.id = :companyId', { companyId });

      const credentials = await credentialsQuery.getRawOne();

      if (!credentials) {
        return {
          success: false,
          message: 'Company not found',
        };
      }

      const responseData = {
        companyId: credentials.c_id,
        companyName: credentials.company_name,
        userId: credentials.user_id,
        username: credentials.u_username,
        email: credentials.u_email,
        fullName: credentials.u_full_name,
        phone: credentials.u_phone,
        password: credentials.u_password,
      };

      return {
        success: true,
        data: responseData,
      };
    } catch (error) {
      console.error('Error fetching company credentials:', error);
      return {
        success: false,
        message: 'Failed to fetch company credentials',
        error: error.message,
      };
    }
  }

  // Franchise Settings Methods
  async getFranchiseSettings(userId: number) {
    try {
      // Get franchise information
      const franchise = await this.franchiseRepository.findOne({
        where: { userId: userId },
      });

      if (!franchise) {
        return {
          success: false,
          message: 'Franchise not found',
        };
      }

      // Get user information
      const user = await this.userRepository.findOne({
        where: { id: userId },
      });

      if (!user) {
        return {
          success: false,
          message: 'User not found',
        };
      }

      // Calculate profile completion
      const profileCompletion = this.calculateFranchiseProfileCompletion(
        franchise,
        user,
      );

      return {
        success: true,
        data: {
          franchise: {
            id: franchise.id,
            franchiseName: franchise.franchiseName,
            companyName: franchise.companyName,
            email: franchise.email,
            contactNo: franchise.contactNo,
            address: franchise.address,
            countryId: franchise.countryId,
            stateId: franchise.stateId,
            cityId: franchise.cityId,
            postal_code: franchise.postal_code,
            areaId: franchise.areaId,
            gstNo: franchise.gstNo,
            serviceArea: franchise.serviceArea,
            status: franchise.status,
            createdAt: franchise.createdAt,
            updatedAt: franchise.updatedAt,
          },
          user: {
            id: user.id,
            username: user.username,
            email: user.email,
            fullName: user.fullName,
            phone: user.phone,
            profilePicture: user.profilePicture,
            status: user.status,
          },
          profileCompletion,
        },
      };
    } catch (error) {
      console.error('Error fetching franchise settings:', error);
      return {
        success: false,
        message: 'Failed to fetch franchise settings',
        error: error.message,
      };
    }
  }

  async updateFranchiseInfo(userId: number, franchiseData: any) {
    try {
      // Use transaction to update both user and franchise tables (following franchise creation pattern)
      return await this.dataSource.transaction(async (manager) => {
        // Find existing franchise
        const franchise = await manager
          .createQueryBuilder(Franchises, 'f')
          .where('f.userId = :userId', { userId })
          .getOne();

        if (!franchise) {
          return {
            success: false,
            message: 'Franchise not found',
          };
        }

        // Update user table (following franchise creation pattern)
        await manager
          .createQueryBuilder()
          .update(UserEntity)
          .set({
            email: franchiseData.email || undefined,
            fullName: franchiseData.franchiseName || undefined,
            phone: franchiseData.contactNo || undefined,
          })
          .where('id = :userId', { userId })
          .execute();

        // Update franchise table (following franchise creation pattern)
        await manager
          .createQueryBuilder()
          .update(Franchises)
          .set({
            franchiseName: franchiseData.franchiseName || undefined,
            companyName: franchiseData.companyName || undefined,
            email: franchiseData.email || undefined,
            contactNo: franchiseData.contactNo || undefined,
            address: franchiseData.address || undefined,
            countryId: franchiseData.country_id || undefined,
            stateId: franchiseData.state_id || undefined,
            cityId: franchiseData.city_id || undefined,
            postal_code: franchiseData.postal_code || undefined,
            areaId: franchiseData.area_id || undefined,
            gstNo: franchiseData.gstNo || undefined,
            serviceArea: franchiseData.serviceArea || undefined,
          })
          .where('userId = :userId', { userId })
          .execute();

        // Get updated data
        const updatedFranchise = await manager
          .createQueryBuilder(Franchises, 'f')
          .where('f.userId = :userId', { userId })
          .getOne();

        const updatedUser = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        return {
          success: true,
          message: 'Franchise information updated successfully',
          data: {
            franchise: {
              id: updatedFranchise.id,
              franchiseName: updatedFranchise.franchiseName,
              companyName: updatedFranchise.companyName,
              email: updatedFranchise.email,
              contactNo: updatedFranchise.contactNo,
              address: updatedFranchise.address,
              countryId: updatedFranchise.countryId,
              stateId: updatedFranchise.stateId,
              cityId: updatedFranchise.cityId,
              postal_code: updatedFranchise.postal_code,
              areaId: updatedFranchise.areaId,
              gstNo: updatedFranchise.gstNo,
              serviceArea: updatedFranchise.serviceArea,
              status: updatedFranchise.status,
              updatedAt: updatedFranchise.updatedAt,
            },
            user: {
              id: updatedUser.id,
              username: updatedUser.username,
              email: updatedUser.email,
              fullName: updatedUser.fullName,
              phone: updatedUser.phone,
              profilePicture: updatedUser.profilePicture,
              status: updatedUser.status,
            },
          },
        };
      });
    } catch (error) {
      console.error('Error updating franchise info:', error);
      return {
        success: false,
        message: 'Failed to update franchise information',
        error: error.message,
      };
    }
  }

  async updateFranchiseUserInfo(userId: number, userData: any) {
    try {
      // Use transaction to update user table (following franchise creation pattern)
      return await this.dataSource.transaction(async (manager) => {
        // Find existing user
        const user = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        if (!user) {
          return {
            success: false,
            message: 'User not found',
          };
        }

        // Update user table (following franchise creation pattern)
        await manager
          .createQueryBuilder()
          .update(UserEntity)
          .set({
            fullName: userData.fullName || undefined,
            phone: userData.phone || undefined,
            email: userData.email || undefined,
          })
          .where('id = :userId', { userId })
          .execute();

        // Get updated user data
        const updatedUser = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        return {
          success: true,
          message: 'User information updated successfully',
          data: {
            user: {
              id: updatedUser.id,
              username: updatedUser.username,
              email: updatedUser.email,
              fullName: updatedUser.fullName,
              phone: updatedUser.phone,
              profilePicture: updatedUser.profilePicture,
              status: updatedUser.status,
            },
          },
        };
      });
    } catch (error) {
      console.error('Error updating user info:', error);
      return {
        success: false,
        message: 'Failed to update user information',
        error: error.message,
      };
    }
  }

  async updateFranchisePassword(userId: number, passwordData: any) {
    try {
      // Use transaction to update user password (following franchise creation pattern)
      return await this.dataSource.transaction(async (manager) => {
        // Find existing user
        const user = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        if (!user) {
          return {
            success: false,
            message: 'User not found',
          };
        }

        // Check if OTP verification is required
        if (passwordData.requireOTP && !passwordData.otp) {
          return { success: false, message: 'OTP verification required' };
        }

        // If OTP is provided, verify it
        if (passwordData.otp) {
          const otpVerification = await this.verifyOTPForPasswordChange(
            userId,
            passwordData.otp,
          );
          if (!otpVerification.success) {
            return { success: false, message: otpVerification.message };
          }
        }

        // Validate password match
        if (passwordData.newPassword !== passwordData.confirmPassword) {
          return {
            success: false,
            message: 'Passwords do not match',
          };
        }

        // Update password directly (no hashing) - following franchise creation pattern
        await manager
          .createQueryBuilder()
          .update(UserEntity)
          .set({
            password: passwordData.newPassword,
          })
          .where('id = :userId', { userId })
          .execute();

        // Clear OTP after successful password change
        if (passwordData.otp) {
          await this.clearOTPForUser(userId);
        }

        return {
          success: true,
          message: 'Password updated successfully',
        };
      });
    } catch (error) {
      console.error('Error updating password:', error);
      return {
        success: false,
        message: 'Failed to update password',
        error: error.message,
      };
    }
  }

  async deleteFranchiseAccount(userId: number) {
    try {
      // Use transaction to soft delete both user and franchise (following franchise creation pattern)
      return await this.dataSource.transaction(async (manager) => {
        // Find existing user
        const user = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        if (!user) {
          return {
            success: false,
            message: 'User not found',
          };
        }

        // Find existing franchise
        const franchise = await manager
          .createQueryBuilder(Franchises, 'f')
          .where('f.userId = :userId', { userId })
          .getOne();

        // Soft delete user (set status to inactive) - following franchise creation pattern
        await manager
          .createQueryBuilder()
          .update(UserEntity)
          .set({
            status: 'inactive',
            deletedAt: new Date(),
          })
          .where('id = :userId', { userId })
          .execute();

        // Soft delete franchise if exists
        if (franchise) {
          await manager
            .createQueryBuilder()
            .update(Franchises)
            .set({
              status: 'inactive',
            })
            .where('userId = :userId', { userId })
            .execute();
        }

        return {
          success: true,
          message: 'Account deleted successfully',
        };
      });
    } catch (error) {
      console.error('Error deleting franchise account:', error);
      return {
        success: false,
        message: 'Failed to delete account',
        error: error.message,
      };
    }
  }

  async uploadFranchiseLogo(userId: number, file: Express.Multer.File) {
    try {
      // Use transaction to update user profile picture (following franchise creation pattern)
      return await this.dataSource.transaction(async (manager) => {
        // Find existing user
        const user = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        if (!user) {
          return {
            success: false,
            message: 'User not found',
          };
        }

        // Generate file path
        const filePath = `/uploads/franchise-logos/${file.filename}`;

        // Update user profile_picture field - following franchise creation pattern
        await manager
          .createQueryBuilder()
          .update(UserEntity)
          .set({
            profilePicture: filePath,
          })
          .where('id = :userId', { userId })
          .execute();

        return {
          success: true,
          message: 'Logo uploaded successfully',
          data: {
            logoPath: filePath,
          },
        };
      });
    } catch (error) {
      console.error('Error uploading franchise logo:', error);
      return {
        success: false,
        message: 'Failed to upload logo',
        error: error.message,
      };
    }
  }

  async deleteFranchiseLogo(userId: number) {
    try {
      // Use transaction to remove user profile picture (following franchise creation pattern)
      return await this.dataSource.transaction(async (manager) => {
        // Find existing user
        const user = await manager
          .createQueryBuilder(UserEntity, 'u')
          .where('u.id = :userId', { userId })
          .getOne();

        if (!user) {
          return {
            success: false,
            message: 'User not found',
          };
        }

        // Update user profile_picture field to null - following franchise creation pattern
        await manager
          .createQueryBuilder()
          .update(UserEntity)
          .set({
            profilePicture: null,
          })
          .where('id = :userId', { userId })
          .execute();

        return {
          success: true,
          message: 'Logo deleted successfully',
        };
      });
    } catch (error) {
      console.error('Error deleting franchise logo:', error);
      return {
        success: false,
        message: 'Failed to delete logo',
        error: error.message,
      };
    }
  }

  private calculateFranchiseProfileCompletion(franchise: any, user: any) {
    const fields = [
      { name: 'Franchise Name', value: franchise.franchiseName },
      { name: 'Email', value: franchise.email },
      { name: 'Contact Number', value: franchise.contactNo },
      { name: 'Address', value: franchise.address },
      { name: 'GST Number', value: franchise.gstNo },
      { name: 'Full Name', value: user.fullName },
      { name: 'Phone', value: user.phone },
      { name: 'Profile Picture', value: user.profilePicture },
    ];

    const completedFields = fields.filter(
      (field) => field.value && field.value.trim() !== '',
    ).length;
    const totalFields = fields.length;
    const completion = Math.round((completedFields / totalFields) * 100);

    const missingFields = fields
      .filter((field) => !field.value || field.value.trim() === '')
      .map((field) => field.name);

    return {
      completion,
      missingFields,
      totalFields,
      completedFields,
    };
  }
  // async getAdminOverview() {
  //   try {
  //     // Total companies
  //     const totalCompanies = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('companies', 'c')
  //       .where('c.is_deleted = 0')
  //       .getRawOne();

  //     const activeCompanies = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('companies', 'c')
  //       .where('c.is_deleted = 0')
  //       .andWhere('c.status = 1')
  //       .getRawOne();

  //     const inactiveCompanies = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('companies', 'c')
  //       .where('c.is_deleted = 0')
  //       .andWhere('c.status = 0')
  //       .getRawOne();

  //     // Total job posts
  //     const totalJobPosts = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_posts', 'jp')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .getRawOne();

  //     const activeJobPosts = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_posts', 'jp')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .andWhere('jp.status = :status', { status: 'open' })
  //       .andWhere('jp.expires_at > NOW()')
  //       .getRawOne();

  //     const expiredJobPosts = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_posts', 'jp')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .andWhere('jp.expires_at <= NOW()')
  //       .getRawOne();

  //     // Applications
  //     const totalApplications = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_applications', 'ja')
  //       .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .getRawOne();

  //     const thisMonthApplications = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_applications', 'ja')
  //       .innerJoin('job_posts', 'jp', 'jp.id = ja.job_post_id')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .andWhere('MONTH(ja.applied_at) = MONTH(NOW())')
  //       .andWhere('YEAR(ja.applied_at) = YEAR(NOW())')
  //       .getRawOne();

  //     // Views
  //     const totalViews = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_views', 'jv')
  //       .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .getRawOne();

  //     const thisMonthViews = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('job_views', 'jv')
  //       .innerJoin('job_posts', 'jp', 'jp.id = jv.job_post_id')
  //       .innerJoin('companies', 'c', 'c.id = jp.company_id')
  //       .where('c.is_deleted = 0')
  //       .andWhere('MONTH(jv.viewed_at) = MONTH(NOW())')
  //       .andWhere('YEAR(jv.viewed_at) = YEAR(NOW())')
  //       .getRawOne();

  //     // Total candidates
  //     const totalCandidates = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('users', 'u')
  //       .where('u.role = :role', { role: 'seeker' })
  //       .getRawOne();

  //     // Total verified users
  //     const totalVerifiedUsers = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('users', 'u')
  //       .where('u.role != :role', { role: 'admin' })
  //       .getRawOne();

  //     // Total franchises
  //     const totalFranchises = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('franchises', 'f')
  //       // .where('f.is_deleted = 0')
  //       .getRawOne();

  //     // Active franchises
  //     const activeFranchises = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('franchises', 'f')
  //       .where('f.status = :status', { status: 'active' })
  //       .getRawOne();

  //     // Inactive franchises
  //     const inactiveFranchises = await this.dataSource
  //       .createQueryBuilder()
  //       .select('COUNT(*)', 'count')
  //       .from('franchises', 'f')
  //       .where('f.status = :status', { status: 'inactive' })
  //       .getRawOne();

  //     return {
  //       success: true,
  //       data: {
  //         totalCompanies: parseInt(totalCompanies?.count || '0'),
  //         activeCompanies: parseInt(activeCompanies?.count || '0'),
  //         inactiveCompanies: parseInt(inactiveCompanies?.count || '0'),
  //         totalJobPosts: parseInt(totalJobPosts?.count || '0'),
  //         activeJobPosts: parseInt(activeJobPosts?.count || '0'),
  //         expiredJobPosts: parseInt(expiredJobPosts?.count || '0'),
  //         totalApplications: parseInt(totalApplications?.count || '0'),
  //         thisMonthApplications: parseInt(thisMonthApplications?.count || '0'),
  //         totalViews: parseInt(totalViews?.count || '0'),
  //         thisMonthViews: parseInt(thisMonthViews?.count || '0'),
  //         totalFranchises: parseInt(totalFranchises?.count || '0'),
  //         activeFranchises: parseInt(activeFranchises?.count || '0'),
  //         inactiveFranchises: parseInt(inactiveFranchises?.count || '0'),
  //         totalCandidates: parseInt(totalCandidates?.count || '0'),
  //         totalVerifiedUsers: parseInt(totalVerifiedUsers?.count || '0'),
  //       },
  //     };
  //   } catch (error) {
  //     return {
  //       success: false,
  //       message: 'Failed to fetch admin overview',
  //       error: error.message,
  //     };
  //   }
  // }

  async getAdminOverview(from?: string, to?: string) {
    try {
      const parseDate = (value?: string) =>
        value && /^\d{4}-\d{2}-\d{2}$/.test(value) ? value : null;
      const startDate = parseDate(from);
      const endDate = parseDate(to);
      const asCount = (rows: any[]) => Number(rows?.[0]?.count || 0);
      const asPercent = (current: number, previous: number) => {
        if (!previous) return current > 0 ? 100 : 0;
        return Math.round(((current - previous) / previous) * 100);
      };
      const formatDate = (date: Date) => date.toISOString().slice(0, 10);
      const addDays = (date: Date, days: number) => {
        const next = new Date(date);
        next.setDate(next.getDate() + days);
        return next;
      };
      const dateWhere = (column: string) => {
        const clauses: string[] = [];
        const params: string[] = [];

        if (startDate) {
          clauses.push(`${column} >= ?`);
          params.push(`${startDate} 00:00:00`);
        }

        if (endDate) {
          clauses.push(`${column} <= ?`);
          params.push(`${endDate} 23:59:59`);
        }

        return { clauses, params };
      };
      const runCount = async (
        fromSql: string,
        baseWhere: string[],
        dateColumn?: string,
      ) => {
        const where = [...baseWhere];
        const params: string[] = [];

        if (dateColumn) {
          const range = dateWhere(dateColumn);
          where.push(...range.clauses);
          params.push(...range.params);
        }

        return asCount(
          await this.dataSource.query(
            `SELECT COUNT(*) AS count ${fromSql} ${where.length ? `WHERE ${where.join(' AND ')}` : ''
            }`,
            params,
          ),
        );
      };
      const runFixedRangeCount = async (
        fromSql: string,
        baseWhere: string[],
        dateColumn: string,
        rangeStart: string,
        rangeEnd: string,
      ) =>
        asCount(
          await this.dataSource.query(
            `SELECT COUNT(*) AS count ${fromSql} WHERE ${[
              ...baseWhere,
              `${dateColumn} >= ?`,
              `${dateColumn} <= ?`,
            ].join(' AND ')}`,
            [`${rangeStart} 00:00:00`, `${rangeEnd} 23:59:59`],
          ),
        );
      const today = new Date();
      const buildMonthlySeries = async (
        fromSql: string,
        baseWhere: string[],
        dateColumn: string,
        valueKey: string,
      ) => {
        const rangeStart = startDate
          ? new Date(`${startDate}T00:00:00`)
          : addDays(today, -365);
        const rangeEnd = endDate ? new Date(`${endDate}T00:00:00`) : today;
        const cursor = new Date(
          rangeStart.getFullYear(),
          rangeStart.getMonth(),
          1,
        );
        const monthEnd = new Date(
          rangeEnd.getFullYear(),
          rangeEnd.getMonth(),
          1,
        );
        const monthMap = new Map<string, number>();

        while (cursor <= monthEnd) {
          monthMap.set(
            `${cursor.getFullYear()}-${String(cursor.getMonth() + 1).padStart(
              2,
              '0',
            )}`,
            0,
          );
          cursor.setMonth(cursor.getMonth() + 1);
        }

        const selectedRange = dateWhere(dateColumn);
        const fallbackRange = !startDate
          ? {
            clauses: [`${dateColumn} >= ?`, `${dateColumn} <= ?`],
            params: [
              `${formatDate(rangeStart)} 00:00:00`,
              `${formatDate(rangeEnd)} 23:59:59`,
            ],
          }
          : selectedRange;
        const where = [...baseWhere, ...fallbackRange.clauses];
        const rows = await this.dataSource.query(
          `
            SELECT DATE_FORMAT(${dateColumn}, '%Y-%m') AS month_key, COUNT(*) AS count
            ${fromSql}
            WHERE ${where.join(' AND ')}
            GROUP BY month_key
            ORDER BY month_key ASC
          `,
          fallbackRange.params,
        );

        rows.forEach((row: any) => {
          if (row.month_key)
            monthMap.set(row.month_key, Number(row.count || 0));
        });

        return Array.from(monthMap.entries()).map(([monthKey, count]) => {
          const [year, month] = monthKey.split('-').map(Number);
          return {
            month: new Date(year, month - 1, 1).toLocaleString('en-US', {
              month: 'short',
              year: 'numeric',
            }),
            [valueKey]: count,
          };
        });
      };

      const companyFrom = 'FROM companies c';
      const companyWhere = ['COALESCE(c.is_deleted, 0) = 0'];
      const jobFrom =
        'FROM job_posts jp INNER JOIN companies c ON c.id = jp.company_id';
      const jobWhere = ['COALESCE(c.is_deleted, 0) = 0'];
      const applicationFrom = `
        FROM job_applications ja
        INNER JOIN job_posts jp ON jp.id = ja.job_post_id
        INNER JOIN companies c ON c.id = jp.company_id
      `;
      const applicationWhere = ['COALESCE(c.is_deleted, 0) = 0'];
      const userFrom = 'FROM users u';
      const seekerWhere = ["u.role = 'seeker'", 'u.deleted_at IS NULL'];
      const verifiedSeekerWhere = [
        ...seekerWhere,
        'COALESCE(u.phone_verified, 0) = 1',
      ];
      const completedUserRegistrationWhere = [
        "u.role = 'seeker'",
        'u.deleted_at IS NULL',
        "u.onboarding_status = 'completed'",
      ];
      const approvedRecruiterCompanyWhere = [
        ...companyWhere,
        'c.is_approved = 1',
        'c.is_admin_company = 0',
      ];
      const associateFrom = 'FROM associates a';
      const activeAssociateWhere = ['a.status = 1'];
      const franchiseFrom = 'FROM franchises f';

      const [
        totalCandidates,
        totalVerifiedUsers,
        totalFranchises,
        totalCompanies,
        activeCompanies,
        inactiveCompanies,
        activeFranchises,
        inactiveFranchises,
        totalJobPosts,
        activeJobPosts,
        expiredJobPosts,
        totalApplications,
        totalViews,
      ] = await Promise.all([
        runCount(userFrom, seekerWhere, 'u.created_at'),
        runCount(userFrom, verifiedSeekerWhere, 'u.created_at'),
        runCount(franchiseFrom, [], 'f.created_at'),
        runCount(companyFrom, companyWhere, 'c.created_at'),
        runCount(
          companyFrom,
          [...companyWhere, 'c.status = 1'],
          'c.created_at',
        ),
        runCount(
          companyFrom,
          [...companyWhere, 'c.status = 0'],
          'c.created_at',
        ),
        runCount(franchiseFrom, ["f.status = 'active'"], 'f.created_at'),
        runCount(franchiseFrom, ["f.status = 'inactive'"], 'f.created_at'),
        runCount(jobFrom, jobWhere, 'jp.created_at'),
        runCount(
          jobFrom,
          [
            ...jobWhere,
            "jp.status = 'open'",
            '(jp.expires_at IS NULL OR jp.expires_at > NOW())',
          ],
          'jp.created_at',
        ),
        runCount(
          jobFrom,
          [
            ...jobWhere,
            "(jp.status = 'closed' OR (jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
          ],
          'jp.created_at',
        ),
        runCount(applicationFrom, applicationWhere, 'ja.applied_at'),
        runCount(
          `
            FROM job_views jv
            INNER JOIN job_posts jp ON jp.id = jv.job_post_id
            INNER JOIN companies c ON c.id = jp.company_id
          `,
          jobWhere,
          'jv.viewed_at',
        ),
      ]);

      const current7Start = formatDate(addDays(today, -6));
      const current7End = formatDate(today);
      const previous7Start = formatDate(addDays(today, -13));
      const previous7End = formatDate(addDays(today, -7));
      const [
        currentCandidates,
        previousCandidates,
        currentVerifiedUsers,
        previousVerifiedUsers,
        currentCompanies,
        previousCompanies,
        currentJobs,
        previousJobs,
        currentActiveJobs,
        previousActiveJobs,
        currentExpiredJobs,
        previousExpiredJobs,
      ] = await Promise.all([
        runFixedRangeCount(
          userFrom,
          seekerWhere,
          'u.created_at',
          current7Start,
          current7End,
        ),
        runFixedRangeCount(
          userFrom,
          seekerWhere,
          'u.created_at',
          previous7Start,
          previous7End,
        ),
        runFixedRangeCount(
          userFrom,
          verifiedSeekerWhere,
          'u.created_at',
          current7Start,
          current7End,
        ),
        runFixedRangeCount(
          userFrom,
          verifiedSeekerWhere,
          'u.created_at',
          previous7Start,
          previous7End,
        ),
        runFixedRangeCount(
          companyFrom,
          companyWhere,
          'c.created_at',
          current7Start,
          current7End,
        ),
        runFixedRangeCount(
          companyFrom,
          companyWhere,
          'c.created_at',
          previous7Start,
          previous7End,
        ),
        runFixedRangeCount(
          jobFrom,
          jobWhere,
          'jp.created_at',
          current7Start,
          current7End,
        ),
        runFixedRangeCount(
          jobFrom,
          jobWhere,
          'jp.created_at',
          previous7Start,
          previous7End,
        ),
        runFixedRangeCount(
          jobFrom,
          [
            ...jobWhere,
            "jp.status = 'open'",
            '(jp.expires_at IS NULL OR jp.expires_at > NOW())',
          ],
          'jp.created_at',
          current7Start,
          current7End,
        ),
        runFixedRangeCount(
          jobFrom,
          [
            ...jobWhere,
            "jp.status = 'open'",
            '(jp.expires_at IS NULL OR jp.expires_at > NOW())',
          ],
          'jp.created_at',
          previous7Start,
          previous7End,
        ),
        runFixedRangeCount(
          jobFrom,
          [
            ...jobWhere,
            "(jp.status = 'closed' OR (jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
          ],
          'jp.created_at',
          current7Start,
          current7End,
        ),
        runFixedRangeCount(
          jobFrom,
          [
            ...jobWhere,
            "(jp.status = 'closed' OR (jp.expires_at IS NOT NULL AND jp.expires_at <= NOW()))",
          ],
          'jp.created_at',
          previous7Start,
          previous7End,
        ),
      ]);

      const [
        monthlyJobPosts,
        monthlyCandidates,
        monthlyApplications,
        monthlyCompanies,
      ] = await Promise.all([
        buildMonthlySeries(jobFrom, jobWhere, 'jp.created_at', 'jobs'),
        buildMonthlySeries(userFrom, seekerWhere, 'u.created_at', 'candidates'),
        buildMonthlySeries(
          applicationFrom,
          applicationWhere,
          'ja.applied_at',
          'applications',
        ),
        buildMonthlySeries(
          companyFrom,
          companyWhere,
          'c.created_at',
          'companies',
        ),
      ]);

      const categoryRange = dateWhere('jp.created_at');
      const topJobCategories = await this.dataSource.query(
        `
          SELECT COALESCE(i.name, 'Uncategorized') AS name, COUNT(*) AS value
          ${jobFrom}
          LEFT JOIN industries i ON i.id = jp.industry_id
          WHERE ${[...jobWhere, ...categoryRange.clauses].join(' AND ')}
          GROUP BY name
          ORDER BY value DESC
          LIMIT 5
        `,
        categoryRange.params,
      );
      const companyJobRange = dateWhere('jp.created_at');
      const topCompaniesByJobPosts = await this.dataSource.query(
        `
          SELECT c.name AS name, COUNT(*) AS jobs
          ${jobFrom}
          WHERE ${[...jobWhere, ...companyJobRange.clauses].join(' AND ')}
          GROUP BY c.id, c.name
          ORDER BY jobs DESC, c.name ASC
          LIMIT 10
        `,
        companyJobRange.params,
      );
      const completedUserRegistrationRange = dateWhere('u.created_at');
      const completedUserRegistrationsByRole = await this.dataSource.query(
        `
          SELECT u.role AS role, COUNT(*) AS value
          ${userFrom}
          WHERE ${[
          ...completedUserRegistrationWhere,
          ...completedUserRegistrationRange.clauses,
        ].join(' AND ')}
          GROUP BY u.role
        `,
        completedUserRegistrationRange.params,
      );
      const associateRegistrationCount = await runCount(
        associateFrom,
        activeAssociateWhere,
        'a.created_at',
      );
      const recruiterRegistrationCount = await runCount(
        companyFrom,
        approvedRecruiterCompanyWhere,
        'c.created_at',
      );
      const expiringJobsByCityAll = await this.dataSource.query(
        `
          SELECT
            COALESCE(ci.city_name, 'Unknown City') AS name,
            SUM(CASE WHEN DATEDIFF(DATE(jp.expires_at), CURDATE()) = 1 THEN 1 ELSE 0 END) AS tomorrow,
            SUM(CASE WHEN DATEDIFF(DATE(jp.expires_at), CURDATE()) = 2 THEN 1 ELSE 0 END) AS in2Days,
            SUM(CASE WHEN DATEDIFF(DATE(jp.expires_at), CURDATE()) = 3 THEN 1 ELSE 0 END) AS in3Days,
            COUNT(*) AS value
          ${jobFrom}
          LEFT JOIN city ci ON ci.city_id = jp.city_id
          WHERE ${[
          ...jobWhere,
          "jp.status = 'open'",
          'jp.expires_at IS NOT NULL',
          'jp.expires_at > NOW()',
          'DATE(jp.expires_at) >= DATE_ADD(CURDATE(), INTERVAL 1 DAY)',
          'DATE(jp.expires_at) <= DATE_ADD(CURDATE(), INTERVAL 3 DAY)',
        ].join(' AND ')}
          GROUP BY COALESCE(ci.city_id, 0), ci.city_name
          ORDER BY tomorrow DESC, in2Days DESC, in3Days DESC, value DESC, name ASC
        `,
      );
      const expiringJobsByCity = expiringJobsByCityAll.slice(0, 10);
      const todayDate = formatDate(today);
      const [
        jobsPostedToday,
        applicationsToday,
        newUsersToday,
        newCompaniesToday,
      ] = await Promise.all([
        runFixedRangeCount(
          jobFrom,
          jobWhere,
          'jp.created_at',
          todayDate,
          todayDate,
        ),
        runFixedRangeCount(
          applicationFrom,
          applicationWhere,
          'ja.applied_at',
          todayDate,
          todayDate,
        ),
        runFixedRangeCount(
          userFrom,
          seekerWhere,
          'u.created_at',
          todayDate,
          todayDate,
        ),
        runFixedRangeCount(
          companyFrom,
          companyWhere,
          'c.created_at',
          todayDate,
          todayDate,
        ),
      ]);

      return {
        success: true,
        data: {
          totalCandidates,
          totalVerifiedUsers,
          totalFranchises,
          totalCompanies,
          activeCompanies,
          inactiveCompanies,
          activeFranchises,
          inactiveFranchises,
          totalJobPosts,
          activeJobPosts,
          expiredJobPosts,
          totalApplications,
          totalViews,
          cardTrends: {
            totalCandidates: asPercent(currentCandidates, previousCandidates),
            totalVerifiedUsers: asPercent(
              currentVerifiedUsers,
              previousVerifiedUsers,
            ),
            totalCompanies: asPercent(currentCompanies, previousCompanies),
            totalJobPosts: asPercent(currentJobs, previousJobs),
            activeJobPosts: asPercent(currentActiveJobs, previousActiveJobs),
            expiredJobPosts: asPercent(currentExpiredJobs, previousExpiredJobs),
          },
          monthlyJobPosts,
          monthlyCandidates,
          monthlyApplications,
          monthlyCompanies,
          topJobCategories: topJobCategories.map((row: any) => ({
            name: row.name,
            value: Number(row.value || 0),
          })),
          topCompaniesByJobPosts: topCompaniesByJobPosts.map((row: any) => ({
            name: row.name,
            jobs: Number(row.jobs || 0),
          })),
          completedRegistrationsByRole: [
            {
              role: 'seeker',
              value: Number(
                completedUserRegistrationsByRole.find(
                  (row: any) => row.role === 'seeker',
                )?.value || 0,
              ),
            },
            {
              role: 'recruiter',
              value: recruiterRegistrationCount,
            },
            {
              role: 'associate',
              value: associateRegistrationCount,
            },
          ],
          expiringJobsByCity: expiringJobsByCity.map((row: any) => ({
            name: row.name,
            tomorrow: Number(row.tomorrow || 0),
            in2Days: Number(row.in2Days || 0),
            in3Days: Number(row.in3Days || 0),
            value: Number(row.value || 0),
          })),
          expiringJobsByCityAll: expiringJobsByCityAll.map((row: any) => ({
            name: row.name,
            tomorrow: Number(row.tomorrow || 0),
            in2Days: Number(row.in2Days || 0),
            in3Days: Number(row.in3Days || 0),
            value: Number(row.value || 0),
          })),
          todayActivity: {
            jobsPosted: jobsPostedToday,
            applications: applicationsToday,
            newUsers: newUsersToday,
            newCompanies: newCompaniesToday,
          },
        },
      };
    } catch (error: any) {
      return {
        success: false,
        message: 'Failed to fetch admin overview',
        error: error.message,
      };
    }
  }

  // Auto-complete methods
  private createSlug(title: string, id: number): string {
    const slug = title
      .toLowerCase()
      .replace(/[^a-z0-9\s-]/g, '')
      .replace(/\s+/g, '-')
      .replace(/-+/g, '-')
      .replace(/^-+|-+$/g, '');
    return `${slug}-${id}`;
  }

  // SEO-friendly slug without ID (for companies)
  private createSlugWithoutId(title: string): string {
    const slug = title
      .toLowerCase()
      .replace(/[^a-z0-9\s-]/g, '')
      .replace(/\s+/g, '-')
      .replace(/-+/g, '-')
      .replace(/^-+|-+$/g, '');
    return slug;
  }

  async getJobSuggestions(query: string) {
    try {
      const suggestions = await this.dataSource
        .createQueryBuilder()
        .select([
          'job.title AS title',
          'job.id AS id',
          'company.name AS company_name',
          'company.logo AS company_logo',
        ])
        .from('job_posts', 'job')
        .leftJoin('companies', 'company', 'company.id = job.company_id')
        .where('job.status = :status', { status: 'open' })
        .andWhere('(job.expires_at IS NULL OR job.expires_at > NOW())')
        .andWhere('LOWER(job.title) LIKE LOWER(:query)', {
          query: `%${query}%`,
        })
        .orderBy('job.title', 'ASC')
        .limit(10)
        .getRawMany();

      return {
        success: true,
        suggestions: suggestions.map((item) => ({
          id: item.id,
          title: item.title,
          company_name: item.company_name,
          company_logo: item.company_logo,
          type: 'job',
          slug: this.createSlug(item.title, item.id),
        })),
      };
    } catch (error) {
      console.error('Error in getJobSuggestions:', error);
      return {
        success: false,
        message: 'Failed to fetch job suggestions',
        error: error.message,
        suggestions: [],
      };
    }
  }

  async getCompanySuggestions(query: string) {
    try {
      const suggestions = await this.dataSource
        .createQueryBuilder()
        .select([
          'company.name AS name',
          'company.id AS id',
          'company.logo AS logo',
          'company.location AS location',
        ])
        .from('companies', 'company')
        .where('company.status = :status', { status: 1 })
        .andWhere('LOWER(company.name) LIKE LOWER(:query)', {
          query: `%${query}%`,
        })
        .orderBy('company.name', 'ASC')
        .limit(10)
        .getRawMany();

      return {
        success: true,
        suggestions: suggestions.map((item) => ({
          id: item.id,
          name: item.name,
          logo: item.logo || null,
          location: item.location || null,
          type: 'company',
          // SEO-friendly slug without ID (e.g., "tst-11" instead of "tst-11-123")
          // Frontend will use company name search to find the correct company
          slug: this.createSlugWithoutId(item.name),
          // Old approach with ID (not SEO-friendly):
          // slug: this.createSlug(item.name, item.id),
        })),
      };
    } catch (error) {
      console.error('Error in getCompanySuggestions:', error);
      return {
        success: false,
        message: 'Failed to fetch company suggestions',
        error: error.message,
        suggestions: [],
      };
    }
  }

  async getSearchSuggestions(query: string) {
    try {
      // Get both job and company suggestions
      const [jobSuggestions, companySuggestions] = await Promise.all([
        this.getJobSuggestions(query),
        this.getCompanySuggestions(query),
      ]);

      return {
        success: true,
        suggestions: {
          jobs: jobSuggestions.suggestions || [],
          companies: companySuggestions.suggestions || [],
        },
      };
    } catch (error) {
      console.error('Error in getSearchSuggestions:', error);
      return {
        success: false,
        message: 'Failed to fetch search suggestions',
        error: error.message,
        suggestions: { jobs: [], companies: [] },
      };
    }
  }

  // Helper method to safely parse JSON data
  private safeParseJson(jsonString: any): any[] | null {
    if (!jsonString) return null;

    // If it's already an array, return it
    if (Array.isArray(jsonString)) return jsonString;

    // If it's a string, try to parse it
    if (typeof jsonString === 'string') {
      try {
        return JSON.parse(jsonString);
      } catch (error) {
        // If JSON parsing fails, try to parse as comma-separated values
        if (jsonString.includes(',')) {
          return jsonString
            .split(',')
            .map((id) => parseInt(id.trim()))
            .filter((id) => !isNaN(id));
        }
        // If it's a single number, return as array
        const num = parseInt(jsonString);
        return isNaN(num) ? null : [num];
      }
    }

    return null;
  }
}
