/**
 * Audit & Blockchain Database Service
 * Operations for audit logging and blockchain block storage
 */

import prisma from './prisma';
import type { AuditLog, BlockchainBlock, Prisma } from '@prisma/client';
import type { PaginationOptions, PaginatedResult, DateRangeFilter } from './types';
import { createPaginatedResult } from './types';

// ============================================
// AUDIT LOG OPERATIONS
// ============================================

// Create an audit log entry
export async function createAuditLog(data: {
  userId?: string;
  action: string;
  entityType: string;
  entityId?: string;
  previousValue?: Record<string, unknown>;
  newValue?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
  ipAddress?: string;
  userAgent?: string;
}): Promise<AuditLog> {
  return prisma.auditLog.create({ data });
}

// Get audit logs with pagination
export async function getAuditLogs(
  options: PaginationOptions = {},
  filters?: {
    userId?: string;
    action?: string;
    entityType?: string;
    entityId?: string;
    dateRange?: DateRangeFilter;
  }
): Promise<PaginatedResult<AuditLog>> {
  const page = options.page || 1;
  const limit = options.limit || 50;
  const skip = (page - 1) * limit;

  const where: Prisma.AuditLogWhereInput = {};
  if (filters?.userId) where.userId = filters.userId;
  if (filters?.action) where.action = { contains: filters.action, mode: 'insensitive' };
  if (filters?.entityType) where.entityType = filters.entityType;
  if (filters?.entityId) where.entityId = filters.entityId;
  if (filters?.dateRange) {
    where.timestamp = {
      gte: filters.dateRange.start,
      lte: filters.dateRange.end,
    };
  }

  const [logs, total] = await Promise.all([
    prisma.auditLog.findMany({
      where,
      skip,
      take: limit,
      orderBy: { timestamp: 'desc' },
      include: { user: true },
    }),
    prisma.auditLog.count({ where }),
  ]);

  return createPaginatedResult(logs, total, page, limit);
}

// Get audit logs for entity
export async function getEntityAuditLogs(
  entityType: string,
  entityId: string
): Promise<AuditLog[]> {
  return prisma.auditLog.findMany({
    where: { entityType, entityId },
    orderBy: { timestamp: 'desc' },
    include: { user: true },
  });
}

// Get audit logs by user
export async function getUserAuditLogs(
  userId: string,
  options: PaginationOptions = {}
): Promise<PaginatedResult<AuditLog>> {
  const page = options.page || 1;
  const limit = options.limit || 50;
  const skip = (page - 1) * limit;

  const [logs, total] = await Promise.all([
    prisma.auditLog.findMany({
      where: { userId },
      skip,
      take: limit,
      orderBy: { timestamp: 'desc' },
    }),
    prisma.auditLog.count({ where: { userId } }),
  ]);

  return createPaginatedResult(logs, total, page, limit);
}

// Get recent audit logs summary
export async function getAuditLogsSummary(hours: number = 24) {
  const since = new Date(Date.now() - hours * 60 * 60 * 1000);

  const [
    totalLogs,
    actionBreakdown,
    entityBreakdown,
    recentLogs,
  ] = await Promise.all([
    prisma.auditLog.count({
      where: { timestamp: { gte: since } },
    }),
    prisma.auditLog.groupBy({
      by: ['action'],
      _count: { action: true },
      where: { timestamp: { gte: since } },
    }),
    prisma.auditLog.groupBy({
      by: ['entityType'],
      _count: { entityType: true },
      where: { timestamp: { gte: since } },
    }),
    prisma.auditLog.findMany({
      where: { timestamp: { gte: since } },
      orderBy: { timestamp: 'desc' },
      take: 10,
      include: { user: true },
    }),
  ]);

  return {
    totalLogs,
    actionBreakdown: Object.fromEntries(
      actionBreakdown.map(a => [a.action, a._count.action])
    ),
    entityBreakdown: Object.fromEntries(
      entityBreakdown.map(e => [e.entityType, e._count.entityType])
    ),
    recentLogs,
  };
}

// ============================================
// BLOCKCHAIN BLOCK OPERATIONS
// ============================================

// Create a blockchain block
export async function createBlockchainBlock(data: {
  index: number;
  timestamp: Date;
  previousHash: string;
  hash: string;
  nonce: number;
  blockType: string;
  plantId?: string;
  transportEventId?: string;
  content: Record<string, unknown>;
}): Promise<BlockchainBlock> {
  return prisma.blockchainBlock.create({ data });
}

// Get blockchain block by index
export async function getBlockchainBlockByIndex(index: number): Promise<BlockchainBlock | null> {
  return prisma.blockchainBlock.findUnique({
    where: { index },
  });
}

// Get blockchain block by hash
export async function getBlockchainBlockByHash(hash: string): Promise<BlockchainBlock | null> {
  return prisma.blockchainBlock.findUnique({
    where: { hash },
  });
}

// Get latest blockchain block
export async function getLatestBlockchainBlock(): Promise<BlockchainBlock | null> {
  return prisma.blockchainBlock.findFirst({
    orderBy: { index: 'desc' },
  });
}

// Get blockchain blocks with pagination
export async function getBlockchainBlocks(
  options: PaginationOptions = {},
  filters?: {
    blockType?: string;
    plantId?: string;
    transportEventId?: string;
  }
): Promise<PaginatedResult<BlockchainBlock>> {
  const page = options.page || 1;
  const limit = options.limit || 50;
  const skip = (page - 1) * limit;

  const where: Prisma.BlockchainBlockWhereInput = {};
  if (filters?.blockType) where.blockType = filters.blockType;
  if (filters?.plantId) where.plantId = filters.plantId;
  if (filters?.transportEventId) where.transportEventId = filters.transportEventId;

  const [blocks, total] = await Promise.all([
    prisma.blockchainBlock.findMany({
      where,
      skip,
      take: limit,
      orderBy: { index: 'desc' },
    }),
    prisma.blockchainBlock.count({ where }),
  ]);

  return createPaginatedResult(blocks, total, page, limit);
}

// Verify blockchain integrity
export async function verifyBlockchainIntegrity(): Promise<{
  isValid: boolean;
  invalidBlocks: number[];
  totalBlocks: number;
}> {
  const blocks = await prisma.blockchainBlock.findMany({
    orderBy: { index: 'asc' },
  });

  const invalidBlocks: number[] = [];

  for (let i = 1; i < blocks.length; i++) {
    const currentBlock = blocks[i];
    const previousBlock = blocks[i - 1];

    // Check if previous hash matches
    if (currentBlock.previousHash !== previousBlock.hash) {
      invalidBlocks.push(currentBlock.index);
    }
  }

  return {
    isValid: invalidBlocks.length === 0,
    invalidBlocks,
    totalBlocks: blocks.length,
  };
}

// Get blockchain statistics
export async function getBlockchainStats() {
  const [
    totalBlocks,
    blocksByType,
    latestBlock,
    oldestBlock,
  ] = await Promise.all([
    prisma.blockchainBlock.count(),
    prisma.blockchainBlock.groupBy({
      by: ['blockType'],
      _count: { blockType: true },
    }),
    prisma.blockchainBlock.findFirst({ orderBy: { index: 'desc' } }),
    prisma.blockchainBlock.findFirst({ orderBy: { index: 'asc' } }),
  ]);

  return {
    totalBlocks,
    blocksByType: Object.fromEntries(
      blocksByType.map(b => [b.blockType, b._count.blockType])
    ),
    latestBlockIndex: latestBlock?.index ?? -1,
    latestBlockHash: latestBlock?.hash,
    oldestBlockTimestamp: oldestBlock?.timestamp,
    latestBlockTimestamp: latestBlock?.timestamp,
  };
}

// Helper to log entity changes
export async function logEntityChange<T extends Record<string, unknown>>(
  userId: string | undefined,
  action: 'CREATE' | 'UPDATE' | 'DELETE',
  entityType: string,
  entityId: string,
  previousValue: T | null,
  newValue: T | null,
  metadata?: Record<string, unknown>
): Promise<AuditLog> {
  return createAuditLog({
    userId,
    action,
    entityType,
    entityId,
    previousValue: previousValue || undefined,
    newValue: newValue || undefined,
    metadata,
  });
}
