import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as sgMail from '@sendgrid/mail';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';

export interface NotificationChannel {
  send(recipient: string, subject: string, body: string, context?: Record<string, unknown>): Promise<boolean>;
}

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

  constructor(private readonly config: ConfigService) {
    const apiKey = this.config.get<string>('SENDGRID_API_KEY');
    if (apiKey) {
      sgMail.setApiKey(apiKey);
      this.logger.log('SendGrid Email adapter initialized');
    } else {
      this.logger.warn('SENDGRID_API_KEY not configured - emails will be logged only');
    }
  }

  async send(
    recipient: string,
    subject: string,
    body: string,
    context?: Record<string, unknown>,
  ): Promise<boolean> {
    const apiKey = this.config.get<string>('SENDGRID_API_KEY');
    const fromEmail = this.config.get<string>('SENDGRID_FROM_EMAIL') || 'noreply@edr-platform.com';

    if (!apiKey) {
      this.logger.log(`[EMAIL MOCK] To: ${recipient} | Subject: ${subject} | Body: ${body.substring(0, 100)}`);
      return true;
    }

    try {
      const msg: sgMail.MailDataRequired = {
        to: recipient,
        from: fromEmail,
        subject,
        text: body,
        html: this.formatHtml(body, context),
      };

      await sgMail.send(msg);
      this.logger.log(`Email sent successfully to ${recipient}`);
      return true;
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      this.logger.error(`Failed to send email to ${recipient}: ${message}`);
      return false;
    }
  }

  private formatHtml(body: string, context?: Record<string, unknown>): string {
    const contextHtml = context
      ? `<div style="margin-top: 20px; padding: 10px; background: #f5f5f5; border-radius: 4px;">
           <small>${JSON.stringify(context, null, 2)}</small>
         </div>`
      : '';

    return `
      <!DOCTYPE html>
      <html>
        <head>
          <meta charset="utf-8">
          <style>
            body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
            .container { max-width: 600px; margin: 0 auto; padding: 20px; }
            .header { background: #0066cc; color: white; padding: 20px; text-align: center; }
            .content { padding: 20px; background: white; }
            .footer { text-align: center; padding: 20px; color: #666; font-size: 12px; }
          </style>
        </head>
        <body>
          <div class="container">
            <div class="header">
              <h2>Ethio-Djibouti Railway</h2>
            </div>
            <div class="content">
              ${body.replace(/\n/g, '<br>')}
              ${contextHtml}
            </div>
            <div class="footer">
              <p>© 2024 Ethio-Djibouti Railway. All rights reserved.</p>
            </div>
          </div>
        </body>
      </html>
    `;
  }
}

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

  constructor(
    private readonly config: ConfigService,
    private readonly http: HttpService,
  ) {
    const provider = this.config.get<string>('SMS_PROVIDER');
    this.logger.log(`SMS adapter initialized with provider: ${provider || 'MOCK'}`);
  }

  async send(
    recipient: string,
    subject: string,
    body: string,
    _context?: Record<string, unknown>,
  ): Promise<boolean> {
    const provider = this.config.get<string>('SMS_PROVIDER');
    const apiKey = this.config.get<string>('SMS_API_KEY');

    if (!provider || !apiKey) {
      this.logger.log(`[SMS MOCK] To: ${recipient} | Message: ${body.substring(0, 100)}`);
      return true;
    }

    try {
      switch (provider.toLowerCase()) {
        case 'twilio':
          return await this.sendViaTwilio(recipient, body);
        case 'africastalking':
          return await this.sendViaAfricasTalking(recipient, body);
        default:
          this.logger.warn(`Unknown SMS provider: ${provider}`);
          return false;
      }
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      this.logger.error(`Failed to send SMS to ${recipient}: ${message}`);
      return false;
    }
  }

  private async sendViaTwilio(to: string, body: string): Promise<boolean> {
    const accountSid = this.config.get<string>('TWILIO_ACCOUNT_SID');
    const authToken = this.config.get<string>('TWILIO_AUTH_TOKEN');
    const fromNumber = this.config.get<string>('TWILIO_FROM_NUMBER');

    const url = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`;
    const auth = Buffer.from(`${accountSid}:${authToken}`).toString('base64');

    const response = await firstValueFrom(
      this.http.post(
        url,
        new URLSearchParams({
          To: to,
          From: fromNumber || '',
          Body: body,
        }),
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Authorization': `Basic ${auth}`,
          },
        },
      ),
    );

    return response.status === 201;
  }

  private async sendViaAfricasTalking(to: string, body: string): Promise<boolean> {
    const apiKey = this.config.get<string>('SMS_API_KEY');
    const username = this.config.get<string>('AFRICASTALKING_USERNAME');
    const from = this.config.get<string>('AFRICASTALKING_FROM');

    const url = 'https://api.africastalking.com/version1/messaging';

    const response = await firstValueFrom(
      this.http.post(
        url,
        new URLSearchParams({
          username: username || '',
          to,
          message: body,
          from: from || '',
        }),
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'apiKey': apiKey || '',
          },
        },
      ),
    );

    return response.status === 201;
  }
}

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

  constructor(private readonly config: ConfigService) {
    this.logger.log('Push notification adapter initialized');
  }

  async send(
    recipient: string,
    subject: string,
    body: string,
    context?: Record<string, unknown>,
  ): Promise<boolean> {
    // Push notifications would typically use FCM/APNS
    // For now, just log
    this.logger.log(`[PUSH MOCK] To: ${recipient} | Title: ${subject} | Body: ${body.substring(0, 100)}`);
    return true;
  }
}
