import {
  BaseHandler,
  Handler,
  Operation,
  ScheduledOperation,
  type IServiceContainer,
} from '@stock-bot/handlers';
import { getLogger } from '@stock-bot/logger';
import { AccessibilityScanner } from '@wcag-ada/scanner';
import type { AccessibilityScanOptions } from '@wcag-ada/shared';
import { prisma } from '../services/prisma';

interface ScanPayload {
  jobId: string;
  websiteId: string;
  userId: string;
  options: AccessibilityScanOptions;
}

interface BatchScanPayload {
  websiteIds: string[];
  userId: string;
  options?: Partial<AccessibilityScanOptions>;
}

interface CleanupPayload {
  daysToKeep?: number;
}

const logger = getLogger('accessibility-scan-handler');

@Handler('accessibility-scan')
export class AccessibilityScanHandler extends BaseHandler {
  private scanner: AccessibilityScanner  < /dev/null |  null = null;

  constructor(services: IServiceContainer) {
    super(services);
  }

  /**
   * Initialize scanner instance
   */
  private async ensureScanner(): Promise<AccessibilityScanner> {
    if (\!this.scanner) {
      this.scanner = new AccessibilityScanner();
      await this.scanner.initialize();
      logger.info('Scanner initialized');
    }
    return this.scanner;
  }

  /**
   * Scan a single website
   */
  @Operation('scan-website')
  async scanWebsite(payload: ScanPayload): Promise<{ scanResultId: string }> {
    const { jobId, websiteId, userId, options } = payload;

    logger.info('Starting website scan', { jobId, websiteId, userId });

    try {
      // Update job status to running
      await prisma.scanJob.update({
        where: { id: jobId },
        data: {
          status: 'RUNNING',
          startedAt: new Date(),
        },
      });

      // Get scanner instance
      const scanner = await this.ensureScanner();

      // Perform scan
      const result = await scanner.scan(options);

      // Save scan result
      const scanResult = await prisma.scanResult.create({
        data: {
          websiteId,
          jobId,
          url: result.url,
          scanDuration: result.scanDuration,
          summary: result.summary as any,
          violations: result.violations as any,
          passes: result.passes as any,
          incomplete: result.incomplete as any,
          inapplicable: result.inapplicable as any,
          pageMetadata: result.pageMetadata as any,
          wcagCompliance: result.wcagCompliance as any,
        },
      });

      // Update job status to completed
      await prisma.scanJob.update({
        where: { id: jobId },
        data: {
          status: 'COMPLETED',
          completedAt: new Date(),
        },
      });

      // Update website with latest scan info
      await prisma.website.update({
        where: { id: websiteId },
        data: {
          lastScanAt: new Date(),
          complianceScore: result.summary.score,
        },
      });

      logger.info('Website scan completed', {
        jobId,
        websiteId,
        scanResultId: scanResult.id,
        score: result.summary.score,
      });

      return { scanResultId: scanResult.id };
    } catch (error: any) {
      logger.error('Website scan failed', {
        jobId,
        websiteId,
        error: error.message,
      });

      // Update job status to failed
      await prisma.scanJob.update({
        where: { id: jobId },
        data: {
          status: 'FAILED',
          error: error.message,
          completedAt: new Date(),
        },
      });

      throw error;
    }
  }

  /**
   * Scan multiple websites in batch
   */
  @Operation('scan-batch')
  async scanBatch(payload: BatchScanPayload): Promise<{ jobsCreated: number }> {
    const { websiteIds, userId, options = {} } = payload;

    logger.info('Starting batch scan', {
      websiteCount: websiteIds.length,
      userId,
    });

    const jobs = [];

    for (const websiteId of websiteIds) {
      try {
        // Get website details
        const website = await prisma.website.findUnique({
          where: { id: websiteId },
        });

        if (\!website) {
          logger.warn('Website not found', { websiteId });
          continue;
        }

        // Create scan job
        const scanJob = await prisma.scanJob.create({
          data: {
            websiteId,
            userId,
            type: 'FULL_SCAN',
            status: 'PENDING',
          },
        });

        // Queue the scan
        const jobData: ScanPayload = {
          jobId: scanJob.id,
          websiteId,
          userId,
          options: {
            url: website.url,
            ...options,
          },
        };

        // Add to queue using the service's queue manager
        await this.services.queue.addJob('accessibility-scan', 'scan-website', jobData, {
          priority: 5,
          delay: jobs.length * 1000, // Stagger jobs by 1 second
        });

        jobs.push(scanJob.id);
      } catch (error: any) {
        logger.error('Failed to create batch scan job', {
          websiteId,
          error: error.message,
        });
      }
    }

    logger.info('Batch scan jobs created', {
      jobsCreated: jobs.length,
      totalRequested: websiteIds.length,
    });

    return { jobsCreated: jobs.length };
  }

  /**
   * Clean up old scan results
   */
  @ScheduledOperation('cleanup-old-scans', '0 2 * * *', {
    priority: 10,
    immediately: false,
    description: 'Clean up scan results older than specified days (default: 90)',
  })
  async cleanupOldScans(payload: CleanupPayload = {}): Promise<{ deleted: number }> {
    const daysToKeep = payload.daysToKeep || 90;
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

    logger.info('Starting cleanup of old scans', {
      daysToKeep,
      cutoffDate,
    });

    try {
      // Delete old scan results
      const deleteResult = await prisma.scanResult.deleteMany({
        where: {
          createdAt: {
            lt: cutoffDate,
          },
        },
      });

      // Delete orphaned scan jobs
      const deleteJobs = await prisma.scanJob.deleteMany({
        where: {
          createdAt: {
            lt: cutoffDate,
          },
          status: {
            in: ['COMPLETED', 'FAILED'],
          },
        },
      });

      logger.info('Cleanup completed', {
        scanResultsDeleted: deleteResult.count,
        scanJobsDeleted: deleteJobs.count,
      });

      return { deleted: deleteResult.count + deleteJobs.count };
    } catch (error: any) {
      logger.error('Cleanup failed', { error: error.message });
      throw error;
    }
  }

  /**
   * Get scan statistics for monitoring
   */
  @Operation('get-scan-stats')
  async getScanStats(): Promise<any> {
    const [pending, running, completed, failed, totalScans] = await Promise.all([
      prisma.scanJob.count({ where: { status: 'PENDING' } }),
      prisma.scanJob.count({ where: { status: 'RUNNING' } }),
      prisma.scanJob.count({ where: { status: 'COMPLETED' } }),
      prisma.scanJob.count({ where: { status: 'FAILED' } }),
      prisma.scanResult.count(),
    ]);

    const last24Hours = new Date();
    last24Hours.setHours(last24Hours.getHours() - 24);

    const recentScans = await prisma.scanResult.count({
      where: {
        createdAt: {
          gte: last24Hours,
        },
      },
    });

    return {
      jobs: {
        pending,
        running,
        completed,
        failed,
        total: pending + running + completed + failed,
      },
      scans: {
        total: totalScans,
        last24Hours: recentScans,
      },
    };
  }

  /**
   * Cleanup resources when handler is destroyed
   */
  async destroy(): Promise<void> {
    if (this.scanner) {
      await this.scanner.close();
      this.scanner = null;
      logger.info('Scanner closed');
    }
  }
}
