/**
 * Redirect Tracker Service for Worker
 * 
 * Simplified version for background processing
 */

import axios from 'axios';
import { z } from 'zod';
import { logger } from '../lib/logger';
import { prisma } from '../lib/prisma';

const TrackRequest = z.object({
  url: z.string().url(),
  method: z.enum(['GET', 'POST', 'HEAD']).default('GET'),
  userAgent: z.string().optional(),
  headers: z.record(z.string()).optional(),
  maxHops: z.number().min(1).max(20).default(10),
  timeout: z.number().min(1000).max(30000).default(15000),
  enableSSLAnalysis: z.boolean().default(true),
  enableSEOAnalysis: z.boolean().default(true),
  enableSecurityAnalysis: z.boolean().default(true),
  projectId: z.string().optional(),
});

type TrackRequest = z.infer<typeof TrackRequest>;

export interface TrackResult {
  check: {
    id: string;
    inputUrl: string;
    method: string;
    status: string;
    totalTimeMs: number;
    redirectCount: number;
    finalUrl?: string;
  };
}

export class RedirectTrackerService {
  
  async trackUrl(request: TrackRequest, userId: string): Promise<TrackResult> {
    const validatedRequest = TrackRequest.parse(request);
    const startTime = Date.now();
    
    try {
      // Create check record
      const check = await prisma.check.create({
        data: {
          inputUrl: validatedRequest.url,
          method: validatedRequest.method,
          status: 'OK',
          startedAt: new Date(),
          projectId: validatedRequest.projectId,
        },
      });

      // Perform redirect tracking
      const hops = await this.followRedirects(validatedRequest);
      
      const totalTime = Date.now() - startTime;
      const finalUrl = hops.length > 0 ? hops[hops.length - 1].url : validatedRequest.url;
      
      // Update check with results
      await prisma.check.update({
        where: { id: check.id },
        data: {
          status: 'OK',
          finishedAt: new Date(),
          totalTimeMs: totalTime,
          finalUrl,
        },
      });

      // Save hops
      if (hops.length > 0) {
        await prisma.hop.createMany({
          data: hops.map((hop, index) => ({
            checkId: check.id,
            hopIndex: index,
            url: hop.url,
            statusCode: hop.statusCode,
            redirectType: hop.redirectType,
            latencyMs: hop.latencyMs,
            contentType: hop.contentType,
            responseHeadersJson: hop.responseHeaders || {},
          })),
        });
      }

      logger.info(`URL tracking completed: ${check.id}`, {
        userId,
        url: validatedRequest.url,
        redirectCount: hops.length,
        totalTime,
      });

      return {
        check: {
          id: check.id,
          inputUrl: check.inputUrl,
          method: check.method,
          status: check.status,
          totalTimeMs: totalTime,
          redirectCount: hops.length,
          finalUrl,
        },
      };

    } catch (error) {
      logger.error('URL tracking failed:', {
        userId,
        url: validatedRequest.url,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  private async followRedirects(request: TrackRequest): Promise<Array<{
    url: string;
    statusCode?: number;
    redirectType: string;
    latencyMs?: number;
    contentType?: string;
    responseHeaders?: Record<string, string>;
  }>> {
    const hops: Array<{
      url: string;
      statusCode?: number;
      redirectType: string;
      latencyMs?: number;
      contentType?: string;
      responseHeaders?: Record<string, string>;
    }> = [];

    let currentUrl = request.url;
    let hopCount = 0;
    const visitedUrls = new Set<string>();

    while (hopCount < request.maxHops) {
      if (visitedUrls.has(currentUrl)) {
        // Loop detected
        hops.push({
          url: currentUrl,
          redirectType: 'LOOP',
        });
        break;
      }

      visitedUrls.add(currentUrl);
      const hopStartTime = Date.now();

      try {
        const response = await axios({
          method: request.method,
          url: currentUrl,
          timeout: request.timeout,
          maxRedirects: 0, // Handle redirects manually
          validateStatus: () => true, // Accept all status codes
          headers: {
            'User-Agent': request.userAgent || 'RedirectIntelligence/2.0',
            ...request.headers,
          },
        });

        const latency = Date.now() - hopStartTime;
        const headers: Record<string, string> = {};
        
        // Convert headers to plain object
        Object.entries(response.headers).forEach(([key, value]) => {
          if (typeof value === 'string') {
            headers[key.toLowerCase()] = value;
          }
        });

        hops.push({
          url: currentUrl,
          statusCode: response.status,
          redirectType: this.getRedirectType(response.status),
          latencyMs: latency,
          contentType: headers['content-type']?.split(';')[0] || undefined,
          responseHeaders: headers,
        });

        // Check if this is a redirect
        if (response.status >= 300 && response.status < 400) {
          const location = headers.location;
          if (location) {
            currentUrl = new URL(location, currentUrl).href;
            hopCount++;
            continue;
          }
        }

        // Not a redirect, we're done
        break;

      } catch (error) {
        const latency = Date.now() - hopStartTime;
        
        hops.push({
          url: currentUrl,
          redirectType: 'ERROR',
          latencyMs: latency,
        });
        break;
      }
    }

    // Add final hop if we stopped due to max hops
    if (hopCount >= request.maxHops && hops.length > 0) {
      const lastHop = hops[hops.length - 1];
      if (lastHop.statusCode && lastHop.statusCode >= 300 && lastHop.statusCode < 400) {
        hops.push({
          url: currentUrl,
          redirectType: 'FINAL',
        });
      }
    }

    return hops;
  }

  private getRedirectType(statusCode: number): string {
    switch (statusCode) {
      case 301: return 'HTTP_301';
      case 302: return 'HTTP_302';
      case 307: return 'HTTP_307';
      case 308: return 'HTTP_308';
      case 200: return 'FINAL';
      default: return statusCode >= 300 && statusCode < 400 ? 'OTHER' : 'FINAL';
    }
  }
}

