import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import { headers } from 'next/headers';
import { BRAND } from '@/lib/white-label/constants';

export const dynamic = 'force-dynamic';

/**
 * MCP (Model Context Protocol) Server Endpoint
 * 
 * Allows external AI clients (Claude, ChatGPT, OpenClaw, etc.) to:
 * - Query memories
 * - Write new memories
 * - Search across categories
 * - Get stale/neglected items
 * - Cross-reference data
 * 
 * Authentication: Bearer token or API key in header
 */

const MCP_VERSION = '1.0.0';

interface MCPRequest {
  jsonrpc: '2.0';
  id: string | number;
  method: string;
  params?: Record<string, unknown>;
}

interface MCPResponse {
  jsonrpc: '2.0';
  id: string | number;
  result?: unknown;
  error?: {
    code: number;
    message: string;
    data?: unknown;
  };
}

// Validate API key
async function validateAuth(request: NextRequest): Promise<boolean> {
  const headersList = headers();
  const authHeader = headersList.get('authorization');
  const apiKey = headersList.get('x-api-key');
  
  // Check for MCP API key in env
  const validKey = process.env.TESA_MCP_API_KEY;
  
  if (!validKey) {
    // If no key is set, allow access (dev mode)
    return true;
  }
  
  if (authHeader?.startsWith('Bearer ')) {
    return authHeader.slice(7) === validKey;
  }
  
  if (apiKey) {
    return apiKey === validKey;
  }
  
  return false;
}

// MCP method handlers
const methods: Record<string, (params: Record<string, unknown>) => Promise<unknown>> = {
  // Get server info
  'mcp.info': async () => ({
    name: 'TesaMemory',
    version: MCP_VERSION,
    description: `Tesa AI Agent Memory System for ${BRAND.companyName}`,
    capabilities: [
      'memory.query',
      'memory.write',
      'memory.update',
      'memory.delete',
      'memory.search',
      'memory.stale',
      'memory.crossref',
      'memory.stats',
      'insights.generate',
    ],
  }),

  // Query memories with filters
  'memory.query': async (params) => {
    const { category, importance, limit = 50, includeInactive = false } = params;
    
    const where: Record<string, unknown> = {};
    if (!includeInactive) where.isActive = true;
    if (category) where.category = category;
    if (importance) where.importance = { gte: Number(importance) };

    const memories = await prisma.tesaMemory.findMany({
      where,
      orderBy: [{ importance: 'desc' }, { updatedAt: 'desc' }],
      take: Number(limit),
    });

    return { memories, count: memories.length };
  },

  // Write a new memory
  'memory.write': async (params) => {
    const { category, key, value, context, importance = 5, confidence = 1.0, tags = [], source = 'mcp' } = params;

    if (!category || !key || !value) {
      throw { code: -32602, message: 'category, key, and value are required' };
    }

    // Check for existing
    const existing = await prisma.tesaMemory.findFirst({
      where: { key: String(key), isActive: true },
    });

    let memory;
    if (existing) {
      memory = await prisma.tesaMemory.update({
        where: { id: existing.id },
        data: {
          value: String(value),
          context: context ? String(context) : null,
          importance: Number(importance),
          confidence: Number(confidence),
          tags: tags as string[],
          source: String(source),
        },
      });
      return { memory, action: 'updated' };
    } else {
      memory = await prisma.tesaMemory.create({
        data: {
          category: String(category),
          key: String(key),
          value: String(value),
          context: context ? String(context) : null,
          importance: Number(importance),
          confidence: Number(confidence),
          tags: tags as string[],
          source: String(source),
        },
      });
      return { memory, action: 'created' };
    }
  },

  // Update existing memory
  'memory.update': async (params) => {
    const { id, ...updates } = params;
    
    if (!id) {
      throw { code: -32602, message: 'id is required' };
    }

    const data: Record<string, unknown> = {};
    if (updates.value !== undefined) data.value = String(updates.value);
    if (updates.context !== undefined) data.context = updates.context ? String(updates.context) : null;
    if (updates.importance !== undefined) data.importance = Number(updates.importance);
    if (updates.confidence !== undefined) data.confidence = Number(updates.confidence);
    if (updates.tags !== undefined) data.tags = updates.tags as string[];
    if (updates.isActive !== undefined) data.isActive = Boolean(updates.isActive);

    const memory = await prisma.tesaMemory.update({
      where: { id: String(id) },
      data,
    });

    return { memory };
  },

  // Delete (soft) a memory
  'memory.delete': async (params) => {
    const { id, hard = false } = params;
    
    if (!id) {
      throw { code: -32602, message: 'id is required' };
    }

    if (hard) {
      await prisma.tesaMemory.delete({ where: { id: String(id) } });
    } else {
      await prisma.tesaMemory.update({
        where: { id: String(id) },
        data: { isActive: false },
      });
    }

    return { success: true, hard };
  },

  // Full-text search
  'memory.search': async (params) => {
    const { query, categories, limit = 20 } = params;
    
    if (!query) {
      throw { code: -32602, message: 'query is required' };
    }

    const where: Record<string, unknown> = {
      isActive: true,
      OR: [
        { key: { contains: String(query), mode: 'insensitive' } },
        { value: { contains: String(query), mode: 'insensitive' } },
        { context: { contains: String(query), mode: 'insensitive' } },
      ],
    };

    if (categories && Array.isArray(categories)) {
      where.category = { in: categories as string[] };
    }

    const memories = await prisma.tesaMemory.findMany({
      where,
      orderBy: [{ importance: 'desc' }, { updatedAt: 'desc' }],
      take: Number(limit),
    });

    // Update access stats
    if (memories.length > 0) {
      await prisma.tesaMemory.updateMany({
        where: { id: { in: memories.map((m: any) => m.id) } },
        data: { lastAccessed: new Date(), accessCount: { increment: 1 } },
      });
    }

    return { memories, count: memories.length };
  },

  // Get stale/neglected items
  'memory.stale': async (params) => {
    const { days = 30, minImportance = 5 } = params;
    
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - Number(days));

    const staleMemories = await prisma.tesaMemory.findMany({
      where: {
        isActive: true,
        importance: { gte: Number(minImportance) },
        OR: [
          { lastAccessed: { lt: cutoffDate } },
          { lastAccessed: null },
        ],
        updatedAt: { lt: cutoffDate },
      },
      orderBy: { importance: 'desc' },
      take: 20,
    });

    return {
      staleMemories,
      count: staleMemories.length,
      cutoffDate: cutoffDate.toISOString(),
    };
  },

  // Cross-reference: find related memories
  'memory.crossref': async (params) => {
    const { id, keywords } = params;
    
    let targetMemory = null;
    let searchTerms: string[] = [];

    if (id) {
      targetMemory = await prisma.tesaMemory.findUnique({ where: { id: String(id) } });
      if (targetMemory) {
        // Extract keywords from the memory
        const text = `${targetMemory.key} ${targetMemory.value} ${targetMemory.context || ''}`;
        searchTerms = text.toLowerCase().split(/\s+/).filter(w => w.length > 3);
        // Add tags
        if (Array.isArray(targetMemory.tags)) {
          searchTerms.push(...(targetMemory.tags as string[]));
        }
      }
    }

    if (keywords && Array.isArray(keywords)) {
      searchTerms.push(...(keywords as string[]));
    }

    if (searchTerms.length === 0) {
      throw { code: -32602, message: 'id or keywords required' };
    }

    // Find related memories
    const related = await prisma.tesaMemory.findMany({
      where: {
        isActive: true,
        id: targetMemory ? { not: targetMemory.id } : undefined,
        OR: searchTerms.slice(0, 10).map(term => ({
          OR: [
            { key: { contains: term, mode: 'insensitive' } },
            { value: { contains: term, mode: 'insensitive' } },
          ],
        })),
      },
      orderBy: { importance: 'desc' },
      take: 10,
    });

    return {
      sourceMemory: targetMemory,
      relatedMemories: related,
      searchTerms: searchTerms.slice(0, 10),
    };
  },

  // Get memory statistics
  'memory.stats': async () => {
    const [total, byCategory, byImportance, recentActivity] = await Promise.all([
      prisma.tesaMemory.count({ where: { isActive: true } }),
      prisma.tesaMemory.groupBy({
        by: ['category'],
        where: { isActive: true },
        _count: { category: true },
      }),
      prisma.tesaMemory.groupBy({
        by: ['importance'],
        where: { isActive: true },
        _count: { importance: true },
      }),
      prisma.tesaMemory.findMany({
        where: { isActive: true },
        orderBy: { updatedAt: 'desc' },
        take: 5,
        select: { id: true, key: true, updatedAt: true },
      }),
    ]);

    return {
      total,
      byCategory: Object.fromEntries(byCategory.map((c: any) => [c.category, c._count.category])),
      byImportance: Object.fromEntries(byImportance.map((i: any) => [i.importance, i._count.importance])),
      recentActivity,
    };
  },
};

export async function POST(request: NextRequest) {
  // Validate auth
  const isAuthed = await validateAuth(request);
  if (!isAuthed) {
    return NextResponse.json(
      { jsonrpc: '2.0', error: { code: -32000, message: 'Unauthorized' }, id: null },
      { status: 401 }
    );
  }

  try {
    const body: MCPRequest = await request.json();
    const { jsonrpc, id, method, params = {} } = body;

    // Validate JSON-RPC format
    if (jsonrpc !== '2.0') {
      return NextResponse.json({
        jsonrpc: '2.0',
        error: { code: -32600, message: 'Invalid Request: jsonrpc must be "2.0"' },
        id: id || null,
      });
    }

    // Check if method exists
    if (!methods[method]) {
      return NextResponse.json({
        jsonrpc: '2.0',
        error: { code: -32601, message: `Method not found: ${method}` },
        id,
      });
    }

    // Execute method
    try {
      const result = await methods[method](params as Record<string, unknown>);
      return NextResponse.json({ jsonrpc: '2.0', result, id } as MCPResponse);
    } catch (methodError: unknown) {
      const err = methodError as { code?: number; message?: string };
      return NextResponse.json({
        jsonrpc: '2.0',
        error: {
          code: err.code || -32603,
          message: err.message || 'Internal error',
        },
        id,
      });
    }
  } catch (error) {
    console.error('MCP Server Error:', error);
    return NextResponse.json({
      jsonrpc: '2.0',
      error: { code: -32700, message: 'Parse error' },
      id: null,
    });
  }
}

// GET endpoint for capability discovery
export async function GET() {
  return NextResponse.json({
    name: 'TesaMemory MCP Server',
    version: MCP_VERSION,
    description: 'Model Context Protocol server for Tesa AI Agent Memory',
    endpoints: {
      mcp: '/api/tesa/mcp',
      memory: '/api/tesa/memory',
      context: '/api/tesa/memory/context',
    },
    methods: Object.keys(methods),
    auth: {
      required: !!process.env.TESA_MCP_API_KEY,
      methods: ['Bearer token', 'X-API-Key header'],
    },
  });
}
