import { DeleteDTO } from '@hed-hog/api';
import { MailService as MailMainService } from '@hed-hog/api-mail';
import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
import { PrismaService } from '@hed-hog/api-prisma';
import {
    BadRequestException,
    Inject,
    Injectable,
    NotFoundException,
    forwardRef,
} from '@nestjs/common';
import { MailService } from '../mail/mail.service';
import { CreateDTO } from './dto/create.dto';
import { UpdateDTO } from './dto/update.dto';

@Injectable()
export class MailProfileService {
  constructor(
    @Inject(forwardRef(() => PrismaService))
    private readonly prismaService: PrismaService,
    @Inject(forwardRef(() => PaginationService))
    private readonly paginationService: PaginationService,
    @Inject(forwardRef(() => MailMainService))
    private readonly mailMainService: MailMainService,
    @Inject(forwardRef(() => MailService))
    private readonly mailService: MailService,
  ) {}

  async list(paginationParams: PaginationDTO) {
    const fields = ['slug', 'name', 'from_email', 'reply_to_email'];
    const OR = this.prismaService.createInsensitiveSearch(fields, paginationParams);

    return this.paginationService.paginate(
      this.prismaService.mail_profile,
      paginationParams,
      {
        where: OR.length > 0 ? { OR } : {},
        select: {
          id: true,
          slug: true,
          name: true,
          provider: true,
          from_name: true,
          from_email: true,
          reply_to_name: true,
          reply_to_email: true,
          created_at: true,
          updated_at: true,
        },
        orderBy: [{ updated_at: 'desc' }, { id: 'desc' }],
      },
    );
  }

  async get(id: number) {
    const profile = await this.prismaService.mail_profile.findUnique({
      where: { id },
    });

    if (!profile) {
      throw new NotFoundException(`Mail profile with id ${id} not found.`);
    }

    return profile;
  }

  async create(data: CreateDTO) {
    await this.ensureSlugIsAvailable(data.slug);
    this.validateProviderFields(data as unknown as Record<string, unknown>);

    return this.prismaService.mail_profile.create({
      data: this.normalizeData(data as unknown as Record<string, unknown>) as any,
    });
  }

  async update({ id, data }: { id: number; data: UpdateDTO }) {
    const existing = await this.prismaService.mail_profile.findUnique({
      where: { id },
    });

    if (!existing) {
      throw new NotFoundException(`Mail profile with id ${id} not found.`);
    }

    if (data.slug && data.slug !== existing.slug) {
      await this.ensureSlugIsAvailable(data.slug);
    }

    const merged = {
      ...existing,
      ...data,
    } satisfies Record<string, unknown>;

    this.validateProviderFields(merged);

    return this.prismaService.mail_profile.update({
      where: { id },
      data: this.normalizeData(merged) as any,
    });
  }

  async delete({ ids }: DeleteDTO): Promise<{ count: number }> {
    if (!ids?.length) {
      throw new BadRequestException('You must select at least one item to delete.');
    }

    return this.prismaService.mail_profile.deleteMany({
      where: {
        id: {
          in: ids,
        },
      },
    });
  }

  async test(data: CreateDTO) {
    this.validateProviderFields(data as unknown as Record<string, unknown>);

    const normalized = this.normalizeData(data as unknown as Record<string, unknown>);
    const config = this.buildMailConfig(normalized);
    const destinationEmail = normalized.from_email;
    const sender = normalized.from_name
      ? `${normalized.from_name} <${normalized.from_email}>`
      : normalized.from_email;
    const replyTo = normalized.reply_to_email
      ? normalized.reply_to_name
        ? `${normalized.reply_to_name} <${normalized.reply_to_email}>`
        : normalized.reply_to_email
      : undefined;

    try {
      this.mailMainService.setConfig(config);

      await this.mailMainService.send({
        to: destinationEmail,
        from: sender,
        replyTo,
        subject: `[HedHog] Teste do perfil ${normalized.name}`,
        body: `
          <p>Este e-mail confirma que o perfil <strong>${normalized.name}</strong> foi validado com sucesso.</p>
          <p><strong>Slug:</strong> ${normalized.slug}</p>
          <p><strong>Provedor:</strong> ${normalized.provider}</p>
          <p><strong>Remetente:</strong> ${sender}</p>
        `,
      });

      return {
        success: true,
        destination: destinationEmail,
      };
    } finally {
      await this.mailService.reloadConfig().catch(() => undefined);
    }
  }

  private async ensureSlugIsAvailable(slug: string) {
    const existing = await this.prismaService.mail_profile.findUnique({
      where: { slug },
      select: { id: true },
    });

    if (existing) {
      throw new BadRequestException(`Mail profile with slug "${slug}" already exists.`);
    }
  }

  private validateProviderFields(input: Record<string, unknown>) {
    const provider = String(input.provider ?? '').toUpperCase();

    if (!provider) {
      throw new BadRequestException('Provider is required.');
    }

    if (!String(input.from_email ?? '').trim()) {
      throw new BadRequestException('Sender email is required.');
    }

    switch (provider) {
      case 'SMTP':
        if (!String(input.smtp_host ?? '').trim()) {
          throw new BadRequestException('SMTP host is required.');
        }
        if (!input.smtp_port) {
          throw new BadRequestException('SMTP port is required.');
        }
        if (!String(input.smtp_username ?? '').trim()) {
          throw new BadRequestException('SMTP username is required.');
        }
        if (!String(input.smtp_password ?? '').trim()) {
          throw new BadRequestException('SMTP password is required.');
        }
        break;
      case 'GMAIL':
        if (!String(input.gmail_client_id ?? '').trim()) {
          throw new BadRequestException('Gmail client ID is required.');
        }
        if (!String(input.gmail_client_secret ?? '').trim()) {
          throw new BadRequestException('Gmail client secret is required.');
        }
        if (!String(input.gmail_refresh_token ?? '').trim()) {
          throw new BadRequestException('Gmail refresh token is required.');
        }
        break;
      case 'SES':
        if (!String(input.ses_access_key_id ?? '').trim()) {
          throw new BadRequestException('SES access key ID is required.');
        }
        if (!String(input.ses_secret_access_key ?? '').trim()) {
          throw new BadRequestException('SES secret access key is required.');
        }
        if (!String(input.ses_region ?? '').trim()) {
          throw new BadRequestException('SES region is required.');
        }
        break;
      default:
        throw new BadRequestException(`Invalid provider: ${provider}`);
    }
  }

  private normalizeData(input: Record<string, unknown>) {
    const provider = String(input.provider ?? '').toUpperCase();

    return {
      slug: this.normalizeString(input.slug),
      name: this.normalizeString(input.name),
      provider,
      from_name: this.normalizeNullableString(input.from_name),
      from_email: this.normalizeString(input.from_email),
      reply_to_name: this.normalizeNullableString(input.reply_to_name),
      reply_to_email: this.normalizeNullableString(input.reply_to_email),
      smtp_host: provider === 'SMTP' ? this.normalizeNullableString(input.smtp_host) : null,
      smtp_port:
        provider === 'SMTP' && input.smtp_port !== undefined && input.smtp_port !== null
          ? Number(input.smtp_port)
          : null,
      smtp_secure: provider === 'SMTP' ? Boolean(input.smtp_secure) : false,
      smtp_username:
        provider === 'SMTP' ? this.normalizeNullableString(input.smtp_username) : null,
      smtp_password:
        provider === 'SMTP' ? this.normalizeNullableString(input.smtp_password) : null,
      gmail_client_id:
        provider === 'GMAIL' ? this.normalizeNullableString(input.gmail_client_id) : null,
      gmail_client_secret:
        provider === 'GMAIL'
          ? this.normalizeNullableString(input.gmail_client_secret)
          : null,
      gmail_refresh_token:
        provider === 'GMAIL'
          ? this.normalizeNullableString(input.gmail_refresh_token)
          : null,
      ses_access_key_id:
        provider === 'SES' ? this.normalizeNullableString(input.ses_access_key_id) : null,
      ses_secret_access_key:
        provider === 'SES'
          ? this.normalizeNullableString(input.ses_secret_access_key)
          : null,
      ses_region: provider === 'SES' ? this.normalizeNullableString(input.ses_region) : null,
    };
  }

  private buildMailConfig(input: ReturnType<MailProfileService['normalizeData']>) {
    switch (input.provider) {
      case 'SMTP':
        return {
          global: true,
          type: 'SMTP' as const,
          host: input.smtp_host ?? '',
          port: input.smtp_port ?? 587,
          secure: input.smtp_secure,
          username: input.smtp_username ?? '',
          password: input.smtp_password ?? '',
        };
      case 'GMAIL':
        return {
          global: true,
          type: 'GMAIL' as const,
          clientId: input.gmail_client_id ?? '',
          clientSecret: input.gmail_client_secret ?? '',
          refreshToken: input.gmail_refresh_token ?? '',
          from: input.from_email,
        };
      case 'SES':
        return {
          global: true,
          type: 'SES' as const,
          region: input.ses_region ?? '',
          accessKeyId: input.ses_access_key_id ?? '',
          secretAccessKey: input.ses_secret_access_key ?? '',
          from: input.from_email,
        };
      default:
        throw new BadRequestException(`Invalid provider: ${input.provider}`);
    }
  }

  private normalizeString(value: unknown) {
    return String(value ?? '').trim();
  }

  private normalizeNullableString(value: unknown) {
    if (value === undefined || value === null) {
      return null;
    }

    const normalized = String(value).trim();
    return normalized.length > 0 ? normalized : null;
  }
}
