import {
  Injectable,
  Logger,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as nodemailer from 'nodemailer';

import { EmailSettingsEntity } from './email-settings.entity';
import { UpsertEmailSettingsDto } from './upsert-email-settings.dto';

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

  constructor(
    // ⚠️ same connection where the email_settings table lives (tqs / scheme_dbs )
    @InjectRepository(EmailSettingsEntity, 'scheme_dbs')
    private readonly repo: Repository<EmailSettingsEntity>,
  ) {}

  // ════════════════════════════════════════════════════════════════
  // INTERNAL — consumed by MailsService (returns RAW, unmasked row)
  // ════════════════════════════════════════════════════════════════

  /** The per-user sender row, or null if the user has none. */
  async getRawForUser(userId: number): Promise<EmailSettingsEntity | null> {
    if (!userId) return null;
    return this.repo.findOne({ where: { user_id: userId } });
  }

    // 👇 NEW — pick the mailbox for (user_id + scheme).
  //         Used when we need to send from a specific brand mailbox owned by a user.
  async getRawForUserAndScheme(
    userId: number,
    scheme: 'QRS' | 'TQS' | null,
  ): Promise<EmailSettingsEntity | null> {
    if (!userId) return null;
    if (scheme) {
      return this.repo.findOne({ where: { user_id: userId, scheme } });
    }
    return this.repo.findOne({ where: { user_id: userId } });
  }

  // 👇 NEW — find ANY user_id that owns a mailbox for the given brand.
  //         Used to route client-facing emails to the brand mailbox.
  async getRawByScheme(
    scheme: 'QRS' | 'TQS',
  ): Promise<EmailSettingsEntity | null> {
    if (!scheme) return null;
    return this.repo.findOne({ where: { scheme }, order: { id: 'ASC' } });
  }

  // ════════════════════════════════════════════════════════════════
  // CRUD — backs the management UI
  // ════════════════════════════════════════════════════════════════

  async list(): Promise<EmailSettingsEntity[]> {
    const rows = await this.repo.find({ order: { id: 'ASC' } });
    return rows.map((r) => this.mask(r));
  }

  async findOne(id: number): Promise<EmailSettingsEntity> {
    const row = await this.repo.findOne({ where: { id } });
    if (!row) throw new NotFoundException(`Email config ${id} not found`);
    return this.mask(row);
  }

  async create(dto: UpsertEmailSettingsDto): Promise<EmailSettingsEntity> {
    if (dto.user_id) {
      const existing = await this.repo.findOne({
        where: { user_id: dto.user_id },
      });
      if (existing) {
        throw new BadRequestException(
          `User ${dto.user_id} already has an email configuration (#${existing.id}).`,
        );
      }
    }
    const saved = await this.repo.save(this.repo.create(dto));
    this.logger.log(
      `[EMAIL-SETTINGS] Created #${saved.id} (${saved.from_email}) for user ${saved.user_id ?? 'generic'}`,
    );
    return this.mask(saved);
  }

  async update(
    id: number,
    dto: UpsertEmailSettingsDto,
  ): Promise<EmailSettingsEntity> {
    const row = await this.repo.findOne({ where: { id } });
    if (!row) throw new NotFoundException(`Email config ${id} not found`);

    const { smtp_password, ...rest } = dto;
    Object.assign(row, rest);
    // keep the stored password if the form didn't send a new one
    if (smtp_password) row.smtp_password = smtp_password;

    const saved = await this.repo.save(row);
    this.logger.log(`[EMAIL-SETTINGS] Updated #${id} (${saved.from_email})`);
    return this.mask(saved);
  }

  async remove(id: number): Promise<{ ok: true }> {
    const res = await this.repo.delete(id);
    if (!res.affected)
      throw new NotFoundException(`Email config ${id} not found`);
    this.logger.log(`[EMAIL-SETTINGS] Deleted #${id}`);
    return { ok: true };
  }

  /**
   * Verify credentials and send a probe email. Self-contained (throwaway
   * transporter) so this module has NO dependency on MailsService.
   */
  async sendTest(id: number, to: string): Promise<{ ok: true }> {
    const cfg = await this.repo.findOne({ where: { id } });
    if (!cfg) throw new NotFoundException(`Email config ${id} not found`);

    const transporter = this.buildTransporter(cfg);
    const name = (cfg.from_name || '').trim();
    const from = name ? `"${name}" <${cfg.from_email}>` : cfg.from_email;

    try {
      await transporter.verify(); // SMTP handshake + auth check
      await transporter.sendMail({
        from,
        to,
        subject: `Test email from ${cfg.from_email}`,
        text: `This sender (${cfg.from_email}) is configured correctly.`,
      });
    } catch (err: any) {
      throw new BadRequestException(`SMTP test failed: ${err.message}`);
    } finally {
      transporter.close();
    }
    this.logger.log(`[EMAIL-SETTINGS] Test email sent from #${id} to ${to}`);
    return { ok: true };
  }

  // ════════════════════════════════════════════════════════════════
  // HELPERS
  // ════════════════════════════════════════════════════════════════

  private buildTransporter(cfg: EmailSettingsEntity): nodemailer.Transporter {
    const port = Number(cfg.smtp_port);
    const enc = (cfg.smtp_encryption || '').toLowerCase();
    const secure = port === 465 || enc === 'ssl'; // 465=SSL, 587=STARTTLS
    return nodemailer.createTransport({
      host: cfg.smtp_host,
      port,
      secure,
      auth: { user: cfg.smtp_username, pass: cfg.smtp_password },
      ...(secure ? {} : { requireTLS: true }),
    });
  }

  /** never return the real password to the client */
  private mask(row: EmailSettingsEntity): EmailSettingsEntity {
    return { ...row, smtp_password: row.smtp_password ? '••••••••' : '' };
  }
}
