/**
 * Incident Response Service
 * 
 * Implements incident response automation per DoD/MilSpec requirements:
 * - NIST SP 800-53: IR-1 through IR-8
 * - NIST SP 800-171: 3.6.1-3.6.3 (Incident Response)
 * 
 * Features:
 * - Incident detection and analysis
 * - Incident containment and eradication
 * - Incident recovery
 * - Post-incident activities
 * - Integration with DoD incident reporting
 */

import { getDb } from '../db'
import { logger } from '../lib/logger'
import { logSecurityIncident } from './audit-logger'
import crypto from 'crypto'

export type IncidentSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
export type IncidentStatus = 'DETECTED' | 'ANALYZING' | 'CONTAINED' | 'ERADICATED' | 'RECOVERED' | 'CLOSED'
export type IncidentCategory = 
  | 'UNAUTHORIZED_ACCESS'
  | 'DATA_BREACH'
  | 'MALWARE'
  | 'DOS'
  | 'INSIDER_THREAT'
  | 'PHISHING'
  | 'SYSTEM_COMPROMISE'
  | 'OTHER'

export interface Incident {
  id: string
  category: IncidentCategory
  severity: IncidentSeverity
  status: IncidentStatus
  title: string
  description: string
  detectedAt: Date
  containedAt?: Date
  eradicatedAt?: Date
  recoveredAt?: Date
  closedAt?: Date
  affectedResources?: string[]
  impact?: string
  rootCause?: string
  remediation?: string
  lessonsLearned?: string
  reportedToDoD?: boolean
  doDReportId?: string
  createdBy?: string
  assignedTo?: string
}

/**
 * Create a new security incident
 */
export async function createIncident(
  category: IncidentCategory,
  severity: IncidentSeverity,
  title: string,
  description: string,
  detectedBy?: string,
  affectedResources?: string[]
): Promise<Incident> {
  const db = getDb()
  const incidentId = crypto.randomUUID()

  await db.query(
    `INSERT INTO security_incidents 
     (id, category, severity, status, title, description, detected_at, affected_resources, created_by, created_at)
     VALUES ($1, $2, $3, $4, $5, $6, NOW(), $7, $8, NOW())`,
    [incidentId, category, severity, 'DETECTED', title, description, JSON.stringify(affectedResources || []), detectedBy]
  )

  // Log incident in audit log
  await logSecurityIncident(category, severity, detectedBy, {
    incidentId,
    title,
    description,
    affectedResources,
  })

  logger.error('Security incident created', { incidentId, category, severity, title })

  // Auto-contain critical incidents
  if (severity === 'CRITICAL') {
    await containIncident(incidentId, 'AUTO_CONTAIN')
  }

  return {
    id: incidentId,
    category,
    severity,
    status: 'DETECTED',
    title,
    description,
    detectedAt: new Date(),
    affectedResources,
  }
}

/**
 * Update incident status
 */
export async function updateIncidentStatus(
  incidentId: string,
  status: IncidentStatus,
  updatedBy: string,
  notes?: string
): Promise<void> {
  const db = getDb()
  
  const updateFields: string[] = ['status = $2', 'updated_at = NOW()']
  const params: any[] = [incidentId, status, updatedBy]

  if (status === 'CONTAINED') {
    updateFields.push('contained_at = NOW()')
  } else if (status === 'ERADICATED') {
    updateFields.push('eradicated_at = NOW()')
  } else if (status === 'RECOVERED') {
    updateFields.push('recovered_at = NOW()')
  } else if (status === 'CLOSED') {
    updateFields.push('closed_at = NOW()')
  }

  if (notes) {
    updateFields.push('notes = $' + (params.length + 1))
    params.push(notes)
  }

  await db.query(
    `UPDATE security_incidents 
     SET ${updateFields.join(', ')}
     WHERE id = $1`,
    params
  )

  logger.info('Incident status updated', { incidentId, status, updatedBy })
}

/**
 * Contain an incident
 */
export async function containIncident(incidentId: string, method: string, containedBy?: string): Promise<void> {
  const db = getDb()
  const incident = await getIncident(incidentId)
  
  await updateIncidentStatus(incidentId, 'CONTAINED', containedBy || 'SYSTEM', `Contained via: ${method}`)
  
  // Implement containment actions
  try {
    // 1. Isolate affected systems - mark resources as isolated in database
    if (incident.affectedResources && incident.affectedResources.length > 0) {
      for (const resourceId of incident.affectedResources) {
        await db.query(
          `UPDATE resource_inventory 
           SET status = 'ISOLATED', metadata = jsonb_set(COALESCE(metadata, '{}'), '{isolation_reason}', $2::jsonb)
           WHERE id = $1`,
          [resourceId, JSON.stringify({ incidentId, method, containedBy: containedBy || 'SYSTEM' })]
        )
        logger.info('Resource isolated for incident containment', { resourceId, incidentId })
      }
    }
    
    // 2. Block malicious IPs - store in blocked_ips table for firewall rules
    // This would integrate with Cloudflare Gateway or firewall systems
    const metadata = incident.metadata as Record<string, unknown> | null
    if (metadata && Array.isArray(metadata.maliciousIPs)) {
      const ips = metadata.maliciousIPs as string[]
      for (const ip of ips) {
        await db.query(
          `INSERT INTO blocked_ips (ip_address, reason, incident_id, blocked_at, blocked_by)
           VALUES ($1, $2, $3, NOW(), $4)
           ON CONFLICT (ip_address) DO UPDATE SET reason = $2, incident_id = $3, blocked_at = NOW()`,
          [ip, `Blocked due to incident ${incidentId}`, incidentId, containedBy || 'SYSTEM']
        )
        logger.info('IP blocked for incident containment', { ip, incidentId })
      }
    }
    
    // 3. Revoke compromised credentials - disable user accounts
    if (metadata && Array.isArray(metadata.compromisedUsers)) {
      const userIds = metadata.compromisedUsers as string[]
      for (const userId of userIds) {
        await db.query(
          `UPDATE users SET password_hash = $1, updated_at = NOW()
           WHERE id = $2`,
          ['REVOKED', userId] // Set invalid hash to prevent login
        )
        logger.info('User credentials revoked for incident containment', { userId, incidentId })
      }
    }
    
    // 4. Disable affected accounts - mark as suspended
    if (metadata && Array.isArray(metadata.affectedAccounts)) {
      const accountIds = metadata.affectedAccounts as string[]
      for (const accountId of accountIds) {
        await db.query(
          `UPDATE tenants SET status = 'SUSPENDED', metadata = jsonb_set(COALESCE(metadata, '{}'), '{suspension_reason}', $2::jsonb)
           WHERE id = $1`,
          [accountId, JSON.stringify({ incidentId, reason: 'Suspended due to security incident' })]
        )
        logger.info('Account suspended for incident containment', { accountId, incidentId })
      }
    }
    
    // Log containment actions
    await logSecurityIncident({
      event: 'INCIDENT_CONTAINED',
      severity: incident.severity,
      description: `Incident ${incidentId} contained via ${method}`,
      metadata: {
        incidentId,
        method,
        containedBy: containedBy || 'SYSTEM',
        affectedResources: incident.affectedResources,
      },
    })
    
    logger.info('Incident containment actions completed', { incidentId, method })
  } catch (error) {
    logger.error('Error during incident containment', { error, incidentId })
    throw error
  }
}

/**
 * Eradicate threat
 */
export async function eradicateThreat(incidentId: string, method: string, eradicatedBy?: string): Promise<void> {
  const db = getDb()
  const incident = await getIncident(incidentId)
  
  await updateIncidentStatus(incidentId, 'ERADICATED', eradicatedBy || 'SYSTEM', `Eradicated via: ${method}`)
  
  // Implement eradication actions
  try {
    // 1. Remove malware - mark resources for cleanup
    if (incident.affectedResources && incident.affectedResources.length > 0) {
      for (const resourceId of incident.affectedResources) {
        await db.query(
          `UPDATE resource_inventory 
           SET metadata = jsonb_set(COALESCE(metadata, '{}'), '{eradication_status}', $2::jsonb),
               metadata = jsonb_set(metadata, '{eradication_method}', $3::jsonb)
           WHERE id = $1`,
          [
            resourceId,
            JSON.stringify({ status: 'PENDING_CLEANUP', incidentId }),
            JSON.stringify(method),
          ]
        )
        logger.info('Resource marked for malware eradication', { resourceId, incidentId, method })
      }
    }
    
    // 2. Patch vulnerabilities - record patches applied
    const metadata = incident.metadata as Record<string, unknown> | null
    if (metadata && Array.isArray(metadata.vulnerabilities)) {
      const vulnerabilities = metadata.vulnerabilities as Array<{ cve: string; patch: string }>
      for (const vuln of vulnerabilities) {
        await db.query(
          `INSERT INTO vulnerability_patches (cve_id, patch_id, incident_id, applied_at, applied_by)
           VALUES ($1, $2, $3, NOW(), $4)
           ON CONFLICT (cve_id) DO UPDATE SET patch_id = $2, applied_at = NOW()`,
          [vuln.cve, vuln.patch, incidentId, eradicatedBy || 'SYSTEM']
        )
        logger.info('Vulnerability patch recorded', { cve: vuln.cve, incidentId })
      }
    }
    
    // 3. Remove backdoors - revoke suspicious access tokens and API keys
    await db.query(
      `UPDATE api_keys SET revoked = true, revoked_at = NOW(), revoked_by = $1
       WHERE metadata->>'incident_id' = $2 OR metadata->>'suspicious' = 'true'`,
      [eradicatedBy || 'SYSTEM', incidentId]
    )
    logger.info('Suspicious API keys revoked during eradication', { incidentId })
    
    // 4. Clean compromised systems - schedule cleanup tasks
    if (incident.affectedResources && incident.affectedResources.length > 0) {
      for (const resourceId of incident.affectedResources) {
        await db.query(
          `INSERT INTO cleanup_tasks (resource_id, task_type, incident_id, status, created_at, created_by)
           VALUES ($1, 'FULL_CLEANUP', $2, 'PENDING', NOW(), $3)`,
          [resourceId, incidentId, eradicatedBy || 'SYSTEM']
        )
        logger.info('Cleanup task scheduled for resource', { resourceId, incidentId })
      }
    }
    
    // Log eradication actions
    await logSecurityIncident({
      event: 'INCIDENT_ERADICATED',
      severity: incident.severity,
      description: `Incident ${incidentId} eradicated via ${method}`,
      metadata: {
        incidentId,
        method,
        eradicatedBy: eradicatedBy || 'SYSTEM',
      },
    })
    
    logger.info('Incident eradication actions completed', { incidentId, method })
  } catch (error) {
    logger.error('Error during incident eradication', { error, incidentId })
    throw error
  }
}

/**
 * Recover from incident
 */
export async function recoverFromIncident(incidentId: string, recoveredBy?: string): Promise<void> {
  const db = getDb()
  const incident = await getIncident(incidentId)
  
  await updateIncidentStatus(incidentId, 'RECOVERED', recoveredBy || 'SYSTEM')
  
  // Implement recovery actions
  try {
    // 1. Restore from backups - mark resources for restoration
    if (incident.affectedResources && incident.affectedResources.length > 0) {
      for (const resourceId of incident.affectedResources) {
        // Check if backup exists
        const backupResult = await db.query(
          `SELECT id FROM backups WHERE resource_id = $1 ORDER BY created_at DESC LIMIT 1`,
          [resourceId]
        )
        
        if (backupResult.rows.length > 0) {
          await db.query(
            `INSERT INTO restoration_tasks (resource_id, backup_id, incident_id, status, created_at, created_by)
             VALUES ($1, $2, $3, 'PENDING', NOW(), $4)`,
            [resourceId, backupResult.rows[0].id, incidentId, recoveredBy || 'SYSTEM']
          )
          logger.info('Restoration task scheduled from backup', { resourceId, incidentId })
        } else {
          logger.warn('No backup found for resource restoration', { resourceId, incidentId })
        }
      }
    }
    
    // 2. Verify system integrity - create verification tasks
    if (incident.affectedResources && incident.affectedResources.length > 0) {
      for (const resourceId of incident.affectedResources) {
        await db.query(
          `INSERT INTO integrity_checks (resource_id, incident_id, status, created_at, created_by)
           VALUES ($1, $2, 'PENDING', NOW(), $3)`,
          [resourceId, incidentId, recoveredBy || 'SYSTEM']
        )
        logger.info('Integrity check scheduled for resource', { resourceId, incidentId })
      }
    }
    
    // 3. Resume normal operations - restore resource status
    if (incident.affectedResources && incident.affectedResources.length > 0) {
      for (const resourceId of incident.affectedResources) {
        // Only restore if integrity check passes (would be updated by integrity check process)
        await db.query(
          `UPDATE resource_inventory 
           SET status = CASE 
             WHEN metadata->>'integrity_check_status' = 'PASSED' THEN 'ACTIVE'
             ELSE status
           END,
           metadata = jsonb_set(COALESCE(metadata, '{}'), '{recovery_status}', $2::jsonb)
           WHERE id = $1`,
          [
            resourceId,
            JSON.stringify({ incidentId, recoveredBy: recoveredBy || 'SYSTEM', recoveredAt: new Date() }),
          ]
        )
        logger.info('Resource recovery status updated', { resourceId, incidentId })
      }
    }
    
    // Re-enable suspended accounts if incident is resolved
    if (incident.metadata && (incident.metadata as any).affectedAccounts) {
      const accountIds = (incident.metadata as any).affectedAccounts as string[]
      for (const accountId of accountIds) {
        await db.query(
          `UPDATE tenants 
           SET status = 'ACTIVE', 
               metadata = jsonb_set(metadata, '{suspension_reason}', NULL::jsonb)
           WHERE id = $1 AND metadata->>'suspension_reason'->>'incident_id' = $2`,
          [accountId, incidentId]
        )
        logger.info('Account re-enabled after incident recovery', { accountId, incidentId })
      }
    }
    
    // Log recovery actions
    await logSecurityIncident({
      event: 'INCIDENT_RECOVERED',
      severity: incident.severity,
      description: `Incident ${incidentId} recovery initiated`,
      metadata: {
        incidentId,
        recoveredBy: recoveredBy || 'SYSTEM',
        affectedResources: incident.affectedResources,
      },
    })
    
    logger.info('Incident recovery actions completed', { incidentId })
  } catch (error) {
    logger.error('Error during incident recovery', { error, incidentId })
    throw error
  }
}

/**
 * Close incident
 */
export async function closeIncident(
  incidentId: string,
  rootCause?: string,
  remediation?: string,
  lessonsLearned?: string,
  closedBy?: string
): Promise<void> {
  const db = getDb()
  
  await db.query(
    `UPDATE security_incidents 
     SET status = 'CLOSED', closed_at = NOW(), root_cause = $2, remediation = $3, lessons_learned = $4, updated_at = NOW()
     WHERE id = $1`,
    [incidentId, rootCause, remediation, lessonsLearned, closedBy]
  )

  logger.info('Incident closed', { incidentId, closedBy })
}

/**
 * Report incident to DoD
 */
export async function reportToDoD(incidentId: string, reportId?: string): Promise<void> {
  const db = getDb()
  
  await db.query(
    `UPDATE security_incidents 
     SET reported_to_dod = true, dod_report_id = $2, updated_at = NOW()
     WHERE id = $1`,
    [incidentId, reportId]
  )

  logger.info('Incident reported to DoD', { incidentId, reportId })
}

/**
 * Get incident by ID
 */
export async function getIncident(incidentId: string): Promise<Incident | null> {
  const db = getDb()
  
  const result = await db.query(
    'SELECT * FROM security_incidents WHERE id = $1',
    [incidentId]
  )

  if (result.rows.length === 0) {
    return null
  }

  const row = result.rows[0]
  return {
    id: row.id,
    category: row.category,
    severity: row.severity,
    status: row.status,
    title: row.title,
    description: row.description,
    detectedAt: row.detected_at,
    containedAt: row.contained_at,
    eradicatedAt: row.eradicated_at,
    recoveredAt: row.recovered_at,
    closedAt: row.closed_at,
    affectedResources: row.affected_resources,
    impact: row.impact,
    rootCause: row.root_cause,
    remediation: row.remediation,
    lessonsLearned: row.lessons_learned,
    reportedToDoD: row.reported_to_dod,
    doDReportId: row.dod_report_id,
    createdBy: row.created_by,
    assignedTo: row.assigned_to,
  }
}

/**
 * Get all incidents
 */
export async function getAllIncidents(filters?: {
  status?: IncidentStatus
  severity?: IncidentSeverity
  category?: IncidentCategory
  limit?: number
  offset?: number
}): Promise<Incident[]> {
  const db = getDb()
  
  let query = 'SELECT * FROM security_incidents WHERE 1=1'
  const params: any[] = []
  let paramIndex = 1

  if (filters?.status) {
    query += ` AND status = $${paramIndex++}`
    params.push(filters.status)
  }

  if (filters?.severity) {
    query += ` AND severity = $${paramIndex++}`
    params.push(filters.severity)
  }

  if (filters?.category) {
    query += ` AND category = $${paramIndex++}`
    params.push(filters.category)
  }

  query += ' ORDER BY detected_at DESC'

  if (filters?.limit) {
    query += ` LIMIT $${paramIndex++}`
    params.push(filters.limit)
  }

  if (filters?.offset) {
    query += ` OFFSET $${paramIndex++}`
    params.push(filters.offset)
  }

  const result = await db.query(query, params)

  return result.rows.map(row => ({
    id: row.id,
    category: row.category,
    severity: row.severity,
    status: row.status,
    title: row.title,
    description: row.description,
    detectedAt: row.detected_at,
    containedAt: row.contained_at,
    eradicatedAt: row.eradicated_at,
    recoveredAt: row.recovered_at,
    closedAt: row.closed_at,
    affectedResources: row.affected_resources,
    impact: row.impact,
    rootCause: row.root_cause,
    remediation: row.remediation,
    lessonsLearned: row.lessons_learned,
    reportedToDoD: row.reported_to_dod,
    doDReportId: row.dod_report_id,
    createdBy: row.created_by,
    assignedTo: row.assigned_to,
  }))
}

