import {
  Injectable,
  NotFoundException,
  Logger,
} from '@nestjs/common';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { PrismaService } from '../../prisma/prisma.service';
import { SendEmailDto } from './dto/send-email.dto';
import { UpsertEmailTemplateDto } from './dto/upsert-email-template.dto';
import { FindEmailLogsDto } from './dto/find-email-logs.dto';
import { Prisma, EmailType } from '@prisma/client';

export interface PaginatedResult<T> {
  data: T[];
  nextCursor: string | null;
  hasMore: boolean;
}

export interface EmailJobData {
  emailLogId: string;
  clubId: string;
  to: string;
  subject: string;
  html: string;
  text?: string;
}

@Injectable()
export class EmailService {
  private readonly logger = new Logger(EmailService.name);

  constructor(
    private readonly prisma: PrismaService,
    @InjectQueue('email-send') private readonly emailQueue: Queue,
  ) {}

  // ─── Templates ──────────────────────────────────────────────────

  async findTemplates(clubId: string) {
    return this.prisma.emailTemplate.findMany({
      where: { clubId },
      orderBy: { type: 'asc' },
    });
  }

  async findTemplateByType(clubId: string, type: EmailType) {
    const template = await this.prisma.emailTemplate.findUnique({
      where: { clubId_type: { clubId, type } },
    });

    if (!template) {
      throw new NotFoundException(`Email template for type ${type} not found`);
    }
    return template;
  }

  async upsertTemplate(clubId: string, dto: UpsertEmailTemplateDto) {
    return this.prisma.emailTemplate.upsert({
      where: { clubId_type: { clubId, type: dto.type } },
      create: {
        clubId,
        type: dto.type,
        subject: dto.subject,
        bodyHtml: dto.bodyHtml,
        bodyText: dto.bodyText,
        variables: dto.variables ?? [],
        isActive: dto.isActive ?? true,
      },
      update: {
        subject: dto.subject,
        bodyHtml: dto.bodyHtml,
        ...(dto.bodyText !== undefined ? { bodyText: dto.bodyText } : {}),
        ...(dto.variables !== undefined ? { variables: dto.variables } : {}),
        ...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
      },
    });
  }

  async deleteTemplate(clubId: string, type: EmailType) {
    const template = await this.prisma.emailTemplate.findUnique({
      where: { clubId_type: { clubId, type } },
    });

    if (!template) {
      throw new NotFoundException(`Email template for type ${type} not found`);
    }

    await this.prisma.emailTemplate.delete({
      where: { id: template.id },
    });
  }

  // ─── Send ───────────────────────────────────────────────────────

  async send(clubId: string, dto: SendEmailDto) {
    const template = await this.prisma.emailTemplate.findUnique({
      where: { clubId_type: { clubId, type: dto.type } },
    });

    const subject = dto.subject ?? this.renderTemplate(
      template?.subject ?? this.getDefaultSubject(dto.type),
      dto.variables ?? {},
    );

    const html = dto.bodyHtml ?? this.renderTemplate(
      template?.bodyHtml ?? this.getDefaultBody(dto.type),
      dto.variables ?? {},
    );

    const text = template?.bodyText
      ? this.renderTemplate(template.bodyText, dto.variables ?? {})
      : undefined;

    const emailLog = await this.prisma.emailLog.create({
      data: {
        clubId,
        templateId: template?.id,
        recipientEmail: dto.recipientEmail,
        recipientClientId: dto.clientId,
        subject,
        type: dto.type,
        status: 'PENDING',
      },
    });

    const jobData: EmailJobData = {
      emailLogId: emailLog.id,
      clubId,
      to: dto.recipientEmail,
      subject,
      html,
      text,
    };

    await this.emailQueue.add('send', jobData, {
      attempts: 3,
      backoff: { type: 'exponential', delay: 5000 },
      removeOnComplete: 100,
      removeOnFail: 50,
    });

    this.logger.log(
      `Email queued: ${dto.type} to ${dto.recipientEmail} (log: ${emailLog.id})`,
    );

    return { emailLogId: emailLog.id, status: 'queued' };
  }

  async sendBulk(clubId: string, emails: SendEmailDto[]) {
    const results = [];
    for (const dto of emails) {
      const result = await this.send(clubId, dto);
      results.push(result);
    }
    return { queued: results.length, results };
  }

  // ─── Logs ───────────────────────────────────────────────────────

  async findLogs(
    clubId: string,
    params: FindEmailLogsDto,
  ): Promise<PaginatedResult<any>> {
    const { cursor, limit = 20, type, status } = params;

    const where: Prisma.EmailLogWhereInput = {
      clubId,
      ...(type ? { type } : {}),
      ...(status ? { status } : {}),
    };

    const items = await this.prisma.emailLog.findMany({
      where,
      take: limit + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { createdAt: 'desc' },
      include: {
        template: { select: { type: true, subject: true } },
        client: { select: { id: true, firstName: true, lastName: true } },
      },
    });

    const hasMore = items.length > limit;
    const data = hasMore ? items.slice(0, -1) : items;

    return {
      data,
      nextCursor: hasMore ? data[data.length - 1].id : null,
      hasMore,
    };
  }

  async getStats(clubId: string) {
    const [total, sent, delivered, opened, failed] = await Promise.all([
      this.prisma.emailLog.count({ where: { clubId } }),
      this.prisma.emailLog.count({ where: { clubId, status: 'SENT' } }),
      this.prisma.emailLog.count({ where: { clubId, status: 'DELIVERED' } }),
      this.prisma.emailLog.count({ where: { clubId, status: 'OPENED' } }),
      this.prisma.emailLog.count({ where: { clubId, status: 'FAILED' } }),
    ]);

    return {
      total,
      sent,
      delivered,
      opened,
      failed,
      deliveryRate: total > 0 ? ((delivered + opened) / total * 100).toFixed(1) : '0',
      openRate: delivered > 0 ? (opened / delivered * 100).toFixed(1) : '0',
    };
  }

  // ─── Tracking webhook (for provider callbacks) ─────────────────

  async updateStatus(
    providerMessageId: string,
    status: 'DELIVERED' | 'OPENED' | 'CLICKED' | 'BOUNCED',
  ) {
    const log = await this.prisma.emailLog.findFirst({
      where: { providerMessageId },
    });

    if (!log) return;

    const update: any = { status };
    if (status === 'DELIVERED') update.deliveredAt = new Date();
    if (status === 'OPENED') update.openedAt = new Date();
    if (status === 'CLICKED') update.clickedAt = new Date();

    await this.prisma.emailLog.update({
      where: { id: log.id },
      data: update,
    });
  }

  // ─── Template rendering ────────────────────────────────────────

  private renderTemplate(
    template: string,
    variables: Record<string, string>,
  ): string {
    return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
      return variables[key] ?? '';
    });
  }

  private getDefaultSubject(type: EmailType): string {
    const subjects: Record<EmailType, string> = {
      WELCOME: 'Добро пожаловать в {{clubName}}!',
      TRAINING_REMINDER: 'Напоминание о тренировке',
      MEMBERSHIP_EXPIRING: 'Ваш абонемент скоро истекает',
      PROMO: 'Специальное предложение от {{clubName}}',
      BIRTHDAY: 'С Днём рождения! {{clubName}}',
    };
    return subjects[type];
  }

  private getDefaultBody(type: EmailType): string {
    const bodies: Record<EmailType, string> = {
      WELCOME: `
        <h2>Добро пожаловать, {{clientName}}!</h2>
        <p>Рады видеть вас в {{clubName}}.</p>
        <p>Ваш тренер: {{trainerName}}</p>
        <p>Желаем успешных тренировок!</p>
      `,
      TRAINING_REMINDER: `
        <h2>Напоминание о тренировке</h2>
        <p>{{clientName}}, у вас запланирована тренировка:</p>
        <p><strong>Дата:</strong> {{date}}</p>
        <p><strong>Тренер:</strong> {{trainerName}}</p>
        <p>Ждём вас!</p>
      `,
      MEMBERSHIP_EXPIRING: `
        <h2>Абонемент истекает</h2>
        <p>{{clientName}}, ваш абонемент «{{membershipName}}» истекает {{expiryDate}}.</p>
        <p>Продлите абонемент, чтобы не прерывать тренировки!</p>
      `,
      PROMO: `
        <h2>{{promoTitle}}</h2>
        <p>{{clientName}}, специально для вас:</p>
        <p>{{promoText}}</p>
      `,
      BIRTHDAY: `
        <h2>С Днём рождения, {{clientName}}!</h2>
        <p>{{clubName}} поздравляет вас с праздником!</p>
        <p>{{birthdayMessage}}</p>
      `,
    };
    return bodies[type];
  }
}
