import { prisma } from '@/lib/prisma'
import type { ErrorSeverity } from '@prisma/client'
import { notificationService } from './notification-service'

// ============================================================================
// Types
// ============================================================================

export interface CreateRuleInput {
  name: string
  pattern: string
  severity?: ErrorSeverity
  description?: string
}

export interface UpdateRuleInput {
  name?: string
  pattern?: string
  severity?: ErrorSeverity
  description?: string
  isActive?: boolean
}

export interface DetectedErrorInput {
  serverId: string
  ruleId: string
  containerId?: string
  containerName?: string
  logLine: string
  context?: string
}

export interface ErrorFilters {
  serverId?: string
  severity?: ErrorSeverity
  acknowledged?: boolean
  ruleId?: string
  limit?: number
  offset?: number
}

export interface ScanResult {
  ruleId: string
  ruleName: string
  severity: ErrorSeverity
  logLine: string
  lineNumber: number
  matchStart: number
  matchEnd: number
}

// ============================================================================
// Rule Management
// ============================================================================

export async function getErrorRules(clientId: string) {
  return prisma.errorDetectionRule.findMany({
    where: { clientId },
    orderBy: [
      { severity: 'desc' },
      { name: 'asc' },
    ],
    include: {
      _count: {
        select: { detectedErrors: true },
      },
    },
  })
}

export async function getErrorRule(ruleId: string) {
  return prisma.errorDetectionRule.findUnique({
    where: { id: ruleId },
    include: {
      _count: {
        select: { detectedErrors: true },
      },
    },
  })
}

export async function createErrorRule(clientId: string, data: CreateRuleInput) {
  // Validate regex pattern
  try {
    new RegExp(data.pattern)
  } catch (e) {
    throw new Error(`Invalid regex pattern: ${(e as Error).message}`)
  }

  return prisma.errorDetectionRule.create({
    data: {
      clientId,
      name: data.name,
      pattern: data.pattern,
      severity: data.severity ?? 'WARNING',
      description: data.description,
    },
  })
}

export async function updateErrorRule(ruleId: string, data: UpdateRuleInput) {
  // Validate regex pattern if provided
  if (data.pattern) {
    try {
      new RegExp(data.pattern)
    } catch (e) {
      throw new Error(`Invalid regex pattern: ${(e as Error).message}`)
    }
  }

  return prisma.errorDetectionRule.update({
    where: { id: ruleId },
    data,
  })
}

export async function deleteErrorRule(ruleId: string) {
  // This will also delete related detectedErrors due to onDelete: Cascade
  return prisma.errorDetectionRule.delete({
    where: { id: ruleId },
  })
}

// ============================================================================
// Error Detection (Scanning)
// ============================================================================

export async function scanLogs(
  serverId: string,
  containerId: string | undefined,
  containerName: string | undefined,
  logContent: string,
  clientId: string
): Promise<ScanResult[]> {
  // Get all active rules for this client
  const rules = await prisma.errorDetectionRule.findMany({
    where: {
      clientId,
      isActive: true,
    },
  })

  if (rules.length === 0) {
    return []
  }

  const results: ScanResult[] = []
  const lines = logContent.split('\n')

  for (const rule of rules) {
    try {
      const regex = new RegExp(rule.pattern, 'gi')

      lines.forEach((line, index) => {
        const match = regex.exec(line)
        if (match) {
          results.push({
            ruleId: rule.id,
            ruleName: rule.name,
            severity: rule.severity,
            logLine: line,
            lineNumber: index + 1,
            matchStart: match.index,
            matchEnd: match.index + match[0].length,
          })
        }
        // Reset regex lastIndex for next line
        regex.lastIndex = 0
      })
    } catch (e) {
      console.error(`Error applying rule ${rule.name}:`, e)
    }
  }

  return results
}

export async function scanAndStoreErrors(
  serverId: string,
  containerId: string | undefined,
  containerName: string | undefined,
  logContent: string,
  clientId: string
): Promise<number> {
  const scanResults = await scanLogs(serverId, containerId, containerName, logContent, clientId)

  if (scanResults.length === 0) {
    return 0
  }

  // Get context around each matched line (2 lines before and after)
  const lines = logContent.split('\n')

  const errorsToCreate = scanResults.map(result => {
    const lineIndex = result.lineNumber - 1
    const contextStart = Math.max(0, lineIndex - 2)
    const contextEnd = Math.min(lines.length, lineIndex + 3)
    const context = lines.slice(contextStart, contextEnd).join('\n')

    return {
      serverId,
      ruleId: result.ruleId,
      containerId,
      containerName,
      logLine: result.logLine,
      context,
    }
  })

  // Batch create errors (avoid duplicates by checking recent entries)
  const recentCutoff = new Date(Date.now() - 5 * 60 * 1000) // 5 minutes ago

  let createdCount = 0
  const criticalErrors: Array<{ id: string; ruleId: string }> = []

  for (const error of errorsToCreate) {
    // Check if this exact error was recently logged
    const existing = await prisma.detectedError.findFirst({
      where: {
        serverId: error.serverId,
        ruleId: error.ruleId,
        containerId: error.containerId,
        logLine: error.logLine,
        timestamp: { gte: recentCutoff },
      },
    })

    if (!existing) {
      const created = await prisma.detectedError.create({ data: error })
      createdCount++

      // Track critical errors for notification
      const rule = scanResults.find(r => r.ruleId === error.ruleId)
      if (rule?.severity === 'CRITICAL') {
        criticalErrors.push({ id: created.id, ruleId: error.ruleId })
      }
    }
  }

  // Send notifications for critical errors (async, don't wait)
  if (criticalErrors.length > 0) {
    // Only notify for the first critical error to avoid spam
    const firstCritical = criticalErrors[0]
    const errorWithDetails = await prisma.detectedError.findUnique({
      where: { id: firstCritical.id },
      include: {
        rule: true,
        server: { select: { nickname: true, netcupServerId: true, clientId: true } },
      },
    })

    if (errorWithDetails && errorWithDetails.server) {
      // Fire and forget - don't block on notification
      notificationService
        .sendCriticalErrorAlert(errorWithDetails.server.clientId, {
          ...errorWithDetails,
          rule: errorWithDetails.rule,
          server: {
            nickname: errorWithDetails.server.nickname,
            netcupServerId: errorWithDetails.server.netcupServerId,
          },
        })
        .catch((err) => console.error('[Notification] Failed to send critical error alert:', err))
    }
  }

  return createdCount
}

// ============================================================================
// Detected Errors Management
// ============================================================================

export async function getDetectedErrors(clientId: string, filters?: ErrorFilters) {
  const where: Record<string, unknown> = {
    server: {
      clientId,
    },
  }

  if (filters?.serverId) {
    where.serverId = filters.serverId
  }

  if (filters?.severity) {
    where.rule = { severity: filters.severity }
  }

  if (filters?.acknowledged !== undefined) {
    where.acknowledgedAt = filters.acknowledged ? { not: null } : null
  }

  if (filters?.ruleId) {
    where.ruleId = filters.ruleId
  }

  return prisma.detectedError.findMany({
    where,
    include: {
      rule: {
        select: {
          id: true,
          name: true,
          severity: true,
        },
      },
      server: {
        select: {
          id: true,
          nickname: true,
          netcupServerId: true,
        },
      },
    },
    orderBy: {
      timestamp: 'desc',
    },
    take: filters?.limit ?? 100,
    skip: filters?.offset ?? 0,
  })
}

export async function acknowledgeError(errorId: string, userId: string) {
  return prisma.detectedError.update({
    where: { id: errorId },
    data: {
      acknowledgedAt: new Date(),
      acknowledgedBy: userId,
    },
  })
}

export async function acknowledgeErrors(errorIds: string[], userId: string) {
  return prisma.detectedError.updateMany({
    where: { id: { in: errorIds } },
    data: {
      acknowledgedAt: new Date(),
      acknowledgedBy: userId,
    },
  })
}

export async function getUnacknowledgedErrorCount(clientId: string): Promise<number> {
  return prisma.detectedError.count({
    where: {
      server: { clientId },
      acknowledgedAt: null,
    },
  })
}

// ============================================================================
// Cleanup
// ============================================================================

export async function cleanupOldErrors(daysToKeep: number = 90): Promise<number> {
  const cutoff = new Date()
  cutoff.setDate(cutoff.getDate() - daysToKeep)

  const result = await prisma.detectedError.deleteMany({
    where: {
      timestamp: { lt: cutoff },
      acknowledgedAt: { not: null }, // Only delete acknowledged errors
    },
  })

  return result.count
}

// ============================================================================
// Default Rules (for new clients)
// ============================================================================

export const DEFAULT_ERROR_RULES = [
  {
    name: 'Error Keyword',
    pattern: '\\b(error|ERROR|Error)\\b',
    severity: 'ERROR' as ErrorSeverity,
    description: 'Matches any log line containing "error" (case-insensitive word boundary)',
  },
  {
    name: 'Exception',
    pattern: '\\b(exception|Exception|EXCEPTION)\\b',
    severity: 'ERROR' as ErrorSeverity,
    description: 'Matches exception keywords',
  },
  {
    name: 'Failed',
    pattern: '\\b(failed|Failed|FAILED)\\b',
    severity: 'WARNING' as ErrorSeverity,
    description: 'Matches failure keywords',
  },
  {
    name: 'Critical',
    pattern: '\\b(critical|Critical|CRITICAL)\\b',
    severity: 'CRITICAL' as ErrorSeverity,
    description: 'Matches critical severity keywords',
  },
  {
    name: 'Connection Refused',
    pattern: 'connection\\s+refused',
    severity: 'ERROR' as ErrorSeverity,
    description: 'Detects connection refused errors',
  },
  {
    name: 'Out of Memory',
    pattern: '(out\\s+of\\s+memory|OOM|oom-killer)',
    severity: 'CRITICAL' as ErrorSeverity,
    description: 'Detects out of memory conditions',
  },
  {
    name: 'Timeout',
    pattern: '\\b(timeout|timed\\s+out)\\b',
    severity: 'WARNING' as ErrorSeverity,
    description: 'Detects timeout errors',
  },
  {
    name: 'Database Connection Error',
    pattern: '(database|DB|mysql|postgres|mongodb).*?(connection|connect).*?(error|failed|refused)',
    severity: 'CRITICAL' as ErrorSeverity,
    description: 'Detects database connection issues',
  },
]

export async function seedDefaultRules(clientId: string): Promise<number> {
  let createdCount = 0

  for (const rule of DEFAULT_ERROR_RULES) {
    const existing = await prisma.errorDetectionRule.findFirst({
      where: {
        clientId,
        name: rule.name,
      },
    })

    if (!existing) {
      await prisma.errorDetectionRule.create({
        data: {
          clientId,
          ...rule,
        },
      })
      createdCount++
    }
  }

  return createdCount
}
