import * as nodemailer from 'nodemailer';
import { Monitor, User, Snapshot, AlertChannel } from '../types';
import { KeywordMatch } from './differ';
import db from '../db';
import { APP_CONFIG, WEBHOOK_CONFIG, hasFeature } from '../config';

const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: parseInt(process.env.SMTP_PORT || '587'),
  secure: false,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

// ============================================
// Slack Integration
// ============================================

interface SlackMessage {
  title: string;
  text: string;
  url?: string;
  color?: 'good' | 'warning' | 'danger';
}

/**
 * Send a notification to a Slack webhook
 */
export async function sendSlackNotification(
  webhookUrl: string,
  message: SlackMessage,
  userId: string,
  monitorId?: string,
  alertId?: string
): Promise<boolean> {
  const payload = {
    attachments: [
      {
        color: message.color || '#007bff',
        title: message.title,
        title_link: message.url,
        text: message.text,
        footer: 'Website Monitor',
        ts: Math.floor(Date.now() / 1000),
      },
    ],
  };

  let attempt = 1;
  let lastError: string | undefined;

  while (attempt <= WEBHOOK_CONFIG.maxRetries) {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), WEBHOOK_CONFIG.timeoutMs);

      const response = await fetch(webhookUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
        signal: controller.signal,
      });

      clearTimeout(timeout);

      const responseBody = await response.text();

      // Log the attempt
      await db.webhookLogs.create({
        userId,
        monitorId,
        alertId,
        webhookType: 'slack',
        url: webhookUrl,
        payload,
        statusCode: response.status,
        responseBody: responseBody.substring(0, 1000),
        success: response.ok,
        errorMessage: response.ok ? undefined : `HTTP ${response.status}`,
        attempt,
      });

      if (response.ok) {
        console.log(`[Slack] Notification sent successfully`);
        return true;
      }

      lastError = `HTTP ${response.status}: ${responseBody}`;
    } catch (error: any) {
      lastError = error.message || 'Unknown error';

      // Log failed attempt
      await db.webhookLogs.create({
        userId,
        monitorId,
        alertId,
        webhookType: 'slack',
        url: webhookUrl,
        payload,
        success: false,
        errorMessage: lastError,
        attempt,
      });
    }

    if (attempt < WEBHOOK_CONFIG.maxRetries) {
      await new Promise(resolve => setTimeout(resolve, WEBHOOK_CONFIG.retryDelayMs * attempt));
    }
    attempt++;
  }

  console.error(`[Slack] Failed after ${WEBHOOK_CONFIG.maxRetries} attempts: ${lastError}`);
  return false;
}

// ============================================
// Webhook Integration
// ============================================

interface WebhookPayload {
  event: 'change' | 'error' | 'keyword';
  monitor: {
    id: string;
    name: string;
    url: string;
  };
  details: {
    changePercentage?: number;
    errorMessage?: string;
    keywordMatch?: KeywordMatch;
  };
  timestamp: string;
  viewUrl: string;
}

/**
 * Send a notification to a generic webhook
 */
export async function sendWebhookNotification(
  webhookUrl: string,
  payload: WebhookPayload,
  userId: string,
  monitorId?: string,
  alertId?: string
): Promise<boolean> {
  let attempt = 1;
  let lastError: string | undefined;

  while (attempt <= WEBHOOK_CONFIG.maxRetries) {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), WEBHOOK_CONFIG.timeoutMs);

      const response = await fetch(webhookUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'User-Agent': 'WebsiteMonitor/1.0',
          'X-Webhook-Event': payload.event,
        },
        body: JSON.stringify(payload),
        signal: controller.signal,
      });

      clearTimeout(timeout);

      let responseBody = '';
      try {
        responseBody = await response.text();
      } catch {
        // Ignore response body parsing errors
      }

      // Log the attempt
      await db.webhookLogs.create({
        userId,
        monitorId,
        alertId,
        webhookType: 'webhook',
        url: webhookUrl,
        payload: payload as any,
        statusCode: response.status,
        responseBody: responseBody.substring(0, 1000),
        success: response.ok,
        errorMessage: response.ok ? undefined : `HTTP ${response.status}`,
        attempt,
      });

      if (response.ok) {
        console.log(`[Webhook] Notification sent successfully`);
        return true;
      }

      lastError = `HTTP ${response.status}`;
    } catch (error: any) {
      lastError = error.message || 'Unknown error';

      // Log failed attempt
      await db.webhookLogs.create({
        userId,
        monitorId,
        alertId,
        webhookType: 'webhook',
        url: webhookUrl,
        payload: payload as any,
        success: false,
        errorMessage: lastError,
        attempt,
      });
    }

    if (attempt < WEBHOOK_CONFIG.maxRetries) {
      await new Promise(resolve => setTimeout(resolve, WEBHOOK_CONFIG.retryDelayMs * attempt));
    }
    attempt++;
  }

  console.error(`[Webhook] Failed after ${WEBHOOK_CONFIG.maxRetries} attempts: ${lastError}`);
  return false;
}

// ============================================
// Unified Alert Dispatcher
// ============================================

interface AlertData {
  title: string;
  summary: string;
  htmlContent: string;
  viewUrl: string;
  color?: 'good' | 'warning' | 'danger';
  changePercentage?: number;
  errorMessage?: string;
  keywordMatch?: KeywordMatch;
}

/**
 * Dispatch an alert to all configured channels for a user
 */
async function dispatchAlert(
  user: User,
  monitor: Monitor,
  snapshot: Snapshot | null,
  alertType: 'change' | 'error' | 'keyword',
  data: AlertData
): Promise<AlertChannel[]> {
  const usedChannels: AlertChannel[] = [];

  // Create alert record first
  let alertId: string | undefined;
  if (snapshot) {
    const alert = await db.alerts.create({
      monitorId: monitor.id,
      snapshotId: snapshot.id,
      userId: user.id,
      type: alertType,
      title: data.title,
      summary: data.summary,
      channels: ['email'], // Will be updated after dispatch
    });
    alertId = alert.id;
  }

  // 1. Email (always available)
  if (user.emailEnabled !== false) {
    try {
      await transporter.sendMail({
        from: APP_CONFIG.emailFrom,
        to: user.email,
        subject: data.title,
        html: data.htmlContent,
      });
      usedChannels.push('email');
      console.log(`[Alert] Email sent to ${user.email}`);
    } catch (error) {
      console.error(`[Alert] Failed to send email:`, error);
    }
  }

  // 2. Slack (PRO+ feature)
  if (user.slackEnabled && user.slackWebhookUrl && hasFeature(user.plan, 'slack_integration')) {
    const success = await sendSlackNotification(
      user.slackWebhookUrl,
      {
        title: data.title,
        text: data.summary,
        url: data.viewUrl,
        color: data.color || 'warning',
      },
      user.id,
      monitor.id,
      alertId
    );
    if (success) {
      usedChannels.push('slack');
    }
  }

  // 3. Webhook (PRO+ feature)
  if (user.webhookEnabled && user.webhookUrl && hasFeature(user.plan, 'webhook_integration')) {
    const webhookPayload: WebhookPayload = {
      event: alertType,
      monitor: {
        id: monitor.id,
        name: monitor.name,
        url: monitor.url,
      },
      details: {
        changePercentage: data.changePercentage,
        errorMessage: data.errorMessage,
        keywordMatch: data.keywordMatch,
      },
      timestamp: new Date().toISOString(),
      viewUrl: data.viewUrl,
    };

    const success = await sendWebhookNotification(
      user.webhookUrl,
      webhookPayload,
      user.id,
      monitor.id,
      alertId
    );
    if (success) {
      usedChannels.push('webhook');
    }
  }

  // Update alert with used channels
  if (alertId && usedChannels.length > 0) {
    await db.alerts.updateChannels(alertId, usedChannels);
    await db.alerts.markAsDelivered(alertId);
  }

  return usedChannels;
}

// ============================================
// Alert Functions (Public API)
// ============================================

export async function sendChangeAlert(
  monitor: Monitor,
  user: User,
  snapshot: Snapshot,
  changePercentage: number
): Promise<void> {
  const diffUrl = `${APP_CONFIG.appUrl}/monitors/${monitor.id}/history/${snapshot.id}`;

  const htmlContent = `
    <h2>Change Detected</h2>
    <p>A change was detected on your monitored page: <strong>${monitor.name}</strong></p>

    <div style="background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 20px 0;">
      <p><strong>URL:</strong> <a href="${monitor.url}">${monitor.url}</a></p>
      ${snapshot.summary ? `<p><strong>What Changed:</strong> ${snapshot.summary}</p>` : ''}
      <p><strong>Change Percentage:</strong> ${changePercentage.toFixed(2)}%</p>
      <p><strong>Detected At:</strong> ${new Date().toLocaleString()}</p>
    </div>

    <p>
      <a href="${diffUrl}" style="background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">
        View Changes
      </a>
    </p>

    <p style="color: #666; font-size: 12px; margin-top: 30px;">
      You're receiving this because you set up monitoring for this page.
      <a href="${APP_CONFIG.appUrl}/monitors/${monitor.id}">Manage this monitor</a>
    </p>
  `;

  await dispatchAlert(user, monitor, snapshot, 'change', {
    title: `Change detected: ${monitor.name}`,
    summary: snapshot.summary || `${changePercentage.toFixed(2)}% of the page changed`,
    htmlContent,
    viewUrl: diffUrl,
    color: changePercentage > 50 ? 'danger' : changePercentage > 10 ? 'warning' : 'good',
    changePercentage,
  });
}

export async function sendErrorAlert(
  monitor: Monitor,
  user: User,
  errorMessage: string
): Promise<void> {
  const monitorUrl = `${APP_CONFIG.appUrl}/monitors/${monitor.id}`;

  const htmlContent = `
    <h2>Monitoring Error</h2>
    <p>We encountered an error while monitoring: <strong>${monitor.name}</strong></p>

    <div style="background: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #ffc107;">
      <p><strong>URL:</strong> <a href="${monitor.url}">${monitor.url}</a></p>
      <p><strong>Error:</strong> ${errorMessage}</p>
      <p><strong>Time:</strong> ${new Date().toLocaleString()}</p>
    </div>

    <p>We'll keep trying to check this page. If the problem persists, you may want to verify the URL or check if the site is blocking automated requests.</p>

    <p>
      <a href="${monitorUrl}" style="background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">
        View Monitor Settings
      </a>
    </p>

    <p style="color: #666; font-size: 12px; margin-top: 30px;">
      <a href="${APP_CONFIG.appUrl}/monitors/${monitor.id}">Manage this monitor</a>
    </p>
  `;

  // Create snapshot for error (to track it)
  const snapshot = await db.snapshots.create({
    monitorId: monitor.id,
    htmlContent: '',
    textContent: '',
    contentHash: '',
    httpStatus: 0,
    responseTime: 0,
    changed: false,
    errorMessage,
  });

  await dispatchAlert(user, monitor, snapshot, 'error', {
    title: `Error monitoring: ${monitor.name}`,
    summary: errorMessage,
    htmlContent,
    viewUrl: monitorUrl,
    color: 'danger',
    errorMessage,
  });
}

export async function sendKeywordAlert(
  monitor: Monitor,
  user: User,
  match: KeywordMatch
): Promise<void> {
  const monitorUrl = `${APP_CONFIG.appUrl}/monitors/${monitor.id}`;

  let message = '';
  switch (match.type) {
    case 'appeared':
      message = `The keyword "${match.keyword}" appeared on the page`;
      break;
    case 'disappeared':
      message = `The keyword "${match.keyword}" disappeared from the page`;
      break;
    case 'count_changed':
      message = `The keyword "${match.keyword}" count changed from ${match.previousCount} to ${match.currentCount}`;
      break;
  }

  const htmlContent = `
    <h2>Keyword Alert</h2>
    <p>A keyword you're watching changed on: <strong>${monitor.name}</strong></p>

    <div style="background: #d1ecf1; padding: 15px; border-radius: 5px; margin: 20px 0; border-left: 4px solid #17a2b8;">
      <p><strong>URL:</strong> <a href="${monitor.url}">${monitor.url}</a></p>
      <p><strong>Alert:</strong> ${message}</p>
      <p><strong>Time:</strong> ${new Date().toLocaleString()}</p>
    </div>

    <p>
      <a href="${monitorUrl}" style="background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">
        View Monitor
      </a>
    </p>

    <p style="color: #666; font-size: 12px; margin-top: 30px;">
      <a href="${APP_CONFIG.appUrl}/monitors/${monitor.id}">Manage this monitor</a>
    </p>
  `;

  // Get latest snapshot
  const snapshot = await db.snapshots.findLatestByMonitorId(monitor.id);

  await dispatchAlert(user, monitor, snapshot, 'keyword', {
    title: `Keyword alert: ${monitor.name}`,
    summary: message,
    htmlContent,
    viewUrl: monitorUrl,
    color: match.type === 'appeared' ? 'good' : 'warning',
    keywordMatch: match,
  });
}

// ============================================
// Email-only Functions (Auth flows)
// ============================================

export async function sendPasswordResetEmail(
  email: string,
  resetUrl: string
): Promise<void> {
  try {
    const mailOptions = {
      from: APP_CONFIG.emailFrom,
      to: email,
      subject: 'Password Reset Request',
      html: `
        <h2>Password Reset Request</h2>
        <p>You requested to reset your password for your Website Monitor account.</p>

        <p>Click the button below to reset your password. This link will expire in 1 hour.</p>

        <p>
          <a href="${resetUrl}" style="background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; display: inline-block; margin: 20px 0;">
            Reset Password
          </a>
        </p>

        <p>Or copy and paste this link into your browser:</p>
        <p style="background: #f5f5f5; padding: 10px; border-radius: 5px; word-break: break-all;">
          ${resetUrl}
        </p>

        <p style="color: #666; margin-top: 30px;">
          If you didn't request a password reset, you can safely ignore this email.
          Your password will not be changed.
        </p>

        <p style="color: #999; font-size: 12px; margin-top: 30px;">
          This is an automated email. Please do not reply.
        </p>
      `,
    };

    await transporter.sendMail(mailOptions);
    console.log(`[Alert] Password reset email sent to ${email}`);
  } catch (error) {
    console.error('[Alert] Failed to send password reset email:', error);
    throw error;
  }
}

export async function sendEmailVerification(
  email: string,
  verificationUrl: string
): Promise<void> {
  try {
    const mailOptions = {
      from: APP_CONFIG.emailFrom,
      to: email,
      subject: 'Verify Your Email - Website Monitor',
      html: `
        <h2>Welcome to Website Monitor!</h2>
        <p>Thank you for signing up. Please verify your email address to activate your account.</p>

        <p>Click the button below to verify your email:</p>

        <p>
          <a href="${verificationUrl}" style="background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; display: inline-block; margin: 20px 0;">
            Verify Email Address
          </a>
        </p>

        <p>Or copy and paste this link into your browser:</p>
        <p style="background: #f5f5f5; padding: 10px; border-radius: 5px; word-break: break-all;">
          ${verificationUrl}
        </p>

        <p style="color: #666; margin-top: 30px;">
          This verification link will expire in 24 hours.
        </p>

        <p style="color: #999; font-size: 12px; margin-top: 30px;">
          If you didn't create an account, you can safely ignore this email.
        </p>
      `,
    };

    await transporter.sendMail(mailOptions);
    console.log(`[Alert] Verification email sent to ${email}`);
  } catch (error) {
    console.error('[Alert] Failed to send verification email:', error);
    throw error;
  }
}
