import {
  BadGatewayException,
  BadRequestException,
  Injectable,
  InternalServerErrorException,
  Logger,
} from '@nestjs/common';
import { Resend } from 'resend';
import { basename, join } from 'path';
import { existsSync, readFileSync } from 'fs';
import sanitizeHtml from 'sanitize-html';

type EmailAttachment = {
  filename: string;
  content: string;
};

export type SendMeetingRequestMailParams = {
  to: string[];
  replyTo?: string | null;

  requestId: number;
  title: string;
  description?: string | null;

  ministryName: string;
  workingGroupName: string;
  workingGroupEmail?: string | null;

  meetingRequestLetter?: string | null;
  frontendUrl?: string;

  issues?: {
    title?: string | null;
    description?: string | null;
    recommendation?: string | null;

    issueStatus?: {
      name?: string | null;
    } | null;

    category?: {
      name?: string | null;
    } | null;
  }[];
};

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

  private readonly resend: Resend;
  private readonly fromEmail: string;
  private readonly frontendUrl: string;

  constructor() {
    const apiKey = process.env.RESEND_API_KEY?.trim();

    const fromEmail = process.env.RESEND_FROM_EMAIL?.trim();

    const frontendUrl =
      process.env.FRONTEND_URL?.trim() || 'http://localhost:3000';

    if (!apiKey) {
      throw new InternalServerErrorException(
        'RESEND_API_KEY is missing in environment variables.',
      );
    }

    if (!apiKey.startsWith('re_')) {
      throw new InternalServerErrorException(
        'RESEND_API_KEY format is invalid.',
      );
    }

    if (!fromEmail) {
      throw new InternalServerErrorException(
        'RESEND_FROM_EMAIL is missing in environment variables.',
      );
    }

    this.resend = new Resend(apiKey);
    this.fromEmail = fromEmail;
    this.frontendUrl = frontendUrl.replace(/\/+$/, '');
  }

  async sendMeetingRequestToMinistry(
    params: SendMeetingRequestMailParams,
  ): Promise<string | null> {
    const recipients = Array.from(
      new Set(
        params.to
          .map((email) => email.trim().toLowerCase())
          .filter((email) => this.isValidEmail(email)),
      ),
    );

    if (recipients.length === 0) {
      throw new BadRequestException('Ministry email recipient is empty.');
    }

    const replyTo = this.isValidEmail(params.replyTo)
      ? params.replyTo.trim().toLowerCase()
      : undefined;

    const frontendUrl = (params.frontendUrl || this.frontendUrl).replace(
      /\/+$/,
      '',
    );

    const requestUrl = `${frontendUrl}/ministry/meeting-requests/${params.requestId}`;

    const attachments = this.buildAttachments(params.meetingRequestLetter);

    const subject = `New Meeting Request #${params.requestId}: ${params.title}`;

    this.logger.log(
      `Sending meeting request email to: ${recipients.join(', ')}`,
    );

    this.logger.log(`Email sender: ${this.fromEmail}`);

    try {
      const { data, error } = await this.resend.emails.send({
        from: this.fromEmail,
        to: recipients,
        subject,
        replyTo,
        html: this.buildHtml({
          ...params,
          requestUrl,
          replyTo,
        }),
        text: this.buildText({
          ...params,
          requestUrl,
          replyTo,
        }),
        attachments: attachments.length > 0 ? attachments : undefined,
      });

      if (error) {
        this.logger.error(`Resend meeting request error: ${error.message}`);

        throw new BadGatewayException({
          message: 'Failed to send meeting request email.',
          resendError: error.message,
        });
      }

      const resendId = data?.id ? String(data.id) : null;

      this.logger.log(
        `Meeting request email sent successfully. Resend ID: ${resendId ?? '-'}`,
      );

      return resendId;
    } catch (error) {
      if (error instanceof BadGatewayException) {
        throw error;
      }

      this.logger.error(
        'Unexpected Resend error while sending meeting request.',
        error instanceof Error ? error.stack : String(error),
      );

      throw new BadGatewayException({
        message: 'Unable to send meeting request email.',
      });
    }
  }

  private buildAttachments(filePath?: string | null): EmailAttachment[] {
    if (!filePath?.trim()) {
      return [];
    }

    const cleanPath = filePath.replace(/^\/+/, '');

    const fullPath = join(process.cwd(), cleanPath);

    if (!existsSync(fullPath)) {
      this.logger.warn(`Meeting request letter not found: ${fullPath}`);

      return [];
    }

    return [
      {
        filename: basename(fullPath),
        content: readFileSync(fullPath).toString('base64'),
      },
    ];
  }

  private buildHtml(
    params: SendMeetingRequestMailParams & {
      requestUrl: string;
      replyTo?: string;
    },
  ): string {
    const issueRows = params.issues?.length
      ? params.issues
          .map(
            (issue, index) => `
                <tr>
                  <td style="padding:8px;border:1px solid #ddd;">
                    ${index + 1}
                  </td>

                  <td style="padding:8px;border:1px solid #ddd;">
                    ${this.renderTextEditorHtml(issue.title || '-')}
                  </td>

                  <td style="padding:8px;border:1px solid #ddd;">
                    ${this.escapeHtml(issue.category?.name || '-')}
                  </td>

                  <td style="padding:8px;border:1px solid #ddd;">
                    ${this.escapeHtml(issue.issueStatus?.name || '-')}
                  </td>
                </tr>
              `,
          )
          .join('')
      : `
          <tr>
            <td
              colspan="4"
              style="padding:8px;border:1px solid #ddd;"
            >
              No issues attached.
            </td>
          </tr>
        `;

    return `
      <div style="
        font-family: Arial, sans-serif;
        line-height: 1.6;
        color: #111827;
      ">
        <h2 style="color:#1F6DB2;">
          New Meeting Request
        </h2>

        <p>
          Dear ${this.escapeHtml(params.ministryName)},
        </p>

        <p>
          A new meeting request has been submitted
          to your ministry.
        </p>

        <p>
          <strong>Request ID:</strong>
          #${params.requestId}
        </p>

        <p>
          <strong>Title:</strong>
          ${this.escapeHtml(params.title)}
        </p>

        <div style="margin:12px 0;">
          <strong>Description:</strong>

          <div style="margin-top:6px;">
            ${this.renderTextEditorHtml(params.description)}
          </div>
        </div>

        <p>
          <strong>From Working Group:</strong>
          ${this.escapeHtml(params.workingGroupName)}
        </p>

        ${
          params.replyTo
            ? `
              <p>
                <strong>Reply Email:</strong>
                ${this.escapeHtml(params.replyTo)}
              </p>
            `
            : ''
        }

        <h3>Issues</h3>

        <table style="
          border-collapse:collapse;
          width:100%;
        ">
          <thead>
            <tr>
              <th style="padding:8px;border:1px solid #ddd;text-align:left;">
                #
              </th>

              <th style="padding:8px;border:1px solid #ddd;text-align:left;">
                Title
              </th>

              <th style="padding:8px;border:1px solid #ddd;text-align:left;">
                Category
              </th>

              <th style="padding:8px;border:1px solid #ddd;text-align:left;">
                Status
              </th>
            </tr>
          </thead>

          <tbody>
            ${issueRows}
          </tbody>
        </table>

        <p style="margin-top:20px;">
          <a
            href="${this.escapeHtml(params.requestUrl)}"
            style="
              display:inline-block;
              padding:10px 16px;
              background:#1F6DB2;
              color:#fff;
              text-decoration:none;
              border-radius:6px;
            "
          >
            View Meeting Request
          </a>
        </p>

        <p>
          Best regards,<br/>
          G-PSF MIS
        </p>
      </div>
    `;
  }

  private buildText(
    params: SendMeetingRequestMailParams & {
      requestUrl: string;
      replyTo?: string;
    },
  ): string {
    const issueText =
      params.issues
        ?.map(
          (issue, index) =>
            `${index + 1}. ${this.stripHtml(issue.title || '-')} | ${
              issue.category?.name || '-'
            } | ${issue.issueStatus?.name || '-'}`,
        )
        .join('\n') || 'No issues attached.';

    return [
      'New Meeting Request',
      '',
      `Request ID: #${params.requestId}`,
      `Title: ${params.title}`,
      `Description: ${this.stripHtml(params.description)}`,
      `Ministry: ${params.ministryName}`,
      `From Working Group: ${params.workingGroupName}`,
      `Reply Email: ${params.replyTo || '-'}`,
      '',
      'Issues:',
      issueText,
      '',
      `View Meeting Request: ${params.requestUrl}`,
      '',
      'Best regards,',
      'G-PSF MIS',
    ].join('\n');
  }

  private renderTextEditorHtml(value?: string | null): string {
    if (!value?.trim()) {
      return '-';
    }

    return sanitizeHtml(value, {
      allowedTags: [
        'p',
        'br',
        'strong',
        'b',
        'em',
        'i',
        'u',
        's',
        'ul',
        'ol',
        'li',
        'span',
        'a',
        'h1',
        'h2',
        'h3',
        'blockquote',
        'pre',
        'code',
      ],
      allowedAttributes: {
        a: ['href', 'target', 'rel'],
        span: ['style'],
        p: ['style'],
        h1: ['style'],
        h2: ['style'],
        h3: ['style'],
        blockquote: ['style'],
      },
      allowedSchemes: ['http', 'https', 'mailto'],
      transformTags: {
        a: sanitizeHtml.simpleTransform('a', {
          target: '_blank',
          rel: 'noopener noreferrer',
        }),
      },
    });
  }

  private stripHtml(value?: string | null): string {
    if (!value?.trim()) {
      return '-';
    }

    return sanitizeHtml(value, {
      allowedTags: [],
      allowedAttributes: {},
    }).trim();
  }

  private isValidEmail(value?: string | null): value is string {
    return Boolean(value && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim()));
  }

  private escapeHtml(value?: string | null): string {
    return String(value || '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#039;');
  }
}
