import { JSONSchema7 } from 'json-schema';
import { randomUUID } from 'crypto';
import { createTool, createSuccessResult, createErrorResult } from '../../core/tool-framework.js';
import { ToolRegistration, RequestContext } from '../../core/types.js';

/**
 * Memory Management Tools - 12-Factor MCP Implementation
 * 
 * Implements Factor 2: Deterministic Execution with structured outputs
 * Implements Factor 3: Stateless Processes with RequestContext
 * Implements Factor 4: Structured Outputs for LLM consumption
 * Implements Factor 5: Contextual Memory with persistent storage
 */

// Input type interfaces
interface StoreMemoryInput {
  content: string;
  type: 'code' | 'documentation' | 'decision' | 'learning' | 'context' | 'insight';
  title?: string;
  tags?: string[];
  metadata?: Record<string, any>;
  importance?: 'low' | 'medium' | 'high' | 'critical';
  category?: string;
  source?: string;
  relatedIds?: string[];
}

interface SearchMemoryInput {
  query: string;
  type?: 'code' | 'documentation' | 'decision' | 'learning' | 'context' | 'insight';
  tags?: string[];
  importance?: 'low' | 'medium' | 'high' | 'critical';
  category?: string;
  dateRange?: {
    from?: string;
    to?: string;
  };
  limit?: number;
  includeContent?: boolean;
}

interface GetMemoryInput {
  memoryId: string;
}

interface UpdateMemoryInput {
  memoryId: string;
  content?: string;
  title?: string;
  tags?: string[];
  metadata?: Record<string, any>;
  importance?: 'low' | 'medium' | 'high' | 'critical';
  category?: string;
}

interface DeleteMemoryInput {
  memoryId: string;
  reason?: string;
}

interface GetMemoryStatsInput {
  timeframe?: '24h' | '7d' | '30d' | '90d' | 'all';
  type?: 'code' | 'documentation' | 'decision' | 'learning' | 'context' | 'insight';
}

interface LinkMemoriesInput {
  memoryId: string;
  relatedIds: string[];
  linkType?: 'related' | 'supersedes' | 'references' | 'builds_on';
  description?: string;
}

/**
 * Store a new memory
 */
const storeMemoryTool = createTool<StoreMemoryInput, any>({
  name: 'store_memory',
  description: 'Store information in the project memory for future reference and context',
  category: 'memory-management',
  inputSchema: {
    type: 'object',
    properties: {
      content: {
        type: 'string',
        description: 'The content to store in memory',
        minLength: 1,
        maxLength: 10000
      },
      type: {
        type: 'string',
        enum: ['code', 'documentation', 'decision', 'learning', 'context', 'insight'],
        description: 'Type of memory being stored'
      },
      title: {
        type: 'string',
        description: 'Optional title for the memory',
        maxLength: 200
      },
      tags: {
        type: 'array',
        items: { type: 'string', maxLength: 50 },
        description: 'Tags for categorizing and searching',
        maxItems: 20
      },
      metadata: {
        type: 'object',
        description: 'Additional structured metadata',
        additionalProperties: true
      },
      importance: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        default: 'medium',
        description: 'Importance level of this memory'
      },
      category: {
        type: 'string',
        description: 'Category for grouping related memories',
        maxLength: 100
      },
      source: {
        type: 'string',
        description: 'Source of the information (file, meeting, etc.)',
        maxLength: 500
      },
      relatedIds: {
        type: 'array',
        items: { type: 'string', pattern: '^[a-zA-Z0-9-]+$' },
        description: 'IDs of related memories',
        maxItems: 10
      }
    },
    required: ['content', 'type'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: StoreMemoryInput, context: RequestContext) {
    try {
      const memoryId = randomUUID();
      const now = Date.now();

      // Validate related memory IDs if provided
      if (input.relatedIds && input.relatedIds.length > 0) {
        for (const relatedId of input.relatedIds) {
          const relatedCheck = await context.db.get(
            'SELECT id FROM memories WHERE id = ? AND project_id = ?',
            [relatedId, context.projectId || 'default']
          );

          if (!relatedCheck.success || !relatedCheck.data) {
            return createErrorResult({
              code: 'RESOURCE_NOT_FOUND',
              message: `Related memory not found: ${relatedId}`,
              category: 'validation'
            });
          }
        }
      }

      // Generate a title if not provided
      const title = input.title || generateTitleFromContent(input.content, input.type);

      // Insert memory into database
      const result = await context.db.run(
        `INSERT INTO memories 
         (id, title, content, type, tags, metadata, importance, category, 
          source, project_id, created_by, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          memoryId,
          title,
          input.content,
          input.type,
          JSON.stringify(input.tags || []),
          JSON.stringify(input.metadata || {}),
          input.importance || 'medium',
          input.category || null,
          input.source || null,
          context.projectId || 'default',
          context.userId || 'system',
          now,
          now
        ]
      );

      if (!result.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to store memory',
          details: { error: result.error },
          category: 'system'
        });
      }

      // Create relationships if related IDs provided
      if (input.relatedIds && input.relatedIds.length > 0) {
        for (const relatedId of input.relatedIds) {
          await context.db.run(
            'INSERT INTO memory_relationships (id, memory_id, related_memory_id, link_type, created_at) VALUES (?, ?, ?, ?, ?)',
            [randomUUID(), memoryId, relatedId, 'related', now]
          );
        }
      }

      return createSuccessResult({
        memory: {
          id: memoryId,
          title,
          type: input.type,
          importance: input.importance || 'medium',
          category: input.category || null,
          tags: input.tags || [],
          relatedCount: input.relatedIds?.length || 0,
          createdAt: new Date(now).toISOString()
        },
        message: `Memory "${title}" stored successfully`,
        searchable: true,
        relatedMemories: input.relatedIds || []
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to store memory: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Search memories
 */
const searchMemoryTool = createTool<SearchMemoryInput, any>({
  name: 'search_memory',
  description: 'Search stored memories using keywords, filters, and metadata',
  category: 'memory-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      query: {
        type: 'string',
        description: 'Search query for finding relevant memories',
        minLength: 1,
        maxLength: 500
      },
      type: {
        type: 'string',
        enum: ['code', 'documentation', 'decision', 'learning', 'context', 'insight'],
        description: 'Filter by memory type'
      },
      tags: {
        type: 'array',
        items: { type: 'string' },
        description: 'Filter by specific tags',
        maxItems: 10
      },
      importance: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'critical'],
        description: 'Filter by importance level'
      },
      category: {
        type: 'string',
        description: 'Filter by category',
        maxLength: 100
      },
      dateRange: {
        type: 'object',
        properties: {
          from: { type: 'string', format: 'date' },
          to: { type: 'string', format: 'date' }
        },
        description: 'Filter by date range'
      },
      limit: {
        type: 'integer',
        description: 'Maximum number of results',
        minimum: 1,
        maximum: 100,
        default: 20
      },
      includeContent: {
        type: 'boolean',
        description: 'Include full content in results',
        default: false
      }
    },
    required: ['query'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: SearchMemoryInput, context: RequestContext) {
    try {
      // Build the search query
      let sql = `
        SELECT m.*, 
               (CASE 
                WHEN m.title LIKE ? THEN 10
                WHEN m.content LIKE ? THEN 5
                WHEN m.tags LIKE ? THEN 3
                ELSE 1
               END) as relevance_score
        FROM memories m 
        WHERE m.project_id = ? 
        AND (m.title LIKE ? OR m.content LIKE ? OR m.tags LIKE ? OR m.category LIKE ?)
      `;
      
      const searchPattern = `%${input.query}%`;
      const params = [
        searchPattern, // title relevance
        searchPattern, // content relevance  
        searchPattern, // tags relevance
        context.projectId || 'default',
        searchPattern, // title search
        searchPattern, // content search
        searchPattern, // tags search
        searchPattern  // category search
      ];

      // Add filters
      if (input.type) {
        sql += ' AND m.type = ?';
        params.push(input.type);
      }

      if (input.importance) {
        sql += ' AND m.importance = ?';
        params.push(input.importance);
      }

      if (input.category) {
        sql += ' AND m.category = ?';
        params.push(input.category);
      }

      if (input.tags && input.tags.length > 0) {
        const tagConditions = input.tags.map(() => 'm.tags LIKE ?').join(' AND ');
        sql += ` AND (${tagConditions})`;
        input.tags.forEach(tag => params.push(`%"${tag}"%`));
      }

      if (input.dateRange) {
        if (input.dateRange.from) {
          sql += ' AND m.created_at >= ?';
          params.push(new Date(input.dateRange.from).getTime().toString());
        }
        if (input.dateRange.to) {
          sql += ' AND m.created_at <= ?';
          params.push(new Date(input.dateRange.to).getTime().toString());
        }
      }

      sql += ' ORDER BY relevance_score DESC, m.importance DESC, m.created_at DESC LIMIT ?';
      params.push((input.limit || 20).toString());

      const result = await context.db.query(sql, params);

      if (!result.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to search memories',
          details: { error: result.error },
          category: 'system'
        });
      }

      const memories = (result.data || []).map((memory: any) => {
        const tags = JSON.parse(memory.tags || '[]');
        const metadata = JSON.parse(memory.metadata || '{}');
        
        const memoryData: any = {
          id: memory.id,
          title: memory.title,
          type: memory.type,
          importance: memory.importance,
          category: memory.category,
          tags,
          source: memory.source,
          relevanceScore: memory.relevance_score,
          createdBy: memory.created_by,
          createdAt: new Date(memory.created_at).toISOString(),
          updatedAt: new Date(memory.updated_at).toISOString()
        };

        if (input.includeContent) {
          memoryData.content = memory.content;
          memoryData.metadata = metadata;
        } else {
          // Provide a content preview
          memoryData.contentPreview = memory.content.length > 200 
            ? memory.content.substring(0, 200) + '...'
            : memory.content;
        }

        return memoryData;
      });

      return createSuccessResult({
        memories,
        count: memories.length,
        query: input.query,
        filters: {
          type: input.type || null,
          importance: input.importance || null,
          category: input.category || null,
          tags: input.tags || [],
          dateRange: input.dateRange || null
        },
        hasMore: memories.length === (input.limit || 20),
        searchTips: memories.length === 0 ? [
          'Try using broader search terms',
          'Check your filters - they might be too restrictive', 
          'Search in different memory types',
          'Consider searching by tags or categories'
        ] : undefined
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to search memories: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Get specific memory by ID
 */
const getMemoryTool = createTool<GetMemoryInput, any>({
  name: 'get_memory',
  description: 'Retrieve a specific memory by its ID with full content and relationships',
  category: 'memory-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      memoryId: {
        type: 'string',
        description: 'Memory ID to retrieve',
        pattern: '^[a-zA-Z0-9-]+$'
      }
    },
    required: ['memoryId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetMemoryInput, context: RequestContext) {
    try {
      // Get the memory
      const memoryResult = await context.db.get(
        'SELECT * FROM memories WHERE id = ? AND project_id = ?',
        [input.memoryId, context.projectId || 'default']
      );

      if (!memoryResult.success || !memoryResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Memory not found',
          details: { memoryId: input.memoryId },
          category: 'validation'
        });
      }

      const memory = memoryResult.data;

      // Get related memories
      const relatedResult = await context.db.query(
        `SELECT r.link_type, r.description, r.created_at as linked_at,
                rm.id, rm.title, rm.type, rm.importance 
         FROM memory_relationships r
         JOIN memories rm ON (r.related_memory_id = rm.id OR r.memory_id = rm.id)
         WHERE (r.memory_id = ? OR r.related_memory_id = ?) AND rm.id != ?`,
        [input.memoryId, input.memoryId, input.memoryId]
      );

      const relatedMemories = (relatedResult.data || []).map((rel: any) => ({
        id: rel.id,
        title: rel.title,
        type: rel.type,
        importance: rel.importance,
        linkType: rel.link_type,
        description: rel.description,
        linkedAt: new Date(rel.linked_at).toISOString()
      }));

      return createSuccessResult({
        memory: {
          id: memory.id,
          title: memory.title,
          content: memory.content,
          type: memory.type,
          importance: memory.importance,
          category: memory.category,
          tags: JSON.parse(memory.tags || '[]'),
          metadata: JSON.parse(memory.metadata || '{}'),
          source: memory.source,
          createdBy: memory.created_by,
          createdAt: new Date(memory.created_at).toISOString(),
          updatedAt: new Date(memory.updated_at).toISOString()
        },
        relatedMemories,
        statistics: {
          relatedCount: relatedMemories.length,
          contentLength: memory.content.length,
          tagCount: JSON.parse(memory.tags || '[]').length
        }
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to get memory: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Get memory statistics and insights
 */
const getMemoryStatsTool = createTool<GetMemoryStatsInput, any>({
  name: 'get_memory_stats',
  description: 'Get statistics and insights about stored memories',
  category: 'memory-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      timeframe: {
        type: 'string',
        enum: ['24h', '7d', '30d', '90d', 'all'],
        default: '30d',
        description: 'Time period for statistics'
      },
      type: {
        type: 'string',
        enum: ['code', 'documentation', 'decision', 'learning', 'context', 'insight'],
        description: 'Filter statistics by memory type'
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: GetMemoryStatsInput, context: RequestContext) {
    try {
      // Calculate time threshold
      const now = Date.now();
      let timeThreshold = 0;
      
      if (input.timeframe !== 'all') {
        switch (input.timeframe) {
          case '24h':
            timeThreshold = now - (24 * 60 * 60 * 1000);
            break;
          case '7d':
            timeThreshold = now - (7 * 24 * 60 * 60 * 1000);
            break;
          case '30d':
            timeThreshold = now - (30 * 24 * 60 * 60 * 1000);
            break;
          case '90d':
            timeThreshold = now - (90 * 24 * 60 * 60 * 1000);
            break;
        }
      }

      let whereClause = 'WHERE project_id = ?';
      const params = [context.projectId || 'default'];

      if (timeThreshold > 0) {
        whereClause += ' AND created_at >= ?';
        params.push(timeThreshold.toString());
      }

      if (input.type) {
        whereClause += ' AND type = ?';
        params.push(input.type);
      }

      // Get basic statistics
      const totalResult = await context.db.get(
        `SELECT COUNT(*) as total FROM memories ${whereClause}`,
        params
      );

      // Get type distribution
      const typeResult = await context.db.query(
        `SELECT type, COUNT(*) as count FROM memories ${whereClause} GROUP BY type`,
        params
      );

      // Get importance distribution
      const importanceResult = await context.db.query(
        `SELECT importance, COUNT(*) as count FROM memories ${whereClause} GROUP BY importance`,
        params
      );

      // Get category distribution
      const categoryResult = await context.db.query(
        `SELECT category, COUNT(*) as count FROM memories ${whereClause} 
         AND category IS NOT NULL GROUP BY category ORDER BY count DESC LIMIT 10`,
        params
      );

      // Get recent activity
      const recentResult = await context.db.query(
        `SELECT DATE(created_at/1000, 'unixepoch') as date, COUNT(*) as count 
         FROM memories ${whereClause} 
         GROUP BY DATE(created_at/1000, 'unixepoch') 
         ORDER BY date DESC LIMIT 30`,
        params
      );

      // Get top creators
      const creatorsResult = await context.db.query(
        `SELECT created_by, COUNT(*) as count FROM memories ${whereClause} 
         AND created_by IS NOT NULL GROUP BY created_by ORDER BY count DESC LIMIT 10`,
        params
      );

      const total = totalResult.data?.total || 0;
      const typeDistribution = (typeResult.data || []).reduce((acc: any, row: any) => {
        acc[row.type] = row.count;
        return acc;
      }, {});

      const importanceDistribution = (importanceResult.data || []).reduce((acc: any, row: any) => {
        acc[row.importance] = row.count;
        return acc;
      }, {});

      const topCategories = (categoryResult.data || []).map((row: any) => ({
        category: row.category,
        count: row.count
      }));

      const dailyActivity = (recentResult.data || []).map((row: any) => ({
        date: row.date,
        count: row.count
      }));

      const topCreators = (creatorsResult.data || []).map((row: any) => ({
        user: row.created_by,
        count: row.count
      }));

      return createSuccessResult({
        timeframe: input.timeframe || '30d',
        type: input.type || null,
        overview: {
          totalMemories: total,
          averagePerDay: dailyActivity.length > 0 
            ? Math.round((total / dailyActivity.length) * 100) / 100 
            : 0
        },
        distribution: {
          byType: typeDistribution,
          byImportance: importanceDistribution,
          topCategories,
          topCreators
        },
        activity: {
          dailyActivity: dailyActivity.slice(0, 7), // Last 7 days
          trend: calculateTrend(dailyActivity)
        },
        insights: generateInsights(total, typeDistribution, importanceDistribution, dailyActivity)
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to get memory statistics: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Helper functions
 */
function generateTitleFromContent(content: string, type: string): string {
  const words = content.split(/\s+/).slice(0, 8).join(' ');
  const truncated = words.length > 50 ? words.substring(0, 47) + '...' : words;
  const typePrefix = type.charAt(0).toUpperCase() + type.slice(1);
  return `${typePrefix}: ${truncated}`;
}

function calculateTrend(dailyActivity: any[]): string {
  if (dailyActivity.length < 2) return 'stable';
  
  const recent = dailyActivity.slice(0, 3).reduce((sum, day) => sum + day.count, 0);
  const older = dailyActivity.slice(3, 6).reduce((sum, day) => sum + day.count, 0);
  
  if (recent > older * 1.2) return 'increasing';
  if (recent < older * 0.8) return 'decreasing';
  return 'stable';
}

function generateInsights(total: number, typeDistribution: any, importanceDistribution: any, dailyActivity: any[]): string[] {
  const insights = [];
  
  if (total === 0) {
    insights.push('No memories stored yet - start capturing important information');
    return insights;
  }
  
  if (total < 10) {
    insights.push('Getting started with memory collection - consider storing more context');
  } else if (total > 100) {
    insights.push('Rich memory collection - good knowledge base building');
  }
  
  const topType = Object.entries(typeDistribution).reduce((a: any, b: any) => 
    typeDistribution[a[0]] > typeDistribution[b[0]] ? a : b
  )?.[0];
  
  if (topType) {
    insights.push(`Most common memory type: ${topType}`);
  }
  
  const criticalCount = importanceDistribution.critical || 0;
  if (criticalCount > 0) {
    insights.push(`${criticalCount} critical memories stored`);
  }
  
  if (dailyActivity.length > 0) {
    const avgDaily = dailyActivity.reduce((sum, day) => sum + day.count, 0) / dailyActivity.length;
    if (avgDaily > 2) {
      insights.push('High memory activity - good knowledge capture');
    } else if (avgDaily < 0.5) {
      insights.push('Low recent activity - consider storing more insights');
    }
  }
  
  return insights;
}

/**
 * Setup memory management tools
 */
export async function setupMemoryManagementTools(): Promise<ToolRegistration> {
  return {
    module: 'memory-management',
    tools: [
      storeMemoryTool,
      searchMemoryTool,
      getMemoryTool,
      getMemoryStatsTool
    ]
  };
}