/**
 * The Hearth Sentinel Service
 *
 * Orchestrates OWASP threat monitoring, code scanning, and remediation tracking.
 * Integrates with the FF Sentinel for unified security posture.
 */

import prisma from '@/lib/db';
import { codeScanner } from './scanner';
import { owaspThreatDb } from './owasp-db';
import type { ScanResult, ScanFinding } from './types';

export class DfSentinelService {
  /**
   * Run a full OWASP scan and persist results
   */
  async runScan(options: {
    target?: string;
    targetModule?: string;
    scheduledBy?: string;
    artifacts?: Array<{ filePath: string; content: string }>;
  } = {}): Promise<ScanResult> {
    const { target = 'all', targetModule, scheduledBy = 'system', artifacts = [] } = options;

    // Create scan record
    const scan = await prisma.owaspScan.create({
      data: {
        scanType: artifacts.length > 0 ? 'targeted' : 'full',
        target,
        targetModule,
        status: 'running',
        threatDbVersion: 'OWASP Top 10 — 2021',
        scheduledBy,
        startedAt: new Date(),
      },
    });

    try {
      // Run the scan
      const result = codeScanner.scanArtifacts(artifacts);

      // Persist findings
      if (result.findings.length > 0) {
        await prisma.owaspScanFinding.createMany({
          data: result.findings.map(f => ({
            scanId: scan.id,
            filePath: f.filePath,
            lineNumber: f.lineNumber,
            codeSnippet: f.codeSnippet,
            vulnerability: f.vulnerability,
            severity: f.severity,
            confidence: f.confidence,
            remediation: f.remediation,
            status: 'open',
          })),
        });
      }

      // Update scan record
      await prisma.owaspScan.update({
        where: { id: scan.id },
        data: {
          status: 'completed',
          totalFiles: result.totalFiles,
          filesScanned: result.filesScanned,
          findingsCount: result.findings.length,
          criticalCount: result.criticalCount,
          highCount: result.highCount,
          mediumCount: result.mediumCount,
          lowCount: result.lowCount,
          summary: result.summary,
          completedAt: new Date(),
        },
      });

      return { ...result, scanId: scan.id };
    } catch (error) {
      await prisma.owaspScan.update({
        where: { id: scan.id },
        data: {
          status: 'failed',
          summary: `Scan failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
          completedAt: new Date(),
        },
      });
      throw error;
    }
  }

  /**
   * Get scan history with optional filtering
   */
  async getScanHistory(options: {
    limit?: number;
    target?: string;
    status?: string;
  } = {}) {
    const { limit = 20, target, status } = options;
    return prisma.owaspScan.findMany({
      where: {
        ...(target && { target }),
        ...(status && { status }),
      },
      orderBy: { createdAt: 'desc' },
      take: limit,
      include: { _count: { select: { findings: true } } },
    });
  }

  /**
   * Get findings for a specific scan
   */
  async getScanFindings(scanId: string, severity?: string) {
    return prisma.owaspScanFinding.findMany({
      where: {
        scanId,
        ...(severity && { severity }),
      },
      orderBy: [{ severity: 'asc' }, { filePath: 'asc' }],
      include: { threat: true },
    });
  }

  /**
   * Mark a finding as remediated
   */
  async remediateFinding(findingId: string, remediatedBy: string) {
    return prisma.owaspScanFinding.update({
      where: { id: findingId },
      data: {
        status: 'remediated',
        remediatedAt: new Date(),
        remediatedBy,
      },
    });
  }

  /**
   * Mark a finding as false positive
   */
  async markFalsePositive(findingId: string, remediatedBy: string) {
    return prisma.owaspScanFinding.update({
      where: { id: findingId },
      data: {
        status: 'false_positive',
        remediatedBy,
      },
    });
  }

  /**
   * Get the overall security posture dashboard stats
   */
  async getDashboardStats() {
    const [latestScan, openFindings, recentScans, threatDb] = await Promise.all([
      prisma.owaspScan.findFirst({
        where: { status: 'completed' },
        orderBy: { createdAt: 'desc' },
      }),
      prisma.owaspScanFinding.groupBy({
        by: ['severity'],
        where: { status: 'open' },
        _count: true,
      }),
      prisma.owaspScan.findMany({
        where: { status: 'completed' },
        orderBy: { createdAt: 'desc' },
        take: 7,
        select: {
          id: true,
          createdAt: true,
          findingsCount: true,
          criticalCount: true,
          highCount: true,
        },
      }),
      Promise.resolve(owaspThreatDb.getStats()),
    ]);

    const openBySeverity = Object.fromEntries(
      openFindings.map(g => [g.severity, g._count])
    );

    return {
      lastScanAt: latestScan?.completedAt || null,
      lastScanStatus: latestScan?.status || 'never',
      lastScanSummary: latestScan?.summary || 'No scans performed yet',
      openFindings: {
        critical: openBySeverity['critical'] || 0,
        high: openBySeverity['high'] || 0,
        medium: openBySeverity['medium'] || 0,
        low: openBySeverity['low'] || 0,
        total: Object.values(openBySeverity).reduce((a: number, b) => a + (b as number), 0),
      },
      recentScans,
      threatDatabase: threatDb,
    };
  }

  /**
   * Sync OWASP threat database entries to persistent storage
   */
  async syncThreatDb() {
    const categories = owaspThreatDb.getAllCategories();
    let synced = 0;

    for (const category of categories) {
      for (const pattern of category.patterns) {
        if (pattern.cweId) {
          await prisma.owaspThreat.upsert({
            where: { cweId: pattern.cweId },
            update: {
              title: pattern.name,
              description: pattern.description,
              severity: pattern.severity,
              remediation: pattern.remediation,
              owaspCategory: category.id,
              lastUpdated: new Date(),
            },
            create: {
              cweId: pattern.cweId,
              owaspCategory: category.id,
              title: pattern.name,
              description: pattern.description,
              severity: pattern.severity,
              affectedPattern: pattern.regex || pattern.keywords.join(', '),
              remediation: pattern.remediation,
              references: [`https://owasp.org/Top10/${category.id.replace(':', '%3A')}`],
            },
          });
          synced++;
        }
      }
    }

    return { synced, total: owaspThreatDb.getThreatCount() };
  }
}

export const dfSentinel = new DfSentinelService();
