/**
 * Security & Audit Service
 *
 * Provides functionality for audit log viewing, login history analysis,
 * and security monitoring.
 */

import prisma from '@/lib/db';
import { auditLogsAdapter } from '@/lib/adapters/misc';

export interface AuditLogFilters {
  userId?: string;
  type?: string;
  action?: string;
  success?: boolean;
  startDate?: Date;
  endDate?: Date;
  ip?: string;
  page?: number;
  limit?: number;
}

export interface LoginHistoryFilters {
  userId?: string;
  success?: boolean;
  country?: string;
  startDate?: Date;
  endDate?: Date;
  ip?: string;
  page?: number;
  limit?: number;
}

export interface AuditLogEntry {
  id: string;
  userId: string | null;
  type: string;
  action: string;
  success: boolean;
  ip: string;
  userAgent: string | null;
  metadata: Record<string, unknown> | null;
  createdAt: Date;
  user: {
    id: string;
    email: string;
    name: string | null;
  } | null;
}

export interface LoginHistoryEntry {
  id: string;
  userId: string;
  ipAddress: string;
  userAgent: string | null;
  country: string | null;
  countryCode: string | null;
  city: string | null;
  region: string | null;
  latitude: number | null;
  longitude: number | null;
  timezone: string | null;
  provider: string;
  success: boolean;
  failReason: string | null;
  createdAt: Date;
  user: {
    id: string;
    email: string;
    name: string | null;
  };
}

export interface SecurityStats {
  totalLogins24h: number;
  failedLogins24h: number;
  uniqueUsers24h: number;
  uniqueCountries24h: number;
  suspiciousActivities: SuspiciousActivity[];
}

export interface SuspiciousActivity {
  type: 'multiple_failures' | 'new_location' | 'unusual_time' | 'rapid_attempts';
  userId: string;
  userEmail: string;
  userName: string | null;
  details: string;
  count: number;
  lastOccurrence: Date;
}

/**
 * Get audit logs with filtering and pagination
 */
export async function getAuditLogs(filters: AuditLogFilters): Promise<{
  logs: AuditLogEntry[];
  total: number;
  page: number;
  totalPages: number;
}> {
  const page = filters.page || 1;
  const limit = filters.limit || 50;
  const skip = (page - 1) * limit;

  const where: Record<string, unknown> = {};

  if (filters.userId) {
    where.userId = filters.userId;
  }

  if (filters.type) {
    where.type = filters.type;
  }

  if (filters.action) {
    where.action = { contains: filters.action, mode: 'insensitive' };
  }

  if (filters.success !== undefined) {
    where.success = filters.success;
  }

  if (filters.ip) {
    where.ip = { contains: filters.ip };
  }

  if (filters.startDate || filters.endDate) {
    where.createdAt = {};
    if (filters.startDate) {
      (where.createdAt as Record<string, Date>).gte = filters.startDate;
    }
    if (filters.endDate) {
      (where.createdAt as Record<string, Date>).lte = filters.endDate;
    }
  }

  const [logs, total] = await Promise.all([
    prisma.audit_logs.findMany({
      where,
      include: {
        users: {
          select: {
            id: true,
            email: true,
            name: true,
          },
        },
      },
      orderBy: { createdAt: 'desc' },
      skip,
      take: limit,
    }),
    auditLogsAdapter.count({ where }),
  ]);

  return {
    logs: logs.map((log) => ({
      id: log.id,
      userId: log.userId,
      type: log.type,
      action: log.action,
      success: log.success,
      ip: log.ip,
      userAgent: log.userAgent,
      metadata: log.metadata as Record<string, unknown> | null,
      createdAt: log.createdAt,
      user: log.users ? {
        id: log.users.id,
        email: log.users.email,
        name: log.users.name,
      } : null,
    })),
    total,
    page,
    totalPages: Math.ceil(total / limit),
  };
}

/**
 * Get distinct audit log types for filtering
 */
export async function getAuditLogTypes(): Promise<string[]> {
  const types = await prisma.audit_logs.findMany({
    select: { type: true },
    distinct: ['type'],
    orderBy: { type: 'asc' },
  });

  return types.map((t) => t.type);
}

/**
 * Get login history with filtering and pagination
 */
export async function getLoginHistory(filters: LoginHistoryFilters): Promise<{
  history: LoginHistoryEntry[];
  total: number;
  page: number;
  totalPages: number;
}> {
  const page = filters.page || 1;
  const limit = filters.limit || 50;
  const skip = (page - 1) * limit;

  const where: Record<string, unknown> = {};

  if (filters.userId) {
    where.userId = filters.userId;
  }

  if (filters.success !== undefined) {
    where.success = filters.success;
  }

  if (filters.country) {
    where.country = { contains: filters.country, mode: 'insensitive' };
  }

  if (filters.ip) {
    where.ipAddress = { contains: filters.ip };
  }

  if (filters.startDate || filters.endDate) {
    where.createdAt = {};
    if (filters.startDate) {
      (where.createdAt as Record<string, Date>).gte = filters.startDate;
    }
    if (filters.endDate) {
      (where.createdAt as Record<string, Date>).lte = filters.endDate;
    }
  }

  const [history, total] = await Promise.all([
    prisma.login_history.findMany({
      where,
      include: {
        users: {
          select: {
            id: true,
            email: true,
            name: true,
          },
        },
      },
      orderBy: { createdAt: 'desc' },
      skip,
      take: limit,
    }),
    prisma.login_history.count({ where }),
  ]);

  return {
    history: history.map((entry) => ({
      id: entry.id,
      userId: entry.userId,
      ipAddress: entry.ipAddress,
      userAgent: entry.userAgent,
      country: entry.country,
      countryCode: entry.countryCode,
      city: entry.city,
      region: entry.region,
      latitude: entry.latitude ? Number(entry.latitude) : null,
      longitude: entry.longitude ? Number(entry.longitude) : null,
      timezone: entry.timezone,
      provider: entry.provider,
      success: entry.success,
      failReason: entry.failReason,
      createdAt: entry.createdAt,
      user: {
        id: entry.users.id,
        email: entry.users.email,
        name: entry.users.name,
      },
    })),
    total,
    page,
    totalPages: Math.ceil(total / limit),
  };
}

/**
 * Get countries from login history for filtering
 */
export async function getLoginCountries(): Promise<string[]> {
  const countries = await prisma.login_history.findMany({
    select: { country: true },
    distinct: ['country'],
    where: { country: { not: null } },
    orderBy: { country: 'asc' },
  });

  return countries.map((c) => c.country).filter(Boolean) as string[];
}

/**
 * Get security statistics and suspicious activities
 */
export async function getSecurityStats(): Promise<SecurityStats> {
  const now = new Date();
  const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);

  // Get login stats for last 24 hours
  const [totalLogins, failedLogins, uniqueUsers, uniqueCountries] = await Promise.all([
    prisma.login_history.count({
      where: { createdAt: { gte: yesterday } },
    }),
    prisma.login_history.count({
      where: { createdAt: { gte: yesterday }, success: false },
    }),
    prisma.login_history.groupBy({
      by: ['userId'],
      where: { createdAt: { gte: yesterday } },
    }),
    prisma.login_history.groupBy({
      by: ['country'],
      where: { createdAt: { gte: yesterday }, country: { not: null } },
    }),
  ]);

  // Detect suspicious activities
  const suspiciousActivities: SuspiciousActivity[] = [];

  // Find users with multiple failed logins in last 24 hours
  const failedLoginsByUser = await prisma.login_history.groupBy({
    by: ['userId'],
    where: {
      createdAt: { gte: yesterday },
      success: false,
    },
    _count: { id: true },
    having: {
      id: { _count: { gte: 3 } },
    },
  });

  for (const failed of failedLoginsByUser) {
    const user = await prisma.users.findUnique({
      where: { id: failed.userId },
      select: { id: true, email: true, name: true },
    });

    if (user) {
      const lastFailed = await prisma.login_history.findFirst({
        where: { userId: failed.userId, success: false },
        orderBy: { createdAt: 'desc' },
      });

      suspiciousActivities.push({
        type: 'multiple_failures',
        userId: user.id,
        userEmail: user.email,
        userName: user.name,
        details: `${failed._count.id} failed login attempts in the last 24 hours`,
        count: failed._count.id,
        lastOccurrence: lastFailed?.createdAt || now,
      });
    }
  }

  // Find rapid login attempts (more than 5 in 5 minutes)
  const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
  const rapidAttempts = await prisma.login_history.groupBy({
    by: ['userId'],
    where: {
      createdAt: { gte: fiveMinutesAgo },
    },
    _count: { id: true },
    having: {
      id: { _count: { gte: 5 } },
    },
  });

  for (const rapid of rapidAttempts) {
    const user = await prisma.users.findUnique({
      where: { id: rapid.userId },
      select: { id: true, email: true, name: true },
    });

    if (user && !suspiciousActivities.find(a => a.userId === user.id && a.type === 'rapid_attempts')) {
      suspiciousActivities.push({
        type: 'rapid_attempts',
        userId: user.id,
        userEmail: user.email,
        userName: user.name,
        details: `${rapid._count.id} login attempts in the last 5 minutes`,
        count: rapid._count.id,
        lastOccurrence: now,
      });
    }
  }

  return {
    totalLogins24h: totalLogins,
    failedLogins24h: failedLogins,
    uniqueUsers24h: uniqueUsers.length,
    uniqueCountries24h: uniqueCountries.length,
    suspiciousActivities,
  };
}

/**
 * Get login history by country for map visualization
 */
export async function getLoginsByCountry(days: number = 30): Promise<{
  country: string;
  countryCode: string;
  count: number;
  successCount: number;
  failCount: number;
}[]> {
  const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);

  const logins = await prisma.login_history.groupBy({
    by: ['country', 'countryCode'],
    where: {
      createdAt: { gte: since },
      country: { not: null },
    },
    _count: { id: true },
  });

  const successLogins = await prisma.login_history.groupBy({
    by: ['country'],
    where: {
      createdAt: { gte: since },
      country: { not: null },
      success: true,
    },
    _count: { id: true },
  });

  const successMap = new Map(successLogins.map(s => [s.country, s._count.id]));

  return logins
    .filter(l => l.country && l.countryCode)
    .map(l => ({
      country: l.country!,
      countryCode: l.countryCode!,
      count: l._count.id,
      successCount: successMap.get(l.country) || 0,
      failCount: l._count.id - (successMap.get(l.country) || 0),
    }))
    .sort((a, b) => b.count - a.count);
}

/**
 * Export audit logs to CSV format
 */
export async function exportAuditLogsToCSV(filters: AuditLogFilters): Promise<string> {
  const { logs } = await getAuditLogs({ ...filters, limit: 10000 });

  const headers = ['Timestamp', 'User', 'Email', 'Type', 'Action', 'Success', 'IP', 'User Agent'];
  const rows = logs.map(log => [
    log.createdAt.toISOString(),
    log.user?.name || 'N/A',
    log.user?.email || 'N/A',
    log.type,
    log.action,
    log.success ? 'Yes' : 'No',
    log.ip,
    log.userAgent || 'N/A',
  ]);

  return [headers.join(','), ...rows.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(','))].join('\n');
}

/**
 * Export login history to CSV format
 */
export async function exportLoginHistoryToCSV(filters: LoginHistoryFilters): Promise<string> {
  const { history } = await getLoginHistory({ ...filters, limit: 10000 });

  const headers = ['Timestamp', 'User', 'Email', 'Success', 'IP', 'Country', 'City', 'Provider', 'Fail Reason'];
  const rows = history.map(entry => [
    entry.createdAt.toISOString(),
    entry.user.name || 'N/A',
    entry.user.email,
    entry.success ? 'Yes' : 'No',
    entry.ipAddress,
    entry.country || 'N/A',
    entry.city || 'N/A',
    entry.provider,
    entry.failReason || 'N/A',
  ]);

  return [headers.join(','), ...rows.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(','))].join('\n');
}
