/**
 * Agent Memory Service
 * 
 * Core service for managing agent memories, shared knowledge,
 * and cross-agent communication.
 */

import { prisma } from '@/lib/db';
import { 
  MemoryCategory, 
  MemoryQueryOptions, 
  AgentMemoryEntry,
  SharedMemoryEntry,
  InsightType,
  InsightSeverity,
  InsightQueryOptions,
  AgentInsightEntry
} from './types';

export class AgentMemoryService {
  private agentId: string;

  constructor(agentId: string) {
    this.agentId = agentId;
  }

  // ============================================
  // AGENT-SPECIFIC MEMORY
  // ============================================

  async remember(params: {
    category: MemoryCategory;
    key: string;
    content: any;
    confidence?: number;
    importance?: number;
    source?: string;
    expiresAt?: Date;
  }): Promise<AgentMemoryEntry> {
    const { category, key, content, confidence = 0.8, importance = 0.5, source, expiresAt } = params;

    // Upsert - update if exists, create if not
    const memory = await prisma.domainAgentMemory.upsert({
      where: {
        agentId_category_key: {
          agentId: this.agentId,
          category,
          key
        }
      },
      update: {
        content,
        confidence,
        importance,
        source,
        expiresAt,
        accessCount: { increment: 1 },
        updatedAt: new Date()
      },
      create: {
        id: `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        agentId: this.agentId,
        category,
        key,
        content,
        confidence,
        importance,
        source,
        expiresAt,
        updatedAt: new Date()
      }
    });

    return memory as unknown as AgentMemoryEntry;
  }

  async recall(options: MemoryQueryOptions = {}): Promise<AgentMemoryEntry[]> {
    const {
      category,
      search,
      minImportance = 0,
      minConfidence = 0,
      includeExpired = false,
      limit = 50,
      offset = 0,
      orderBy = 'importance',
      orderDir = 'desc'
    } = options;

    const where: any = {
      agentId: this.agentId,
      importance: { gte: minImportance },
      confidence: { gte: minConfidence }
    };

    if (category) {
      where.category = Array.isArray(category) ? { in: category } : category;
    }

    if (!includeExpired) {
      where.OR = [
        { expiresAt: null },
        { expiresAt: { gt: new Date() } }
      ];
    }

    const memories = await prisma.domainAgentMemory.findMany({
      where,
      orderBy: { [orderBy]: orderDir },
      take: limit,
      skip: offset
    });

    // Update access counts
    const ids = memories.map((m: any) => m.id);
    await prisma.domainAgentMemory.updateMany({
      where: { id: { in: ids } },
      data: { accessCount: { increment: 1 } }
    });

    return memories as unknown as AgentMemoryEntry[];
  }

  async forget(key: string, category?: MemoryCategory): Promise<void> {
    await prisma.domainAgentMemory.deleteMany({
      where: {
        agentId: this.agentId,
        key,
        ...(category && { category })
      }
    });
  }

  // ============================================
  // SHARED KNOWLEDGE (GHOST MEMORY)
  // ============================================

  async shareKnowledge(params: {
    category: string;
    key: string;
    value: any;
    confidence?: number;
  }): Promise<SharedMemoryEntry> {
    const { category, key, value, confidence = 0.8 } = params;

    const memory = await prisma.ghostMemory.upsert({
      where: {
        category_key: {
          category,
          key
        }
      },
      update: {
        value,
        confidence,
        source: `agent:${this.agentId}`,
        usageCount: { increment: 1 },
        lastUsed: new Date(),
        updatedAt: new Date()
      },
      create: {
        id: `ghost_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        category,
        key,
        value,
        confidence,
        source: `agent:${this.agentId}`,
        updatedAt: new Date()
      }
    });

    return memory as unknown as SharedMemoryEntry;
  }

  async querySharedKnowledge(params: {
    category?: string;
    search?: string;
    limit?: number;
  }): Promise<SharedMemoryEntry[]> {
    const { category, limit = 50 } = params;

    const where: any = {};
    if (category) where.category = category;

    const memories = await prisma.ghostMemory.findMany({
      where,
      orderBy: { confidence: 'desc' },
      take: limit
    });

    return memories as unknown as SharedMemoryEntry[];
  }

  // ============================================
  // INSIGHTS
  // ============================================

  async createInsight(params: {
    type: InsightType;
    title: string;
    content: string;
    severity?: InsightSeverity;
    confidence?: number;
    actionable?: boolean;
    relatedData?: any;
    tags?: string[];
  }): Promise<AgentInsightEntry> {
    const {
      type,
      title,
      content,
      severity = 'info',
      confidence = 0.8,
      actionable = true,
      relatedData = {},
      tags = []
    } = params;

    const insight = await prisma.agentInsight.create({
      data: {
        agentId: this.agentId,
        type,
        title,
        content,
        severity,
        confidence,
        actionable,
        relatedData,
        tags
      }
    });

    return insight as unknown as AgentInsightEntry;
  }

  async getInsights(options: InsightQueryOptions = {}): Promise<AgentInsightEntry[]> {
    const {
      type,
      severity,
      status,
      actionable,
      limit = 50,
      offset = 0
    } = options;

    const where: any = { agentId: this.agentId };

    if (type) where.type = Array.isArray(type) ? { in: type } : type;
    if (severity) where.severity = Array.isArray(severity) ? { in: severity } : severity;
    if (status) where.status = Array.isArray(status) ? { in: status } : status;
    if (actionable !== undefined) where.actionable = actionable;

    const insights = await prisma.agentInsight.findMany({
      where,
      orderBy: [{ severity: 'desc' }, { createdAt: 'desc' }],
      take: limit,
      skip: offset
    });

    return insights as unknown as AgentInsightEntry[];
  }

  async acknowledgeInsight(insightId: string, acknowledgedBy: string): Promise<void> {
    await prisma.agentInsight.update({
      where: { id: insightId },
      data: {
        status: 'acknowledged',
        acknowledgedBy,
        acknowledgedAt: new Date()
      }
    });
  }

  // ============================================
  // CROSS-AGENT COMMUNICATION
  // ============================================

  async queryAgent(params: {
    toAgentId: string;
    subject: string;
    content: any;
    priority?: 'low' | 'normal' | 'high' | 'urgent';
    requiresResponse?: boolean;
  }): Promise<string> {
    const { toAgentId, subject, content, priority = 'normal' } = params;

    const interaction = await prisma.agentInteraction.create({
      data: {
        fromAgentId: this.agentId,
        toAgentId,
        type: 'query',
        priority,
        subject,
        content
      }
    });

    return interaction.id;
  }

  async escalateToAgent(params: {
    toAgentId: string;
    subject: string;
    content: any;
    priority?: 'low' | 'normal' | 'high' | 'urgent';
  }): Promise<string> {
    const { toAgentId, subject, content, priority = 'high' } = params;

    const interaction = await prisma.agentInteraction.create({
      data: {
        fromAgentId: this.agentId,
        toAgentId,
        type: 'escalation',
        priority,
        subject,
        content
      }
    });

    return interaction.id;
  }

  async respondToQuery(interactionId: string, response: any): Promise<void> {
    const startTime = await prisma.agentInteraction.findUnique({
      where: { id: interactionId },
      select: { createdAt: true }
    });

    const responseTimeMs = startTime ? Date.now() - startTime.createdAt.getTime() : undefined;

    await prisma.agentInteraction.update({
      where: { id: interactionId },
      data: {
        response,
        status: 'completed',
        completedAt: new Date(),
        responseTimeMs
      }
    });
  }

  async getPendingQueries(): Promise<any[]> {
    const interactions = await prisma.agentInteraction.findMany({
      where: {
        toAgentId: this.agentId,
        status: 'pending'
      },
      orderBy: [
        { priority: 'desc' },
        { createdAt: 'asc' }
      ]
    });

    return interactions;
  }

  // ============================================
  // STATS & METRICS
  // ============================================

  async getStats(): Promise<{
    totalMemories: number;
    memoriesByCategory: Record<string, number>;
    totalInsights: number;
    insightsBySeverity: Record<string, number>;
    pendingQueries: number;
  }> {
    const [memoryCount, insightCount, queryCount, memoriesByCat, insightsBySev] = await Promise.all([
      prisma.domainAgentMemory.count({ where: { agentId: this.agentId } }),
      prisma.agentInsight.count({ where: { agentId: this.agentId } }),
      prisma.agentInteraction.count({ where: { toAgentId: this.agentId, status: 'pending' } }),
      prisma.domainAgentMemory.groupBy({
        by: ['category'],
        where: { agentId: this.agentId },
        _count: true
      }),
      prisma.agentInsight.groupBy({
        by: ['severity'],
        where: { agentId: this.agentId },
        _count: true
      })
    ]);

    return {
      totalMemories: memoryCount,
      memoriesByCategory: Object.fromEntries(memoriesByCat.map((m: any) => [m.category, m._count])),
      totalInsights: insightCount,
      insightsBySeverity: Object.fromEntries(insightsBySev.map((i: any) => [i.severity, i._count])),
      pendingQueries: queryCount
    };
  }
}

// Factory function
export function createAgentMemory(agentId: string): AgentMemoryService {
  return new AgentMemoryService(agentId);
}
