/**
 * Export Service for Redirect Intelligence v2
 * 
 * Generates Markdown and PDF reports with embedded Mermaid diagrams
 */

import fs from 'fs/promises';
import path from 'path';
import puppeteer from 'puppeteer';
import Handlebars from 'handlebars';
import MarkdownIt from 'markdown-it';
import mermaid from 'mermaid';
import { logger } from '../lib/logger';
import { prisma } from '../lib/prisma';

export interface ExportData {
  check: {
    id: string;
    inputUrl: string;
    finalUrl?: string;
    method: string;
    status: string;
    totalTimeMs: number;
    startedAt: Date;
    finishedAt: Date;
    redirectCount: number;
    loopDetected?: boolean;
    error?: string;
    hops: Array<{
      hopIndex: number;
      url: string;
      statusCode?: number;
      redirectType: string;
      latencyMs?: number;
      contentType?: string;
      reason?: string;
    }>;
  };
  analysis?: {
    ssl?: Array<{
      host: string;
      daysToExpiry?: number;
      issuer?: string;
      protocol?: string;
      warningsJson: string[];
    }>;
    seo?: {
      robotsTxtStatus: string;
      hasTitle?: boolean;
      hasDescription?: boolean;
      noindex: boolean;
      nofollow: boolean;
      sitemapPresent: boolean;
    };
    security?: {
      safeBrowsingStatus: string;
      mixedContent: string;
      httpsToHttp: boolean;
    };
  };
  organization?: {
    name: string;
    plan: string;
  };
  user?: {
    name: string;
    email: string;
  };
  generatedAt: Date;
}

export class ExportService {
  private markdownRenderer: MarkdownIt;

  constructor() {
    this.markdownRenderer = new MarkdownIt();
    this.initializeHandlebarsHelpers();
  }

  /**
   * Initialize Handlebars helpers for templating
   */
  private initializeHandlebarsHelpers(): void {
    // Format date helper
    Handlebars.registerHelper('formatDate', (date: Date) => {
      return new Intl.DateTimeFormat('en-US', {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        timeZoneName: 'short'
      }).format(date);
    });

    // Format duration helper
    Handlebars.registerHelper('formatDuration', (ms: number) => {
      if (ms < 1000) return `${ms}ms`;
      if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
      return `${(ms / 60000).toFixed(2)}m`;
    });

    // Format URL helper (truncate long URLs)
    Handlebars.registerHelper('formatUrl', (url: string, maxLength = 80) => {
      if (url.length <= maxLength) return url;
      return url.substring(0, maxLength - 3) + '...';
    });

    // Status badge helper
    Handlebars.registerHelper('statusBadge', (status: string) => {
      const badges = {
        'OK': '🟢 OK',
        'ERROR': '🔴 ERROR',
        'TIMEOUT': '🟡 TIMEOUT',
        'LOOP': '🟣 LOOP'
      };
      return badges[status as keyof typeof badges] || `⚪ ${status}`;
    });

    // Redirect type helper
    Handlebars.registerHelper('redirectType', (type: string) => {
      return type.replace('HTTP_', '').replace('_', ' ');
    });

    // Conditional helper
    Handlebars.registerHelper('if_eq', function(a, b, options) {
      if (a === b) {
        return options.fn(this);
      }
      return options.inverse(this);
    });
  }

  /**
   * Generate Mermaid diagram for redirect chain
   */
  private generateMermaidDiagram(hops: ExportData['check']['hops']): string {
    if (hops.length === 0) {
      return 'graph TD\n    A[No redirects found]';
    }

    let diagram = 'graph TD\n';
    
    for (let i = 0; i < hops.length; i++) {
      const hop = hops[i];
      const nodeId = `H${i}`;
      const nextNodeId = `H${i + 1}`;
      
      // Create node
      const statusCode = hop.statusCode ? ` (${hop.statusCode})` : '';
      const domain = new URL(hop.url).hostname;
      diagram += `    ${nodeId}["${domain}${statusCode}"]\n`;
      
      // Create edge to next node if not the last hop
      if (i < hops.length - 1) {
        const redirectType = hop.redirectType.replace('HTTP_', '').replace('_', ' ');
        diagram += `    ${nodeId} -->|"${redirectType}"| ${nextNodeId}\n`;
      }
      
      // Add styling based on status
      if (hop.statusCode) {
        if (hop.statusCode >= 300 && hop.statusCode < 400) {
          diagram += `    ${nodeId} --> ${nodeId}\n`;
          diagram += `    class ${nodeId} redirect\n`;
        } else if (hop.statusCode >= 400) {
          diagram += `    class ${nodeId} error\n`;
        } else {
          diagram += `    class ${nodeId} success\n`;
        }
      }
    }
    
    // Add styling
    diagram += `
    classDef redirect fill:#ffd700,stroke:#333,stroke-width:2px
    classDef error fill:#ff6b6b,stroke:#333,stroke-width:2px  
    classDef success fill:#51cf66,stroke:#333,stroke-width:2px
    `;
    
    return diagram;
  }

  /**
   * Get check data with analysis for export
   */
  async getExportData(checkId: string, userId?: string): Promise<ExportData> {
    const check = await prisma.check.findUnique({
      where: { id: checkId },
      include: {
        hops: {
          orderBy: { hopIndex: 'asc' }
        },
        sslInspections: true,
        seoFlags: true,
        securityFlags: true,
        project: {
          include: {
            organization: {
              include: {
                memberships: {
                  include: {
                    user: true
                  },
                  where: userId ? { userId } : undefined,
                  take: 1
                }
              }
            }
          }
        }
      }
    });

    if (!check) {
      throw new Error('Check not found');
    }

    // TODO: Add proper permission checking
    
    const exportData: ExportData = {
      check: {
        id: check.id,
        inputUrl: check.inputUrl,
        finalUrl: check.finalUrl || undefined,
        method: check.method,
        status: check.status,
        totalTimeMs: check.totalTimeMs || 0,
        startedAt: check.startedAt,
        finishedAt: check.finishedAt || check.startedAt,
        redirectCount: check.hops.length - 1,
        loopDetected: this.detectLoop(check.hops),
        error: undefined, // TODO: Extract from check data
        hops: check.hops.map(hop => ({
          hopIndex: hop.hopIndex,
          url: hop.url,
          statusCode: hop.statusCode || undefined,
          redirectType: hop.redirectType,
          latencyMs: hop.latencyMs || undefined,
          contentType: hop.contentType || undefined,
          reason: hop.reason || undefined,
        }))
      },
      analysis: {
        ssl: check.sslInspections.map(ssl => ({
          host: ssl.host,
          daysToExpiry: ssl.daysToExpiry || undefined,
          issuer: ssl.issuer || undefined,
          protocol: ssl.protocol || undefined,
          warningsJson: Array.isArray(ssl.warningsJson) ? ssl.warningsJson as string[] : [],
        })),
        seo: check.seoFlags ? {
          robotsTxtStatus: check.seoFlags.robotsTxtStatus,
          noindex: check.seoFlags.noindex,
          nofollow: check.seoFlags.nofollow,
          sitemapPresent: check.seoFlags.sitemapPresent,
        } : undefined,
        security: check.securityFlags ? {
          safeBrowsingStatus: check.securityFlags.safeBrowsingStatus,
          mixedContent: check.securityFlags.mixedContent,
          httpsToHttp: check.securityFlags.httpsToHttp,
        } : undefined,
      },
      organization: {
        name: check.project.organization.name,
        plan: check.project.organization.plan,
      },
      user: check.project.organization.memberships[0]?.user ? {
        name: check.project.organization.memberships[0].user.name,
        email: check.project.organization.memberships[0].user.email,
      } : undefined,
      generatedAt: new Date(),
    };

    return exportData;
  }

  /**
   * Simple loop detection for export data
   */
  private detectLoop(hops: any[]): boolean {
    const urls = new Set();
    for (const hop of hops) {
      if (urls.has(hop.url)) {
        return true;
      }
      urls.add(hop.url);
    }
    return false;
  }

  /**
   * Generate Markdown report
   */
  async generateMarkdownReport(checkId: string, userId?: string): Promise<string> {
    try {
      logger.info(`Generating Markdown report for check: ${checkId}`);
      
      const data = await this.getExportData(checkId, userId);
      const template = await this.loadTemplate('markdown-report.hbs');
      const compiledTemplate = Handlebars.compile(template);
      
      // Add Mermaid diagram to data
      const mermaidDiagram = this.generateMermaidDiagram(data.check.hops);
      const templateData = { ...data, mermaidDiagram };
      
      const markdown = compiledTemplate(templateData);
      
      logger.info(`Markdown report generated successfully for check: ${checkId}`);
      return markdown;
      
    } catch (error) {
      logger.error(`Failed to generate Markdown report for check ${checkId}:`, error);
      throw new Error(`Failed to generate Markdown report: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  /**
   * Generate PDF report
   */
  async generatePdfReport(checkId: string, userId?: string): Promise<Buffer> {
    let browser: puppeteer.Browser | null = null;
    
    try {
      logger.info(`Generating PDF report for check: ${checkId}`);
      
      const data = await this.getExportData(checkId, userId);
      
      // Generate HTML template for PDF
      const htmlTemplate = await this.loadTemplate('pdf-report.hbs');
      const compiledTemplate = Handlebars.compile(htmlTemplate);
      
      // Add Mermaid diagram to data
      const mermaidDiagram = this.generateMermaidDiagram(data.check.hops);
      const templateData = { ...data, mermaidDiagram };
      
      const html = compiledTemplate(templateData);
      
      // Launch browser
      browser = await puppeteer.launch({
        headless: true,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-accelerated-2d-canvas',
          '--no-first-run',
          '--no-zygote',
          '--single-process',
          '--disable-gpu'
        ]
      });
      
      const page = await browser.newPage();
      
      // Set content and wait for network idle
      await page.setContent(html, { waitUntil: 'networkidle0' });
      
      // Wait for Mermaid to render
      await page.waitForFunction(
        () => document.querySelector('.mermaid svg') !== null,
        { timeout: 10000 }
      ).catch(() => {
        logger.warn('Mermaid diagram may not have rendered completely');
      });
      
      // Generate PDF
      const pdf = await page.pdf({
        format: 'A4',
        printBackground: true,
        margin: {
          top: '20mm',
          right: '15mm',
          bottom: '20mm',
          left: '15mm'
        }
      });
      
      await browser.close();
      browser = null;
      
      logger.info(`PDF report generated successfully for check: ${checkId}`);
      return pdf;
      
    } catch (error) {
      if (browser) {
        await browser.close().catch(() => {});
      }
      
      logger.error(`Failed to generate PDF report for check ${checkId}:`, error);
      throw new Error(`Failed to generate PDF report: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  /**
   * Load template file
   */
  private async loadTemplate(templateName: string): Promise<string> {
    const templatePath = path.join(__dirname, '../templates', templateName);
    
    try {
      return await fs.readFile(templatePath, 'utf-8');
    } catch (error) {
      logger.error(`Failed to load template: ${templateName}`, error);
      throw new Error(`Template not found: ${templateName}`);
    }
  }

  /**
   * Save report to file system
   */
  async saveReport(content: Buffer | string, checkId: string, format: 'md' | 'pdf'): Promise<string> {
    try {
      const reportsDir = path.join(process.cwd(), 'reports');
      await fs.mkdir(reportsDir, { recursive: true });
      
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const fileName = `redirect-report-${checkId}-${timestamp}.${format}`;
      const filePath = path.join(reportsDir, fileName);
      
      if (typeof content === 'string') {
        await fs.writeFile(filePath, content, 'utf-8');
      } else {
        await fs.writeFile(filePath, content);
      }
      
      logger.info(`Report saved: ${filePath}`);
      return filePath;
      
    } catch (error) {
      logger.error(`Failed to save report:`, error);
      throw new Error(`Failed to save report: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  /**
   * Clean up old report files
   */
  async cleanupOldReports(maxAgeHours = 24): Promise<void> {
    try {
      const reportsDir = path.join(process.cwd(), 'reports');
      const files = await fs.readdir(reportsDir);
      const cutoff = Date.now() - (maxAgeHours * 60 * 60 * 1000);
      
      for (const file of files) {
        const filePath = path.join(reportsDir, file);
        const stats = await fs.stat(filePath);
        
        if (stats.mtime.getTime() < cutoff) {
          await fs.unlink(filePath);
          logger.info(`Cleaned up old report: ${file}`);
        }
      }
      
    } catch (error) {
      logger.warn('Failed to cleanup old reports:', error);
    }
  }
}
