import { getRegisteredContainer } from '@bytelyst/cosmos';
import type {
  ErrorClusterDoc,
  ErrorFingerprint,
  DiagnosticInsightDoc,
  NaturalLanguageQueryDoc,
  ProactiveAlert,
} from './types.js';

// ============================================================================
// Container Access
// ============================================================================

function getErrorClustersContainer() {
  return getRegisteredContainer('error_clusters');
}

function getErrorFingerprintsContainer() {
  return getRegisteredContainer('error_fingerprints');
}

function getDiagnosticInsightsContainer() {
  return getRegisteredContainer('diagnostic_insights');
}

function getDiagnosticQueriesContainer() {
  return getRegisteredContainer('diagnostic_queries');
}

function getProactiveAlertsContainer() {
  return getRegisteredContainer('proactive_alerts');
}

// ============================================================================
// Error Cluster Repository
// ============================================================================

export async function createErrorCluster(cluster: ErrorClusterDoc): Promise<ErrorClusterDoc> {
  const container = getErrorClustersContainer();
  const { resource } = await container.items.create(cluster);
  return resource as ErrorClusterDoc;
}

export async function getErrorClusterById(
  clusterId: string,
  productId: string
): Promise<ErrorClusterDoc | null> {
  const container = getErrorClustersContainer();
  try {
    const { resource } = await container.item(clusterId, productId).read();
    return resource as ErrorClusterDoc | null;
  } catch {
    return null;
  }
}

export async function updateErrorCluster(cluster: ErrorClusterDoc): Promise<ErrorClusterDoc> {
  const container = getErrorClustersContainer();
  const { resource } = await container.items.upsert(cluster);
  return resource as unknown as ErrorClusterDoc;
}

export async function findClustersByProduct(
  productId: string,
  options: {
    status?: 'active' | 'investigating' | 'resolved' | 'ignored';
    minOccurrences?: number;
    limit?: number;
  } = {}
): Promise<ErrorClusterDoc[]> {
  const container = getErrorClustersContainer();

  let query = 'SELECT * FROM c WHERE c.productId = @productId';
  const parameters: Array<{ name: string; value: string | number }> = [
    {
      name: '@productId',
      value: productId,
    },
  ];

  if (options.status) {
    query += ' AND c.status = @status';
    parameters.push({ name: '@status', value: options.status });
  }

  if (options.minOccurrences) {
    query += ' AND c.occurrenceCount >= @minOccurrences';
    parameters.push({ name: '@minOccurrences', value: options.minOccurrences });
  }

  query += ' ORDER BY c.occurrenceCount DESC';

  const { resources } = await container.items
    .query({ query, parameters }, { maxItemCount: options.limit || 100 })
    .fetchAll();

  return resources as ErrorClusterDoc[];
}

// ============================================================================
// Vector Search (Cosine Similarity)
// ============================================================================

interface VectorSearchResult {
  cluster: ErrorClusterDoc;
  similarity: number;
}

/**
 * Performs vector search using cosine similarity in Cosmos DB
 * Note: This is a client-side implementation as Cosmos DB doesn't natively
 * support vector search yet. For production scale, consider:
 * - Azure Cognitive Search with vector capability
 * - Redis Vector Similarity Search
 * - PostgreSQL with pgvector
 */
export async function searchSimilarClusters(
  productId: string,
  queryEmbedding: number[],
  options: {
    limit?: number;
    threshold?: number;
    excludeClusterId?: string;
  } = {}
): Promise<VectorSearchResult[]> {
  const container = getErrorClustersContainer();
  const limit = options.limit || 10;
  const threshold = options.threshold || 0.75;

  // Fetch clusters with embeddings (limited set for performance)
  const query = `
    SELECT * FROM c 
    WHERE c.productId = @productId 
    AND IS_DEFINED(c.embedding)
    AND c.status != 'ignored'
    ${options.excludeClusterId ? 'AND c.id != @excludeId' : ''}
  `;

  const parameters = [{ name: '@productId', value: productId }];
  if (options.excludeClusterId) {
    parameters.push({ name: '@excludeId', value: options.excludeClusterId });
  }

  const { resources } = await container.items
    .query({ query, parameters }, { maxItemCount: 1000 })
    .fetchAll();

  const clusters = resources as ErrorClusterDoc[];

  // Calculate cosine similarity for each cluster
  const results: VectorSearchResult[] = clusters
    .map(cluster => ({
      cluster,
      similarity: cluster.embedding ? cosineSimilarity(queryEmbedding, cluster.embedding) : 0,
    }))
    .filter(result => result.similarity >= threshold)
    .sort((a, b) => b.similarity - a.similarity)
    .slice(0, limit);

  return results;
}

/**
 * Calculates cosine similarity between two vectors
 */
function cosineSimilarity(a: number[], b: number[]): number {
  if (a.length !== b.length) {
    throw new Error('Vectors must have same dimensions');
  }

  let dotProduct = 0;
  let normA = 0;
  let normB = 0;

  for (let i = 0; i < a.length; i++) {
    dotProduct += a[i] * b[i];
    normA += a[i] * a[i];
    normB += b[i] * b[i];
  }

  if (normA === 0 || normB === 0) {
    return 0;
  }

  return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}

/**
 * Finds clusters by embedding similarity to a given cluster
 */
export async function findRelatedClusters(
  clusterId: string,
  productId: string,
  options: {
    limit?: number;
    threshold?: number;
  } = {}
): Promise<ErrorClusterDoc[]> {
  // Get the source cluster
  const sourceCluster = await getErrorClusterById(clusterId, productId);
  if (!sourceCluster?.embedding) {
    return [];
  }

  // Search for similar clusters
  const results = await searchSimilarClusters(productId, sourceCluster.embedding, {
    limit: options.limit || 5,
    threshold: options.threshold || 0.8,
    excludeClusterId: clusterId,
  });

  return results.map(r => r.cluster);
}

// ============================================================================
// Error Fingerprint Repository
// ============================================================================

export async function getFingerprintByHash(
  fingerprintHash: string
): Promise<ErrorFingerprint | null> {
  const container = getErrorFingerprintsContainer();
  try {
    const { resource } = await container.item(fingerprintHash, fingerprintHash).read();
    return resource as ErrorFingerprint | null;
  } catch {
    return null;
  }
}

export async function saveFingerprint(fingerprint: ErrorFingerprint): Promise<ErrorFingerprint> {
  const container = getErrorFingerprintsContainer();
  const { resource } = await container.items.upsert(fingerprint);
  return resource as unknown as ErrorFingerprint;
}

// ============================================================================
// Diagnostic Insight Repository
// ============================================================================

export async function createDiagnosticInsight(
  insight: DiagnosticInsightDoc
): Promise<DiagnosticInsightDoc> {
  const container = getDiagnosticInsightsContainer();
  const { resource } = await container.items.create(insight);
  return resource as DiagnosticInsightDoc;
}

export async function getDiagnosticInsightById(
  insightId: string,
  clusterId: string
): Promise<DiagnosticInsightDoc | null> {
  const container = getDiagnosticInsightsContainer();
  try {
    const { resource } = await container.item(insightId, clusterId).read();
    return resource as DiagnosticInsightDoc | null;
  } catch {
    return null;
  }
}

export async function getLatestInsightForCluster(
  clusterId: string,
  productId: string
): Promise<DiagnosticInsightDoc | null> {
  const container = getDiagnosticInsightsContainer();

  const query = `
    SELECT * FROM c 
    WHERE c.clusterId = @clusterId AND c.productId = @productId
    ORDER BY c.generatedAt DESC
    OFFSET 0 LIMIT 1
  `;

  const { resources } = await container.items
    .query({
      query,
      parameters: [
        { name: '@clusterId', value: clusterId },
        { name: '@productId', value: productId },
      ],
    })
    .fetchAll();

  return (resources[0] as DiagnosticInsightDoc) || null;
}

export async function updateInsightFeedback(
  insightId: string,
  clusterId: string,
  feedback: { helpful?: boolean; note?: string }
): Promise<void> {
  const container = getDiagnosticInsightsContainer();

  const insight = await getDiagnosticInsightById(insightId, clusterId);
  if (!insight) return;

  const feedbackStats = insight.feedbackStats || { helpful: 0, notHelpful: 0, engineerNotes: [] };

  if (feedback.helpful === true) {
    feedbackStats.helpful++;
  } else if (feedback.helpful === false) {
    feedbackStats.notHelpful++;
  }

  if (feedback.note) {
    feedbackStats.engineerNotes.push(feedback.note);
  }

  await container.items.upsert({
    ...insight,
    feedbackStats,
    updatedAt: new Date().toISOString(),
  });
}

// ============================================================================
// Natural Language Query Repository
// ============================================================================

export async function saveNaturalLanguageQuery(
  query: NaturalLanguageQueryDoc
): Promise<NaturalLanguageQueryDoc> {
  const container = getDiagnosticQueriesContainer();
  const { resource } = await container.items.create(query);
  return resource as NaturalLanguageQueryDoc;
}

export async function getQueryHistory(
  userId: string,
  options: { limit?: number } = {}
): Promise<NaturalLanguageQueryDoc[]> {
  const container = getDiagnosticQueriesContainer();

  const query = `
    SELECT * FROM c 
    WHERE c.userId = @userId
    ORDER BY c.createdAt DESC
    OFFSET 0 LIMIT @limit
  `;

  const { resources } = await container.items
    .query({
      query,
      parameters: [
        { name: '@userId', value: userId },
        { name: '@limit', value: options.limit || 20 },
      ],
    })
    .fetchAll();

  return resources as NaturalLanguageQueryDoc[];
}

// ============================================================================
// Proactive Alert Repository
// ============================================================================

export async function createProactiveAlert(alert: ProactiveAlert): Promise<ProactiveAlert> {
  const container = getProactiveAlertsContainer();
  const { resource } = await container.items.create(alert);
  return resource as ProactiveAlert;
}

export async function getActiveAlerts(
  productId: string | undefined,
  options?: {
    acknowledged?: boolean;
    severity?: 'critical' | 'high' | 'medium' | 'low';
    limit?: number;
  }
): Promise<ProactiveAlert[]> {
  const container = getProactiveAlertsContainer();

  let whereClause = 'NOT IS_DEFINED(c.resolvedAt)';

  if (productId) {
    whereClause += ' AND c.productId = @productId';
  }

  if (options?.acknowledged === false) {
    whereClause += ' AND NOT IS_DEFINED(c.acknowledgedAt)';
  } else if (options?.acknowledged === true) {
    whereClause += ' AND IS_DEFINED(c.acknowledgedAt)';
  }

  if (options?.severity) {
    whereClause += ' AND c.severity = @severity';
  }

  const parameters: Array<{ name: string; value: string | number | boolean }> = [];
  if (productId) {
    parameters.push({ name: '@productId', value: productId });
  }
  if (options?.severity) {
    parameters.push({ name: '@severity', value: options.severity });
  }

  const query = `
    SELECT * FROM c 
    WHERE ${whereClause}
    ORDER BY 
      CASE c.severity
        WHEN 'critical' THEN 1
        WHEN 'high' THEN 2
        WHEN 'medium' THEN 3
        WHEN 'low' THEN 4
        ELSE 5
      END,
      c.createdAt DESC
    OFFSET 0 LIMIT @limit
  `;

  parameters.push({ name: '@limit', value: options?.limit ?? 50 });

  const { resources } = await container.items
    .query({
      query,
      parameters,
    })
    .fetchAll();

  return resources as ProactiveAlert[];
}

export async function acknowledgeAlert(
  alertId: string,
  options: {
    acknowledgedBy: string;
    note?: string;
  }
): Promise<void> {
  const container = getProactiveAlertsContainer();

  try {
    // Query to find the alert by id (cross-partition query)
    const { resources } = await container.items
      .query({
        query: 'SELECT * FROM c WHERE c.id = @id',
        parameters: [{ name: '@id', value: alertId }],
      })
      .fetchAll();

    if (resources.length === 0) return;

    const alert = resources[0] as ProactiveAlert;

    await container.items.upsert({
      ...alert,
      acknowledgedAt: new Date().toISOString(),
      acknowledgedBy: options.acknowledgedBy,
      acknowledgementNote: options.note,
    });
  } catch {
    // Alert not found
  }
}

export async function resolveAlert(alertId: string): Promise<void> {
  const container = getProactiveAlertsContainer();

  try {
    // Query to find the alert by id (cross-partition query)
    const { resources } = await container.items
      .query({
        query: 'SELECT * FROM c WHERE c.id = @id',
        parameters: [{ name: '@id', value: alertId }],
      })
      .fetchAll();

    if (resources.length === 0) return;

    const alert = resources[0] as ProactiveAlert;

    await container.items.upsert({
      ...alert,
      resolvedAt: new Date().toISOString(),
    });
  } catch {
    // Alert not found
  }
}

// ============================================================================
// Analytics Queries
// ============================================================================

export async function getClusterTrends(
  productId: string,
  timeRange: { start: string; end: string }
): Promise<
  Array<{
    clusterId: string;
    errorType: string;
    firstSeenAt: string;
    lastSeenAt: string;
    occurrenceCount: number;
    uniqueUsers: number;
  }>
> {
  const container = getErrorClustersContainer();

  const query = `
    SELECT 
      c.id as clusterId,
      c.errorType,
      c.firstSeenAt,
      c.lastSeenAt,
      c.occurrenceCount,
      c.uniqueUsers
    FROM c 
    WHERE c.productId = @productId
    AND c.lastSeenAt >= @start
    AND c.lastSeenAt <= @end
    AND c.status != 'ignored'
    ORDER BY c.occurrenceCount DESC
  `;

  const { resources } = await container.items
    .query({
      query,
      parameters: [
        { name: '@productId', value: productId },
        { name: '@start', value: timeRange.start },
        { name: '@end', value: timeRange.end },
      ],
    })
    .fetchAll();

  return resources as Array<{
    clusterId: string;
    errorType: string;
    firstSeenAt: string;
    lastSeenAt: string;
    occurrenceCount: number;
    uniqueUsers: number;
  }>;
}

export async function getTopErrorTypes(
  productId: string,
  limit: number = 10
): Promise<Array<{ errorType: string; count: number; totalOccurrences: number }>> {
  const container = getErrorClustersContainer();

  const query = `
    SELECT 
      c.errorType,
      COUNT(1) as count,
      SUM(c.occurrenceCount) as totalOccurrences
    FROM c 
    WHERE c.productId = @productId
    AND c.status = 'active'
    GROUP BY c.errorType
    ORDER BY totalOccurrences DESC
    OFFSET 0 LIMIT @limit
  `;

  const { resources } = await container.items
    .query({
      query,
      parameters: [
        { name: '@productId', value: productId },
        { name: '@limit', value: limit },
      ],
    })
    .fetchAll();

  return resources as Array<{ errorType: string; count: number; totalOccurrences: number }>;
}
