import { Injectable, Logger } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { Subject } from 'rxjs';
import { filter } from 'rxjs/operators';
import * as nodemailer from 'nodemailer';
import * as crypto from 'crypto';
import { InquiryNotificationEntity } from './entities/inquiry-notification.entity';
import { buildEmailHtmlTemplate } from './email-template';
import { MailsService } from '../mails/mails.service'; // 🆕
export interface InquiryNotificationEvent {
  targetUserId: number;
  targetEmail?: string;
  type: string;
  inquiry_id: number;
  inquiry_ref: string;
  company_name: string;
  old_status: string;
  new_status: string;
  message: string;
  timestamp: string;
  pdf_url?: string;
  docx_url?: string;
  inquiry_type?: string;
  auditor_name?: string;
  previous_cert_no?: string;
  cert_body?: string;        // ✅ ADD THIS LINE
  audit_stage?: string;      // ✅ ADD THIS LINE
  submitted_by_name?: string;   // ✅ NEW
  submitted_by_email?: string;  // ✅ NEW
  notes?: string; // ✅ NEW
  actor_user_id?: number; // 🆕 send from this user's mailbox
  cc?: string[]; // 🆕 ← ADD THIS LINE
}

@Injectable()
export class InquiryNotificationsService {
  private readonly logger = new Logger(InquiryNotificationsService.name);
  private readonly events$ = new Subject<
    InquiryNotificationEvent & { dbId: number }
  >();

  // ✅ NEW — Scheme CC email(s) from env, supports comma-separated list
  private readonly schemeCcEmails: string[] = (
    process.env.SCHEME_CC_EMAIL || ''
  )
    .split(',')
    .map((e) => e.trim())
    .filter(Boolean);

  // ✅ NEW — Which notification types should CC scheme department
  private readonly ccTypes = new Set<string>([
    'NEW_INQUIRY',
    'CHANGES_REQUESTED',
    'CLIENT_CONFIRMED',
    'FINAL_ISSUED',
    // Add or remove types as needed:
    // 'IN_REVIEW',
    // 'DRAFT_READY',
  ]);

  // private readonly transporter = nodemailer.createTransport({
  //   host: 'smtp.gmail.com',
  //   port: 587,
  //   secure: false,
  //   auth: {
  //     user: process.env.GMAIL_USER || '',
  //     pass: process.env.GMAIL_PASS || '',
  //   },
  // });

  // ✅ UPDATED — Zoho SMTP transporter (configurable via env)
  private readonly transporter = nodemailer.createTransport({
    host: process.env.ZOHO_HOST || 'smtp.zoho.com',
    port: Number(process.env.ZOHO_PORT) || 587,
    secure: Number(process.env.ZOHO_PORT) === 465, // true for 465 (SSL), false for 587 (TLS)
    auth: {
      user: process.env.ZOHO_USER || '',
      pass: process.env.ZOHO_PASS || '',
    },
  });

  constructor(
    @InjectDataSource('scheme_dbs')
    private readonly dataSource: DataSource,
    private readonly mailsService: MailsService, // 🆕

  ) { }

  static verifyDownloadToken(
    token: string,
    inquiryId: number,
    type: 'pdf' | 'docx',
  ): boolean {
    try {
      const secret =
        process.env.DOWNLOAD_SECRET ||
        'change-this-in-env-must-be-long-random-string-32chars';
      const decoded = Buffer.from(token, 'base64url').toString('utf-8');
      const parts = decoded.split('.');
      if (parts.length !== 4) return false;
      const [id, t, exp, sig] = parts;
      if (Number(id) !== inquiryId || t !== type) return false;
      if (Number(exp) < Date.now()) return false;
      const expected = crypto
        .createHmac('sha256', secret)
        .update(`${id}.${t}.${exp}`)
        .digest('hex')
        .slice(0, 32);
      return sig === expected;
    } catch {
      return false;
    }
  }

  private signDownloadToken(inquiryId: number, type: 'pdf' | 'docx'): string {
    const secret =
      process.env.DOWNLOAD_SECRET ||
      'change-this-in-env-must-be-long-random-string-32chars';
    const expiresAt = Date.now() + 30 * 24 * 60 * 60 * 1000;
    const payload = `${inquiryId}.${type}.${expiresAt}`;
    const signature = crypto
      .createHmac('sha256', secret)
      .update(payload)
      .digest('hex')
      .slice(0, 32);
    return Buffer.from(`${payload}.${signature}`).toString('base64url');
  }

  async emit(event: InquiryNotificationEvent): Promise<void> {
    try {
      if (event.pdf_url && !event.pdf_url.includes('token=')) {
        const token = this.signDownloadToken(event.inquiry_id, 'pdf');
        event.pdf_url = `/api/inquiries/${event.inquiry_id}/download-draft/pdf?token=${token}`;
      }
      if (event.docx_url && !event.docx_url.includes('token=')) {
        const token = this.signDownloadToken(event.inquiry_id, 'docx');
        event.docx_url = `/api/inquiries/${event.inquiry_id}/download-draft/docx?token=${token}`;
      }

      const saved = await this.saveToDb(event);
      this.events$.next({ ...event, dbId: saved.id });

      if (event.targetEmail) {
        this.sendEmail(event).catch((err) =>
          this.logger.warn(
            `Email failed for ${event.inquiry_ref}: ${(err as Error).message}`,
          ),
        );
      }
    } catch (err: unknown) {
      this.logger.error(`Notification failed: ${(err as Error).message}`);
    }
  }

  private async saveToDb(
    event: InquiryNotificationEvent,
  ): Promise<InquiryNotificationEntity> {
    const repo = this.dataSource.getRepository(InquiryNotificationEntity);
    const notification = repo.create({
      inquiry_id: event.inquiry_id,
      inquiry_ref: event.inquiry_ref,
      company_name: event.company_name,
      type: event.type,
      message: event.message,
      target_user_id: event.targetUserId,
      old_status: event.old_status,
      new_status: event.new_status,
      pdf_url: event.pdf_url,
      docx_url: event.docx_url,
      is_read: false,
      is_resolved: false,
    });
    return await repo.save(notification);
  }

  getEventsForUser$(userId: number) {
    return this.events$.pipe(filter((event) => event.targetUserId === userId));
  }

  private async sendEmail(event: InquiryNotificationEvent): Promise<void> {
    const subject = this.getEmailSubject(event.type, event.inquiry_ref);
    const html = this.buildEmailHtml(event);

    // ✅ NEW — Build CC list (only for selected notification types)
    // Avoids CC'ing the targetEmail if it's already in the CC list
    const ccList = this.ccTypes.has(event.type)
      ? this.schemeCcEmails.filter(
        (cc) => cc.toLowerCase() !== event.targetEmail?.toLowerCase(),
      )
      : [];

    // 🆕 resolve recipient + send from actor's mailbox with fallback
    const mailTo = event.targetEmail;
      if (!mailTo) {
        this.logger.warn(`[INQ-MAIL] no targetEmail for user ${event.targetUserId} — email skipped`);
      } else if (event.actor_user_id) {
        try {
          await this.mailsService.sendAsUser(event.actor_user_id, {
            to: mailTo,
            cc: event.cc?.length ? event.cc : undefined,   // 🆕 line 1 of 3
            subject,
            html,
          });
        } catch (e: any) {
          this.logger.warn(`[INQ-MAIL] sendAsUser failed: ${e?.message} — falling back to default`);
          await this.transporter.sendMail({
            from: process.env.SMTP_FROM,
            to: mailTo,
            cc: event.cc?.length ? event.cc : undefined,   // 🆕 line 2 of 3
            subject,
            html,
          });
        }
      } else {
        await this.transporter.sendMail({
          from: process.env.SMTP_FROM,
          to: mailTo,
          cc: event.cc?.length ? event.cc : undefined,     // 🆕 line 3 of 3
          subject,
          html,
        });
      }

    // ✅ NEW — Better logging that shows CC info
    if (ccList.length > 0) {
      this.logger.log(
        `Email sent to ${event.targetEmail} (CC: ${ccList.join(', ')}) for ${event.inquiry_ref}`,
      );
    } else {
      this.logger.log(
        `Email sent to ${event.targetEmail} for ${event.inquiry_ref}`,
      );
    }
  }

  private getEmailSubject(type: string, ref: string): string {
    const subjects: Record<string, string> = {
      NEW_INQUIRY: `[QRS] New Inquiry Submitted - ${ref}`,
      IN_REVIEW: `[QRS] Your Inquiry ${ref} is now In Review`,
      DRAFT_READY: `[QRS] Draft Certificate Ready - ${ref}`,
      CHANGES_REQUESTED: `[QRS] Changes Requested - ${ref}`,
      CLIENT_CONFIRMED: `[QRS] Client Confirmed - ${ref}`,
      FINAL_ISSUED: `[QRS] Final Certificate Issued - ${ref}`,
    };
    return subjects[type] ?? `[QRS] Update on ${ref}`;
  }

  private buildEmailHtml(event: InquiryNotificationEvent): string {
    return buildEmailHtmlTemplate(event);
  }

  async getForUser(
    userId: number,
    options?: {
      page?: number;
      limit?: number;
      is_resolved?: boolean;
      type?: string;
    },
  ) {
    const page = options?.page ?? 1;
    const limit = options?.limit ?? 20;
    const skip = (page - 1) * limit;

    const query = this.dataSource
      .getRepository(InquiryNotificationEntity)
      .createQueryBuilder('n')
      .where('n.target_user_id = :userId', { userId })
      .orderBy('n.created_at', 'DESC')
      .skip(skip)
      .take(limit);

    if (options?.is_resolved !== undefined)
      query.andWhere('n.is_resolved = :r', { r: options.is_resolved });

    if (options?.type) query.andWhere('n.type = :type', { type: options.type });

    const [data, total] = await query.getManyAndCount();
    return {
      data,
      meta: { total, page, limit, totalPages: Math.ceil(total / limit) },
    };
  }

  async getUnreadCount(userId: number): Promise<number> {
    return this.dataSource.getRepository(InquiryNotificationEntity).count({
      where: { target_user_id: userId, is_read: false },
    });
  }

  async markAsRead(id: number, userId: number): Promise<void> {
    await this.dataSource
      .getRepository(InquiryNotificationEntity)
      .update({ id, target_user_id: userId }, { is_read: true });
  }
  // ✅ NEW — Mirror of markAsRead but flips is_read back to false
  async markAsUnread(id: number, userId: number): Promise<void> {
    await this.dataSource
      .getRepository(InquiryNotificationEntity)
      .update({ id, target_user_id: userId }, { is_read: false });
  }

  async markAllAsRead(userId: number): Promise<void> {
    await this.dataSource
      .getRepository(InquiryNotificationEntity)
      .update({ target_user_id: userId, is_read: false }, { is_read: true });
  }

  async markAsResolved(id: number, userId: number): Promise<void> {
    await this.dataSource
      .getRepository(InquiryNotificationEntity)
      .update(
        { id, target_user_id: userId },
        { is_resolved: true, is_read: true, resolved_at: new Date() },
      );
  }

  async markAllResolved(userId: number): Promise<void> {
    await this.dataSource
      .getRepository(InquiryNotificationEntity)
      .update(
        { target_user_id: userId, is_resolved: false },
        { is_resolved: true, is_read: true, resolved_at: new Date() },
      );
  }
}
