import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { BasePesService, ResendVerificationDto, SMTPConnectionTestProp } from './abstracts/pes.abstract';
import { CreatePesCampaignDto, PesCampaignResponseDto, UpdatePesCampaignDto } from './dto/pes-campaign.dto';
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { DataSource, In, Repository } from 'typeorm';
import { PesCampaign } from './entities/pes-campaign.entity';
import { PesEmailVerification } from './entities/pes-email-verification.entity';
import { PesVerifiedUsers } from './entities/pes_verified_users.entity';
import { PesTemplate } from './entities/pes-template.entity';
import { CampaignStatus, VerificationMethod, VerificationStatus } from './enum/pes.enum';
import { ConfigService } from '@nestjs/config';
import * as nodemailer from 'nodemailer';
import { UserService } from 'modules/user/users.service';
import { UserRole } from 'common/enum/UserRole.enum';
import { CreatePesTemplateDto, PesTemplateResponseDto, UpdatePesTemplateDto } from './dto/pes-template.dto';
import { CreatePesSendingProfileDto, PesSendingProfileResponseDto, UpdatePesSendingProfileDto } from './dto/pes-sending-profile.dto';
import { PesSendingProfile } from './entities/pes-sending-profiles.entity';
import { CreatePesLandingPageDto, PesLandingPageResponseDto, UpdatePesLandingPageDto } from './dto/pes-landing-page.dto';
import { PesLandingPage } from './entities/pes-laning-pages.entity';
import { AppDataSource } from 'config/database/database.config';
import { PesVerifiedUserResponseDto } from './dto/pes-verified-users.dto';
import { CreatePesRecipientGroupDto, PesRecipientGroupResponseDto } from './dto/pes-recipient-group.dto';
import { PesRecipientGroup } from './entities/pes-recepient-group.entity';
import { PesRecipient } from './entities/pes-recipent.entity';
import { PesTrackingService } from './pes-tracking.service';

@Injectable()
export class PesService extends BasePesService {
  private readonly logger = new Logger(PesService.name);
  private transporter: nodemailer.Transporter;
  constructor(
    @InjectRepository(PesCampaign)
    private readonly campaignRepository: Repository<PesCampaign>,
    @InjectRepository(PesEmailVerification)
    private readonly emailVerificationRepository: Repository<PesEmailVerification>,
    @InjectRepository(PesVerifiedUsers)
    private readonly verifiedUsersRepository: Repository<PesVerifiedUsers>,
    @InjectRepository(PesTemplate)
    private readonly templateRepository: Repository<PesTemplate>,
    @InjectRepository(PesSendingProfile)
    private readonly sendingProfileRepository: Repository<PesSendingProfile>,
    @InjectRepository(PesLandingPage)
    private readonly landingPageRepository: Repository<PesLandingPage>,
    @InjectRepository(PesRecipientGroup)
    private readonly recipientGroupRepository: Repository<PesRecipientGroup>,
    @InjectRepository(PesRecipient)
    private readonly recipientRepository: Repository<PesRecipient>,
    private readonly configService: ConfigService,
    private readonly userService: UserService,
    @InjectDataSource(AppDataSource)
    private readonly dataSource: DataSource = AppDataSource,
    private readonly trackingService: PesTrackingService,
  ) {
    super();
    this.initializeEmailTransporter();
  }

  private initializeEmailTransporter(): void {
    try {
      const smtpConfig = {
        host: this.configService.get<string>('AUTH_SMTP_HOST'),
        port: this.configService.get<number>('AUTH_SMTP_PORT'),
        secure: true,
        auth: {
          user: this.configService.get<string>('AUTH_SMTP_USER'),
          pass: this.configService.get<string>('AUTH_SMTP_PASSWORD'),
        },
        tls: {
          rejectUnauthorized: false,
        },

        connectionTimeout: 60000,
        greetingTimeout: 30000,
        socketTimeout: 60000,

        pool: true,
        maxConnections: 5,
        maxMessages: 100,

        retryDelay: 1000,
        maxRetries: 3,
      };

      this.transporter = nodemailer.createTransport(smtpConfig);

      this.logger.debug('Email transporter initialized successfully');
    } catch (error) {
      this.logger.error('Failed to initialize email transporter:', error);

      this.logger.warn('Email service will be disabled due to configuration error');
    }
  }

  private async sendEmail(to: string, subject: string, text: string, html?: string): Promise<nodemailer.SentMessageInfo> {
    if (!this.transporter) {
      throw new BadRequestException('Email service is not configured properly');
    }

    const mailOptions: nodemailer.SendMailOptions = {
      from: this.configService.get<string>('AUTH_SMTP_USER'),
      to,
      subject,
      text,
      html,
    };

    try {
      return await this.transporter.sendMail(mailOptions);
    } catch (error) {
      this.logger.error('Error sending email:', error);
      throw new BadRequestException('Failed to send email');
    }
  }

  async sendVerificationEmail(email: string, userEmail: string): Promise<{ success: boolean; message: string }> {
    this.logger.log(`Preparing to send verification email to ${email}`);

    try {
      this.logger.log(`Sending verification email to ${email}`);
      if (email === userEmail) {
        return { success: true, message: 'successful user' };
      }

      // Check if email is already verified
      const existingRecord = await this.emailVerificationRepository.findOne({ where: { email } });
      if (existingRecord?.status === VerificationStatus.VERIFIED) {
        this.logger.log(`Email ${email} is already verified.`);
        return { success: true, message: 'successful user' };
      }

      this.logger.log(`Creating verification record for email: ${email}`);
      // Check if email is associated with a verified user
      const existingVerifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });
      if (existingVerifiedUser) {
        this.logger.log(`Email ${email} is already associated with a verified user.`);
        return { success: true, message: 'successful user' };
      }

      // Generate OTP and verification details
      this.logger.log(`Generating OTP and verification details for email: ${email}`);
      const otpCode = this.generateOtp();
      const otpExpiresAt = this.getOtpExpiryDate(20); // 20 minutes
      const verificationId = this.generateVerificationId();

      const verificationRecord = this.emailVerificationRepository.create({
        verificationId,
        email,
        method: VerificationMethod.OTP,
        status: VerificationStatus.PENDING,
        otpCode,
        otpExpiresAt,
      });

      this.logger.log(`Saving verification record for email: ${email}`);
      await this.emailVerificationRepository.save(verificationRecord);

      this.logger.log(`Verification record created for email: ${email}`);

      // Send verification email
      const subject = 'Email Verification';
      const text = `Your OTP code is ${otpCode}. It will expire in 20 minutes.`;
      const html = `<p>Your OTP code is <strong>${otpCode}</strong>.</p><p>It will expire in 20 minutes.</p>`;
      this.logger.log(`Sending verification email to ${email}`);
      await this.sendEmail(email, subject, text, html).then(info => {
        this.logger.log(`Verification email sent to ${email}: ${info.response}`);
      });

      return { success: true, message: `Verification email sent` };
    } catch (error) {
      this.logger.error(`Error sending verification email to ${email}:`, error);
      throw new BadRequestException('Failed to send verification email.');
    }
  }

  private generateOtp(): string {
    return Math.floor(100000 + Math.random() * 900000).toString(); // 6-digit
  }

  private getOtpExpiryDate(minutes: number): Date {
    const expires = new Date();
    expires.setMinutes(expires.getMinutes() + minutes);
    return expires;
  }

  private generateVerificationId(): string {
    return `ver-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
  }

  async checkEmailVerificationStatus(email: string): Promise<{ isVerified: boolean }> {
    try {
      const record = await this.emailVerificationRepository.findOne({ where: { email } });
      if (!record) {
        throw new NotFoundException('Email verification record not found');
      }
      return { isVerified: record.status === VerificationStatus.VERIFIED };
    } catch (error) {
      this.logger.error('Error checking email verification status:', error);
      throw new BadRequestException('Failed to check email verification status');
    }
  }

  //   Create verifiedusers table if not exists
  async createVerifiedUser(email: string): Promise<PesVerifiedUserResponseDto> {
    if (!email || !email.includes('@')) {
      throw new BadRequestException('Invalid email address');
    }

    try {
      this.logger.log(`Checking if user with email ${email} exists`);
      const existingUser = await this.userService.findUserByEmail(email);

      if (!existingUser) {
        this.logger.warn(`User with email ${email} not found`);
        throw new BadRequestException('User not found');
      }

      const validRoles = [UserRole.CLIENT];
      const isEligible = existingUser.isEmailVerified && validRoles.includes(existingUser.role);

      if (!isEligible) {
        this.logger.warn(`User with email ${email} is not eligible for verification`);
        throw new BadRequestException('User is not eligible for verification');
      }

      const alreadyVerified = await this.verifiedUsersRepository.findOne({ where: { userId: existingUser.id } });

      this.logger.log(`Checking if user with email ${email} is already verified`);
      if (alreadyVerified) {
        this.logger.log(`User with email ${email} is already verified`);
        return new PesVerifiedUserResponseDto(alreadyVerified);
      }

      const newVerifiedUser = this.verifiedUsersRepository.create({
        email,
        userId: existingUser.id,
        totalCampaigns: 0,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      this.logger.log(`Creating new verified user record for email: ${email}`);
      await this.verifiedUsersRepository.save(newVerifiedUser);

      return new PesVerifiedUserResponseDto(newVerifiedUser);
    } catch (error) {
      this.logger.error('Error creating verified user:', error);
      throw new BadRequestException('Failed to create verified user', {
        cause: error,
        description: error.message,
      });
    }
  }

  async verifyEmail(email: string, otpCode: string, userId: number): Promise<{ success: boolean; message: string }> {
    if (!email || !otpCode) {
      this.logger.warn('Missing email or OTP code');
      throw new BadRequestException('Email and OTP code are required');
    }

    try {
      this.logger.log(`Verifying email: ${email}`);

      const record = await this.emailVerificationRepository.findOne({
        where: { email, otpCode },
      });

      if (!record) {
        this.logger.warn(`No verification record found for email: ${email}`);
        throw new NotFoundException('Email verification record not found');
      }

      // Check OTP expiration
      if (record.otpExpiresAt && record.otpExpiresAt < new Date()) {
        throw new BadRequestException('OTP code has expired');
      }

      if (record.status === VerificationStatus.VERIFIED) {
        this.logger.log(`Email already verified: ${email}`);
        return { success: true, message: 'Email verified successfully' };
      }

      // Mark as verified
      record.status = VerificationStatus.VERIFIED;
      record.verifiedAt = new Date();
      await this.emailVerificationRepository.save(record);

      // Check if user is already in verifiedUsers
      const alreadyVerified = await this.verifiedUsersRepository.findOne({
        where: { email, userId },
      });

      if (!alreadyVerified) {
        const verifiedUser = this.verifiedUsersRepository.create({
          totalCampaigns: 0,
          email,
          userId,
        });
        await this.verifiedUsersRepository.save(verifiedUser);
        this.logger.log(`Verified user record created for: ${email}`);
      }

      return { success: true, message: 'Email verified successfully' };
    } catch (error) {
      this.logger.error(`Error verifying email for ${email}: ${error.message}`);
      if (error instanceof NotFoundException || error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException('Failed to verify email');
    }
  }

  async resendVerificationEmail(data: ResendVerificationDto): Promise<{ success: boolean; message: string }> {
    const { email } = data;

    return { success: true, message: `OTP resent to ${email}` };
  }

  async createCampaign(userEmail: string, campaignData: CreatePesCampaignDto): Promise<PesCampaignResponseDto> {
    return this.dataSource.transaction(async manager => {
      this.logger.log(`Creating campaign with data: ${JSON.stringify(campaignData)}`);
      try {
        const campaignId = `cmp-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;

        const template = await manager.findOne(this.templateRepository.target, {
          where: { id: campaignData.templateId },
        });

        if (!template) {
          throw new NotFoundException('Template not found');
        }

        const verifiedUser = await manager.findOne(this.verifiedUsersRepository.target, {
          where: { email: userEmail },
        });

        if (!verifiedUser) {
          throw new NotFoundException('Verified user not found');
        }

        const sendingProfile = campaignData.sendingProfileId
          ? await manager.findOne(this.sendingProfileRepository.target, {
              where: { id: campaignData.sendingProfileId },
            })
          : null;

        const landingPage = campaignData.landingPageId
          ? await manager.findOne(this.landingPageRepository.target, {
              where: { id: campaignData.landingPageId },
            })
          : null;

        if (!campaignData.recipientGroupId) {
          campaignData.recipientGroupId = null;
        }

        const recipientGroup = campaignData.recipientGroupId
          ? await manager.findOne(this.recipientGroupRepository.target, {
              where: { id: campaignData.recipientGroupId },
            })
          : null;

        const newCampaign = manager.create(this.campaignRepository.target, {
          ...campaignData,
          campaignId,
          status: CampaignStatus.DRAFT,
          template,
          verifiedUser,
          sendingProfile,
          landingPage,
          recipientGroup,
        });

        const createdCampaign = await manager.save(this.campaignRepository.target, newCampaign);

        this.logger.log(`Campaign created successfully with ID: ${createdCampaign.campaignId}`);
        return new PesCampaignResponseDto(createdCampaign);
      } catch (error) {
        this.logger.error('Error creating campaign:', error);
        if (error instanceof NotFoundException || error instanceof BadRequestException) {
          throw error;
        }
        throw new BadRequestException(error.message);
      }
    });
  }

  async updateCampaign(campaignId: string, campaignData: UpdatePesCampaignDto): Promise<PesCampaignResponseDto> {
    const campaign = await this.campaignRepository.findOne({ where: { campaignId } });
    if (!campaign) throw new NotFoundException('Campaign not found');
    Object.assign(campaign, campaignData);
    campaign.updatedAt = new Date();
    const updatedCampaign = await this.campaignRepository.save(campaign);
    this.logger.log(`Campaign updated successfully with ID: ${updatedCampaign.campaignId}`);
    return new PesCampaignResponseDto(updatedCampaign);
  }

  async deleteCampaign(campaignId: string): Promise<{ success: boolean; message: string }> {
    const result = await this.campaignRepository.delete({ campaignId });
    if (!result.affected) throw new NotFoundException('Campaign not found');
    return { success: true, message: 'Campaign deleted successfully' };
  }

  async sendTestEmail(email: string, templateId: string): Promise<{ success: boolean; message: string }> {
    try {
      // Step 1: Validate email verification
      const verifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });
      if (!verifiedUser) {
        this.logger.warn(`No verified user found with email: ${email}`);
        return { success: false, message: 'User not verified' };
      }

      // Step 2: Determine source of template

      const template = await this.templateRepository.findOne({ where: { id: templateId } });

      if (!template) throw new NotFoundException('Template not found');

      // Step 3: Prepare email content
      const subject = template.subject;
      const text = template.textContent || 'This is a test email from the template.';
      const html = template.htmlContent;

      // Step 4: Send the email
      this.logger.log(`Sending email to ${email}`);
      this.logger.debug(`Email subject: ${subject}`);

      await this.sendEmail(email, subject, text, html);
      return {
        success: true,
        message: 'Template email sent successfully',
      };
    } catch (error) {
      this.logger.error('Error sending email:', error);
      throw new BadRequestException('Failed to send email');
    }
  }
  async sendCampaignEmail(email: string, campaignId: string): Promise<{ success: boolean; message: string }> {
    try {
      // Step 1: Validate email verification
      const verifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });
      if (!verifiedUser) {
        this.logger.warn(`No verified user found with email: ${email}`);
        return { success: false, message: 'User not verified' };
      }

      // Step 2: Determine source of template
      const campaign = await this.campaignRepository.findOne({
        where: { campaignId },
        relations: ['template'],
      });

      if (!campaign) throw new NotFoundException('Campaign not found');

      // Create tracking record for this email
      const trackingRecord = await this.trackingService.createEmailTrackingRecord(email, campaign.trackingId);

      // Prepare email content with tracking
      let html = campaign.template.htmlContent;
      let text = campaign.template.textContent || 'Default email content';

      // Add tracking pixel
      const trackingPixel = `<img src="${this.configService.get('TRACKING_BASE_URL')}/api/v1/track/open/${campaign.trackingId}" width="1" height="1" />`;
      html = `${html}${trackingPixel}`;

      // Process links for click tracking
      html = this.processLinksForTracking(html, campaign.trackingId);

      // Send the email
      await this.sendEmail(email, campaign.template.subject, text, html).then(async info => {
        // Update tracking record with service message ID
        await this.trackingService.updateEmailTracking(trackingRecord.id, {
          serviceMessageId: info.messageId,
        });

        // Update campaign status
        await this.campaignRepository.update({ campaignId }, { status: CampaignStatus.COMPLETED });
      });

      return { success: true, message: 'Campaign email sent successfully' };
    } catch (error) {
      this.logger.error('Error sending email:', error);
      throw new BadRequestException('Failed to send email', error.message);
    }
  }

  private processLinksForTracking(html: string, trackingId: string): string {
    return html.replace(/<a\s+(?:[^>]*?\s+)?href=(["'])(.*?)\1/g, (match, originalUrl) => {
      if (originalUrl.startsWith('mailto:') || originalUrl.includes('/api/v1/track/click/')) {
        return match;
      }
      const encodedUrl = encodeURIComponent(originalUrl);
      const trackingUrl = `${this.configService.get('TRACKING_BASE_URL')}/api/v1/track/click/${trackingId}?url=${encodedUrl}`;
      return match.replace(originalUrl, trackingUrl);
    });
  }

  // Sending Profile methods
  async createSendingProfile(sendingProfileData: CreatePesSendingProfileDto): Promise<PesSendingProfileResponseDto> {
    const { verifiedUserId, name } = sendingProfileData;

    try {
      // ✅ Verify user exists
      if (verifiedUserId) {
        const userExists = await this.verifiedUsersRepository.exist({ where: { id: verifiedUserId } });
        if (!userExists) {
          this.logger.warn(`User not found for ID: ${verifiedUserId}`);
          throw new NotFoundException('User not found');
        }
      }

      // ✅ Create and save the new profile
      const newProfile = this.sendingProfileRepository.create({
        ...sendingProfileData,
        profileId: `profile-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      const savedProfile = await this.sendingProfileRepository.save(newProfile);

      this.logger.log(`Sending profile created with ID: ${savedProfile.profileId}`);

      return new PesSendingProfileResponseDto(savedProfile);
    } catch (error) {
      this.logger.error('Error creating sending profile:', error?.message || error);
      throw new BadRequestException(error?.message || 'Failed to create sending profile');
    }
  }

  async testSmtpConnection(smtpConfig: SMTPConnectionTestProp): Promise<{ success: boolean; message: string }> {
    try {
      this.logger.log('Testing SMTP connection with provided configuration');

      const transporter = nodemailer.createTransport({
        host: smtpConfig.smtpHost,
        port: smtpConfig.smtpPort,
        secure: smtpConfig.smtpTls, // true for 465, false for other ports
        auth: {
          user: smtpConfig.smtpUsername,
          pass: smtpConfig.smtpPassword,
        },
      });

      // Test the connection
      await transporter.verify();

      this.logger.log('SMTP connection successful');
      return { success: true, message: 'SMTP connection successful' };
    } catch (error) {
      this.logger.error('SMTP connection test failed:', error);
      throw new BadRequestException('Failed to connect to SMTP server');
    }
  }

  async getSendingProfilesByVerifiedUserId(verifiedUserId: string): Promise<PesSendingProfileResponseDto[]> {
    this.logger.log(`Fetching sending profiles for verified user ID: ${verifiedUserId}`);

    try {
      const profiles = await this.sendingProfileRepository.find({
        where: { verifiedUserId },
        order: { createdAt: 'DESC' },
      });

      if (!profiles.length) {
        this.logger.warn(`No sending profiles found for user ID: ${verifiedUserId}`);
        throw new NotFoundException('No sending profiles found for this user');
      }

      this.logger.log(`Found ${profiles.length} sending profiles for user ID: ${verifiedUserId}`);
      return profiles.map(profile => new PesSendingProfileResponseDto(profile));
    } catch (error) {
      this.logger.error(`Error fetching sending profiles for user ID ${verifiedUserId}:`, error);
      if (error instanceof NotFoundException || error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException('Failed to fetch sending profiles for the user');
    }
  }

  async getSendingProfileById(profileId: string): Promise<PesSendingProfileResponseDto | null> {
    this.logger.log(`Fetching sending profile with ID: ${profileId}`);

    try {
      const profile = await this.sendingProfileRepository.findOne({
        where: { profileId },
      });

      if (!profile) {
        this.logger.warn(`Sending profile with ID ${profileId} not found`);
        return null;
      }

      this.logger.log(`Found sending profile with ID: ${profileId}`);
      return new PesSendingProfileResponseDto(profile);
    } catch (error) {
      this.logger.error(`Error fetching sending profile with ID ${profileId}:`, error);
      throw new BadRequestException('Failed to fetch sending profile');
    }
  }

  async updateSendingProfile(profileId: string, sendingProfileData: UpdatePesSendingProfileDto): Promise<PesSendingProfileResponseDto> {
    this.logger.log(`Updating sending profile with ID: ${profileId}`);

    try {
      const profile = await this.sendingProfileRepository.findOne({ where: { profileId } });
      if (!profile) {
        this.logger.warn(`Sending profile with ID ${profileId} not found`);
        throw new NotFoundException('Sending profile not found');
      }

      Object.assign(profile, sendingProfileData);
      profile.updatedAt = new Date();

      const updatedProfile = await this.sendingProfileRepository.save(profile);
      this.logger.log(`Sending profile updated successfully with ID: ${updatedProfile.profileId}`);
      return new PesSendingProfileResponseDto(updatedProfile);
    } catch (error) {
      this.logger.error(`Error updating sending profile with ID ${profileId}:`, error);
      throw new BadRequestException('Failed to update sending profile');
    }
  }

  async deleteSendingProfile(profileId: string): Promise<{ success: boolean; message: string }> {
    this.logger.log(`Deleting sending profile with ID: ${profileId}`);

    try {
      const result = await this.sendingProfileRepository.delete({ profileId });
      if (!result.affected) {
        this.logger.warn(`Sending profile with ID ${profileId} not found`);
        throw new NotFoundException('Sending profile not found');
      }

      this.logger.log(`Sending profile deleted successfully with ID: ${profileId}`);
      return { success: true, message: 'Sending profile deleted successfully' };
    } catch (error) {
      this.logger.error(`Error deleting sending profile with ID ${profileId}:`, error);
      throw new BadRequestException('Failed to delete sending profile');
    }
  }

  // Landing page methods
  async createLandingPage(landingPageData: CreatePesLandingPageDto): Promise<PesLandingPageResponseDto> {
    const { verifiedUserId, name } = landingPageData;
    try {
      // ✅ Verify user exists
      if (verifiedUserId) {
        const userExists = await this.verifiedUsersRepository.findOne({ where: { id: verifiedUserId } });
        if (!userExists) {
          this.logger.warn(`User not found for ID: ${verifiedUserId}`);
          throw new NotFoundException('User not found');
        }
      }

      // ✅ Prevent duplicate landing page name for the same user
      const duplicatePage = await this.landingPageRepository.findOne({
        where: { verifiedUserId, name },
      });

      if (duplicatePage) {
        this.logger.warn(`Duplicate landing page name "${name}" for user ID: ${verifiedUserId}`);
        throw new BadRequestException('Landing page with this name already exists for the user');
      }

      // ✅ Create and save the new landing page
      const newLandingPage = this.landingPageRepository.create({
        ...landingPageData,
        pageId: `page-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
      });

      const savedLandingPage = await this.landingPageRepository.save(newLandingPage);

      this.logger.log(`Landing page created with ID: ${savedLandingPage.pageId}`);

      return new PesLandingPageResponseDto(savedLandingPage);
    } catch (error) {
      this.logger.error('Error creating landing page:', error?.message || error);
      throw new BadRequestException(error?.message || 'Failed to create landing page');
    }
  }

  async updateLandingPage(pageId: string, landingPageData: UpdatePesLandingPageDto): Promise<PesLandingPageResponseDto> {
    this.logger.log(`Updating landing page with ID: ${pageId}`);

    try {
      const page = await this.landingPageRepository.findOne({ where: { id: pageId } });
      if (!page) {
        this.logger.warn(`Landing page with ID ${pageId} not found`);
        throw new NotFoundException('Landing page not found');
      }

      Object.assign(page, landingPageData);
      page.updatedAt = new Date();

      const updatedPage = await this.landingPageRepository.save(page);
      this.logger.log(`Landing page updated successfully with ID: ${updatedPage.pageId}`);
      return new PesLandingPageResponseDto(updatedPage);
    } catch (error) {
      this.logger.error(`Error updating landing page with ID ${pageId}:`, error);
      throw new BadRequestException('Failed to update landing page');
    }
  }

  async getLandingPageById(pageId: string): Promise<PesLandingPageResponseDto | null> {
    this.logger.log(`Fetching landing page with ID: ${pageId}`);

    try {
      const page = await this.landingPageRepository.findOne({ where: { pageId } });
      if (!page) {
        this.logger.warn(`Landing page with ID ${pageId} not found`);
        return null;
      }

      this.logger.log(`Found landing page with ID: ${pageId}`);
      return new PesLandingPageResponseDto(page);
    } catch (error) {
      this.logger.error(`Error fetching landing page with ID ${pageId}:`, error);
      throw new BadRequestException('Failed to fetch landing page');
    }
  }

  async deleteLandingPage(pageId: string): Promise<{ success: boolean; message: string }> {
    this.logger.log(`Deleting landing page with ID: ${pageId}`);

    try {
      const result = await this.landingPageRepository.delete({ id: pageId });
      if (!result.affected) {
        this.logger.warn(`Landing page with ID ${pageId} not found`);
        throw new NotFoundException('Landing page not found');
      }

      this.logger.log(`Landing page deleted successfully with ID: ${pageId}`);
      return { success: true, message: 'Landing page deleted successfully' };
    } catch (error) {
      this.logger.error(`Error deleting landing page with ID ${pageId}:`, error);
      throw new BadRequestException('Failed to delete landing page');
    }
  }
  async getLandingPagesByVerifiedUserId(verifiedUserId: string): Promise<PesLandingPageResponseDto[]> {
    this.logger.log(`Fetching landing pages for verified user ID: ${verifiedUserId}`);

    try {
      const pages = await this.landingPageRepository.find({
        where: { verifiedUserId },
        order: { createdAt: 'DESC' },
      });

      if (!pages.length) {
        this.logger.warn(`No landing pages found for user ID: ${verifiedUserId}`);
        throw new NotFoundException('No landing pages found for this user');
      }

      this.logger.log(`Found ${pages.length} landing pages for user ID: ${verifiedUserId}`);
      return pages.map(page => new PesLandingPageResponseDto(page));
    } catch (error) {
      this.logger.error(`Error fetching landing pages for user ID ${verifiedUserId}:`, error);
      if (error instanceof NotFoundException || error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException('Failed to fetch landing pages for the user');
    }
  }

  // Template methods
  async createTemplate(templateData: CreatePesTemplateDto): Promise<PesTemplateResponseDto> {
    try {
      this.logger.log(`Creating template with data: ${JSON.stringify(templateData)}`);

      // Check if template with the same name already exists for the user
      const existingTemplate = await this.templateRepository.findOne({
        where: { name: templateData.name, creator: { id: templateData.createdBy } },
      });

      if (existingTemplate) {
        this.logger.warn(`Template with name "${templateData.name}" already exists for user ID: ${templateData.createdBy}`);
        throw new BadRequestException('Template with this name already exists for the user');
      }

      // ✅ Verify user exists
      const userExists = await this.verifiedUsersRepository.findOne({ where: { id: templateData.createdBy } });
      if (!userExists) {
        this.logger.warn(`User not found for ID: ${templateData.createdBy}`);
        throw new NotFoundException('User not found');
      }

      // Create and save the new template
      const newTemplate = this.templateRepository.create({
        ...templateData,
        createdAt: new Date(),
        updatedAt: new Date(),
        templateId: `template-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
        creator: { id: templateData.createdBy },
      });

      const savedTemplate = await this.templateRepository.save(newTemplate);

      this.logger.log(`Template created successfully with ID: ${savedTemplate.id}`);
      return new PesTemplateResponseDto(savedTemplate);
    } catch (error) {
      this.logger.error('Error creating template:', error);
      throw new BadRequestException('Failed to create template');
    }
  }

  async getAllTemplates(): Promise<PesTemplateResponseDto[]> {
    try {
      const templates = await this.templateRepository.find();
      return templates.map(template => new PesTemplateResponseDto(template));
    } catch (error) {
      this.logger.error('Error fetching templates:', error);
      throw new BadRequestException('Failed to fetch templates');
    }
  }

  async updateTemplate(templateId: string, templateData: UpdatePesTemplateDto): Promise<PesTemplateResponseDto> {
    this.logger.log(`Updating template with ID: ${templateId}`);

    try {
      const template = await this.templateRepository.findOne({ where: { id: templateId } });
      if (!template) {
        this.logger.warn(`Template with ID ${templateId} not found`);
        throw new NotFoundException('Template not found');
      }

      Object.assign(template, { ...templateData });
      template.updatedAt = new Date();

      const updatedTemplate = await this.templateRepository.save(template);
      this.logger.log(`Template updated successfully with ID: ${updatedTemplate.id}`);
      return new PesTemplateResponseDto(updatedTemplate);
    } catch (error) {
      this.logger.error(`Error updating template with ID ${templateId}:`, error);
      throw new BadRequestException('Failed to update template');
    }
  }

  async getTemplateById(templateId: string): Promise<PesTemplateResponseDto | null> {
    this.logger.log(`Fetching template with ID: ${templateId}`);

    try {
      const template = await this.templateRepository.findOne({ where: { id: templateId } });
      if (!template) {
        this.logger.warn(`Template with ID ${templateId} not found`);
        return null;
      }

      this.logger.log(`Found template with ID: ${templateId}`);
      return new PesTemplateResponseDto(template);
    } catch (error) {
      this.logger.error(`Error fetching template with ID ${templateId}:`, error);
      throw new BadRequestException('Failed to fetch template');
    }
  }

  async getTemplatesByVerifiedUserId(verifiedUserId: string): Promise<PesTemplateResponseDto[]> {
    this.logger.log(`Fetching templates for verified user ID: ${verifiedUserId}`);

    try {
      const templates = await this.templateRepository.find({
        where: { creator: { id: verifiedUserId } },
        order: { createdAt: 'DESC' },
      });

      if (!templates.length) {
        this.logger.warn(`No templates found for user ID: ${verifiedUserId}`);
        throw new NotFoundException('No templates found for this user');
      }

      this.logger.log(`Found ${templates.length} templates for user ID: ${verifiedUserId}`);
      return templates.map(template => new PesTemplateResponseDto(template));
    } catch (error) {
      this.logger.error(`Error fetching templates for user ID ${verifiedUserId}:`, error);
      if (error instanceof NotFoundException || error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException('Failed to fetch templates for the user');
    }
  }

  async deleteTemplate(templateId: string): Promise<{ success: boolean; message: string }> {
    this.logger.log(`Deleting template with ID: ${templateId}`);

    try {
      const result = await this.templateRepository.delete({ id: templateId });
      if (!result.affected) {
        this.logger.warn(`Template with ID ${templateId} not found`);
        throw new NotFoundException('Template not found');
      }

      this.logger.log(`Template deleted successfully with ID: ${templateId}`);
      return { success: true, message: 'Template deleted successfully' };
    } catch (error) {
      this.logger.error(`Error deleting template with ID ${templateId}:`, error);
      throw new BadRequestException('Failed to delete template');
    }
  }

  // Campaign methods
  async getCampaignByVerifiedUserEmail(email: string): Promise<PesCampaignResponseDto[]> {
    this.logger.log(`Fetching campaigns for verified user with email: ${email}`);

    try {
      let verifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });

      if (!verifiedUser) {
        this.logger.warn(`No verified user found with email: ${email}`);
        const result = await this.createVerifiedUser(email);
        if (!result) {
          this.logger.warn(`User with email ${email} could not be verified`);
          throw new BadRequestException('User could not be verified');
        }
        verifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });
      }

      this.logger.log(`Fetching campaigns for verified user with email: ${email}`);
      const campaigns = await this.campaignRepository.find({
        where: { verifiedUser: { email } },
        relations: ['template', 'sendingProfile', 'landingPage'],
      });

      if (!campaigns.length) {
        this.logger.warn(`No campaigns found for verified user with email: ${email}`);
        throw new NotFoundException('No campaigns found for this user');
      }

      this.logger.log(`Found ${campaigns.length} campaigns for verified user with email: ${email}`);
      return campaigns.map(campaign => new PesCampaignResponseDto(campaign));
    } catch (error) {
      this.logger.error(`Error fetching campaigns for verified user with email ${email}:`, error);
      if (error instanceof NotFoundException || error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException('Failed to fetch campaigns for the user');
    }
  }

  // Verified user methods
  async getVerifiedUserByEmail(email: string): Promise<PesVerifiedUserResponseDto> {
    this.logger.log(`Fetching or creating verified user by email: ${email}`);

    try {
      let verifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });

      if (!verifiedUser) {
        this.logger.warn(`No verified user found for email: ${email}. Creating new verified user.`);

        const result = await this.createVerifiedUser(email);

        if (!result) {
          this.logger.error(`Failed to create verified user for email: ${email}`);
          throw new BadRequestException('User could not be verified');
        }

        verifiedUser = await this.verifiedUsersRepository.findOne({ where: { email } });
      }

      return new PesVerifiedUserResponseDto(verifiedUser);
    } catch (error) {
      this.logger.error(`Error in getVerifiedUserByEmail for ${email}:`, error);
      throw new BadRequestException('Failed to fetch or create verified user');
    }
  }

  // Recipient Group methods
  async getRecipientGroupByVerifiedUserId(verifiedUserId: string): Promise<PesRecipientGroupResponseDto[]> {
    this.logger.log(`Fetching recipient groups for verified user ID: ${verifiedUserId}`);

    const recipientGroups = await this.recipientGroupRepository.find({
      where: { verifiedUser: { id: verifiedUserId } },
      relations: ['recipients'],
    });

    if (!recipientGroups.length) {
      this.logger.warn(`No recipient groups found for verified user ID: ${verifiedUserId}`);
      throw new NotFoundException('No recipient groups found for this user');
    }

    return recipientGroups.map(group => new PesRecipientGroupResponseDto(group));
  }

  async createRecipientGroup(data: CreatePesRecipientGroupDto): Promise<PesRecipientGroupResponseDto> {
    this.logger.log(`Creating recipient group with data: ${JSON.stringify(data)}`);

    try {
      const { name, verifiedUserId } = data;

      // Check if verified user exists
      const verifiedUser = await this.verifiedUsersRepository.findOne({ where: { id: verifiedUserId } });
      if (!verifiedUser) {
        this.logger.warn(`Verified user not found for ID: ${verifiedUserId}`);
        throw new NotFoundException('Verified user not found');
      }

      // Check for duplicate group name
      const existingGroup = await this.recipientGroupRepository.findOne({
        where: { name, verifiedUser: { id: verifiedUserId } },
      });
      if (existingGroup) {
        this.logger.warn(`Recipient group with name "${name}" already exists for user ID: ${verifiedUserId}`);
        throw new BadRequestException('Recipient group with this name already exists for the user');
      }

      // Create and save the new recipient group
      const newGroup = this.recipientGroupRepository.create({
        ...data,
        createdAt: new Date(),
        updatedAt: new Date(),
        verifiedUser,
      });

      const savedGroup = await this.recipientGroupRepository.save(newGroup);
      this.logger.log(`Recipient group created with ID: ${savedGroup.id}`);

      return new PesRecipientGroupResponseDto(savedGroup);
    } catch (error) {
      this.logger.error('Error creating recipient group:', error?.message || error);
      throw new BadRequestException(error?.message || 'Failed to create recipient group');
    }
  }

  async addRecipientsToGroup(groupId: string, emails: string[]): Promise<PesRecipientGroupResponseDto> {
    this.logger.log(`Adding recipients to group ID: ${groupId} with recipient emails: ${emails}`);

    return this.dataSource.transaction(async manager => {
      // 1. Fetch recipient group with existing recipients
      const recipientGroup = await manager.findOne(this.recipientGroupRepository.target, {
        where: { id: groupId },
        relations: ['recipients'],
      });

      if (!recipientGroup) {
        this.logger.warn(`Recipient group not found for ID: ${groupId}`);
        throw new NotFoundException('Recipient group not found');
      }

      // 2. Fetch existing recipients
      const existingRecipients = await manager.find(this.recipientRepository.target, {
        where: { email: In(emails) },
      });

      const existingEmails = new Set(existingRecipients.map(r => r.email));
      const missingEmails = emails.filter(email => !existingEmails.has(email));

      const newlyCreatedRecipients = [];

      for (const email of missingEmails) {
        const user = await this.userService.findUserByEmail(email);

        if (!user) {
          this.logger.warn(`Recipient/User not found for email: ${email}`);
          throw new NotFoundException(`Recipient with email ${email} not found`);
        }

        const newRecipient = manager.create(this.recipientRepository.target, {
          email: user.email,
          firstName: user.firstName,
          lastName: user.lastName,
          department: recipientGroup.department,
          isVerified: true,
          notes: 'Created via group assignment',
          groups: [recipientGroup],
        });

        const savedRecipient = await manager.save(this.recipientRepository.target, newRecipient);
        newlyCreatedRecipients.push(savedRecipient);
      }

      const allRecipients = [...existingRecipients, ...newlyCreatedRecipients];

      // Filter recipients not already in the group
      const existingGroupRecipientIds = new Set(recipientGroup.recipients.map(r => r.id));
      const recipientsToAdd = allRecipients.filter(r => !existingGroupRecipientIds.has(r.id));

      if (recipientsToAdd.length === 0) {
        throw new BadRequestException('All recipients are already in the group');
      }

      recipientGroup.recipients.push(...recipientsToAdd);
      recipientGroup.updatedAt = new Date();

      const updatedGroup = await manager.save(this.recipientGroupRepository.target, recipientGroup);

      this.logger.log(`Successfully added ${recipientsToAdd.length} recipient(s) to group ID: ${groupId}`);

      return new PesRecipientGroupResponseDto(updatedGroup);
    });
  }

  async removeRecipientsFromGroup(groupId: string, recipientIds: string[]): Promise<PesRecipientGroupResponseDto> {
    this.logger.log(`Removing recipients from group ID: ${groupId} with recipient IDs: ${recipientIds}`);

    return this.dataSource.transaction(async manager => {
      // 1. Fetch the recipient group along with its recipients
      const recipientGroup = await manager.findOne(this.recipientGroupRepository.target, {
        where: { id: groupId },
        relations: ['recipients'],
      });

      if (!recipientGroup) {
        this.logger.warn(`Recipient group not found for ID: ${groupId}`);
        throw new NotFoundException('Recipient group not found');
      }

      // 2. Filter recipients to be removed
      const originalCount = recipientGroup.recipients.length;
      recipientGroup.recipients = recipientGroup.recipients.filter(r => !recipientIds.includes(r.id));

      if (recipientGroup.recipients.length === originalCount) {
        this.logger.warn(`No matching recipients found to remove from group ID: ${groupId}`);
        throw new BadRequestException('No recipients found to remove from the group');
      }

      recipientGroup.updatedAt = new Date();

      // 3. Save the updated group
      const updatedGroup = await manager.save(this.recipientGroupRepository.target, recipientGroup);

      this.logger.log(`Successfully removed recipients from group ID: ${updatedGroup.id}`);

      return new PesRecipientGroupResponseDto(updatedGroup);
    });
  }

  async getRecipientGroupById(groupId: string): Promise<PesRecipientGroupResponseDto> {
    const group = await this.recipientGroupRepository.findOne({
      where: { id: groupId },
      relations: ['recipients'],
    });

    if (!group) {
      throw new NotFoundException('Recipient group not found');
    }

    return new PesRecipientGroupResponseDto(group);
  }

  async removeRecipientGroup(groupId: string, verifiedUserId: string): Promise<PesRecipientGroupResponseDto[]> {
    this.logger.log(`Removing recipient group with ID: ${groupId} and verification ID: ${verifiedUserId}`);

    try {
      const group = await this.recipientGroupRepository.findOne({
        where: { id: groupId, verifiedUserId },
      });

      if (!group) {
        this.logger.warn(`Recipient group not found for ID: ${groupId} and verification ID: ${verifiedUserId}`);
        throw new NotFoundException('Recipient group not found');
      }

      await this.recipientGroupRepository.remove(group);
      this.logger.log(`Recipient group with ID: ${groupId} removed successfully`);

      return this.getRecipientGroupByVerifiedUserId(verifiedUserId);
    } catch (error) {
      this.logger.error(`Error removing recipient group with ID ${groupId}:`, error);
      throw new BadRequestException('Failed to remove recipient group');
    }
  }

}
