import db from '../db';
import { Monitor, Snapshot } from '../types';
import { fetchPage } from './fetcher';
import {
  applyIgnoreRules,
  applyCommonNoiseFilters,
  compareDiffs,
  checkKeywords,
} from './differ';
import { calculateChangeImportance } from './importance';
import { sendChangeAlert, sendErrorAlert, sendKeywordAlert } from './alerter';
import { generateSimpleSummary, generateAISummary } from './summarizer';

export interface CheckResult {
  snapshot: Snapshot;
  alertSent: boolean;
}

export async function checkMonitor(monitorId: string): Promise<CheckResult | void> {
  console.log(`[Monitor] Checking monitor ${monitorId}`);

  try {
    const monitor = await db.monitors.findById(monitorId);

    if (!monitor) {
      console.error(`[Monitor] Monitor ${monitorId} not found`);
      return;
    }

    if (monitor.status !== 'active' && monitor.status !== 'error') {
      console.log(`[Monitor] Monitor ${monitorId} is not active or error, skipping (status: ${monitor.status})`);
      return;
    }

    // Fetch page with retries
    let fetchResult = await fetchPage(monitor.url, monitor.elementSelector);

    // Retry on failure (max 3 attempts)
    if (fetchResult.error) {
      console.log(`[Monitor] Fetch failed, retrying... (1/3)`);
      await new Promise((resolve) => setTimeout(resolve, 2000));
      fetchResult = await fetchPage(monitor.url, monitor.elementSelector);

      if (fetchResult.error) {
        console.log(`[Monitor] Fetch failed, retrying... (2/3)`);
        await new Promise((resolve) => setTimeout(resolve, 2000));
        fetchResult = await fetchPage(monitor.url, monitor.elementSelector);
      }
    }


    // Check for HTTP error status
    if (!fetchResult.error && fetchResult.status >= 400) {
      fetchResult.error = `HTTP ${fetchResult.status}`;
    }

    // If still failing after retries
    if (fetchResult.error) {
      console.error(`[Monitor] Failed to fetch ${monitor.url}: ${fetchResult.error}`);

      // Create error snapshot
      const failedSnapshot = await db.snapshots.create({
        monitorId: monitor.id,
        htmlContent: '',
        textContent: '',
        contentHash: '',
        httpStatus: fetchResult.status,
        responseTime: fetchResult.responseTime,
        changed: false,
        errorMessage: fetchResult.error,
      });

      await db.monitors.incrementErrors(monitor.id);

      // Send error alert if consecutive errors > 3
      if (monitor.consecutiveErrors >= 2) {
        const user = await db.users.findById(monitor.userId);
        if (user) {
          await sendErrorAlert(monitor, user, fetchResult.error);
        }
      }


      return { snapshot: failedSnapshot, alertSent: false };
    }

    // Apply noise filters
    console.log(`[Monitor] Ignore rules for ${monitor.name}:`, JSON.stringify(monitor.ignoreRules));
    let processedHtml = applyCommonNoiseFilters(fetchResult.html);
    processedHtml = applyIgnoreRules(processedHtml, monitor.ignoreRules);

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

    let changed = false;
    let changePercentage = 0;
    let diffResult: ReturnType<typeof compareDiffs> | undefined;

    if (previousSnapshot) {
      // Apply same filters to previous content for fair comparison
      let previousHtml = applyCommonNoiseFilters(previousSnapshot.htmlContent);
      previousHtml = applyIgnoreRules(previousHtml, monitor.ignoreRules);

      // Compare
      diffResult = compareDiffs(previousHtml, processedHtml);
      changed = diffResult.changed;
      changePercentage = diffResult.changePercentage;

      console.log(
        `[Monitor] ${monitor.name}: Changed=${changed}, Change%=${changePercentage.toFixed(2)}, Additions=${diffResult.additions}, Deletions=${diffResult.deletions}`
      );

      // Check keywords
      if (monitor.keywordRules && monitor.keywordRules.length > 0) {
        const keywordMatches = checkKeywords(
          previousHtml,
          processedHtml,
          monitor.keywordRules
        );

        if (keywordMatches.length > 0) {
          console.log(`[Monitor] Keyword matches found:`, keywordMatches);
          const user = await db.users.findById(monitor.userId);

          if (user) {
            for (const match of keywordMatches) {
              await sendKeywordAlert(monitor, user, match);
            }
          }
        }
      }
    } else {
      // First check - consider it as "changed" to create baseline
      changed = true;
      console.log(`[Monitor] First check for ${monitor.name}, creating baseline`);
    }

    // Generate human-readable summary (Hybrid approach)
    let summary: string | undefined;

    if (changed && previousSnapshot && diffResult) {
      // Hybrid logic: AI for changes (≥5%), simple for very small changes
      if (changePercentage >= 5) {
        console.log(`[Monitor] Change (${changePercentage}%), using AI summary`);
        try {
          summary = await generateAISummary(diffResult.diff, changePercentage);
        } catch (error) {
          console.error('[Monitor] AI summary failed, falling back to simple summary:', error);
          summary = generateSimpleSummary(
            diffResult.diff,
            previousSnapshot.htmlContent,
            fetchResult.html
          );
        }
      } else {
        console.log(`[Monitor] Small change (${changePercentage}%), using simple summary`);
        summary = generateSimpleSummary(
          diffResult.diff,
          previousSnapshot.htmlContent,
          fetchResult.html
        );
      }
    }

    // Create snapshot
    const snapshot = await db.snapshots.create({
      monitorId: monitor.id,
      htmlContent: fetchResult.html,
      textContent: fetchResult.text,
      contentHash: fetchResult.hash,
      httpStatus: fetchResult.status,
      responseTime: fetchResult.responseTime,
      changed,
      changePercentage: changed ? changePercentage : undefined,
      importanceScore: changed ? await calculateChangeImportance(monitor.id, changePercentage, 0, processedHtml) : 0,
      summary,
    });

    // Update monitor
    await db.monitors.updateLastChecked(monitor.id, changed);

    // Send alert if changed and not first check
    if (changed && previousSnapshot) {
      try {
        const user = await db.users.findById(monitor.userId);
        if (user) {
          await sendChangeAlert(monitor, user, snapshot, changePercentage);
        }
      } catch (alertError) {
        console.error(`[Monitor] Failed to send alert for ${monitor.id}:`, alertError);
        // Continue execution - do not fail the check
      }
    }

    // Clean up old snapshots based on user plan retention period
    try {
      const retentionUser = await db.users.findById(monitor.userId);
      if (retentionUser) {
        const { getRetentionDays } = await import('../config');
        const retentionDays = getRetentionDays(retentionUser.plan);
        await db.snapshots.deleteOldSnapshotsByAge(monitor.id, retentionDays);
      }
    } catch (cleanupError) {
      console.error(`[Monitor] Failed to cleanup snapshots for ${monitor.id}:`, cleanupError);
    }

    console.log(`[Monitor] Check completed for ${monitor.name}`);
    return { snapshot, alertSent: changed && !!previousSnapshot };
  } catch (error) {
    console.error(`[Monitor] Error checking monitor ${monitorId}:`, error);
    await db.monitors.incrementErrors(monitorId);
  }
}

// Re-export scheduler functions for backward compatibility
export { scheduleMonitor, unscheduleMonitor, rescheduleMonitor } from './scheduler';
