/* eslint-disable @typescript-eslint/no-unused-vars */
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { z } from 'zod';
import { UnauthorizedError } from '../../lib/errors.js';
import * as repository from './repository.js';
import { parseQuery, validateQuery, generateQuerySuggestions } from './query-parser.js';
import { executeQuery } from './query-executor.js';
import { analyzeRootCause, generatePatternSummary } from './llm-analyzer.js';
import { aggregateClusterContext } from './telemetry-linking.js';
import type { ErrorClusterDoc, DiagnosticInsightDoc } from './types.js';

// Auth helper
function requireAdmin(req: { jwtPayload?: { role?: string } }): void {
  if (req.jwtPayload?.role !== 'admin') {
    throw new UnauthorizedError('Admin access required');
  }
}

// ============================================================================
// Request/Response Schemas
// ============================================================================

const QueryRequestSchema = z.object({
  query: z.string().min(1),
  productId: z.string().optional(),
  timeRange: z
    .object({
      start: z.string(),
      end: z.string(),
    })
    .optional(),
});

const FeedbackRequestSchema = z.object({
  insightId: z.string(),
  clusterId: z.string(),
  rating: z.enum(['helpful', 'not_helpful']),
  note: z.string().optional(),
});

const AnalyzeClusterRequestSchema = z.object({
  analysisType: z.enum(['root_cause', 'pattern', 'comparison', 'trend']).default('root_cause'),
  modelPreference: z.enum(['gpt-4o-mini', 'gpt-4o']).default('gpt-4o-mini'),
});

const SearchRequestSchema = z.object({
  query: z.string(),
  productId: z.string().optional(),
  limit: z.number().default(10),
  threshold: z.number().default(0.75),
});

// ============================================================================
// Routes
// ============================================================================

export default async function aiDiagnosticsRoutes(fastify: FastifyInstance): Promise<void> {
  // All routes require admin authentication
  fastify.addHook('onRequest', async request => {
    requireAdmin(request);
  });

  // ==========================================================================
  // POST /ai-diagnostics/query - Natural language diagnostic query
  // ==========================================================================
  fastify.post('/query', {
    schema: {
      body: {
        type: 'object',
        properties: {
          query: { type: 'string' },
          productId: { type: 'string' },
          timeRange: {
            type: 'object',
            properties: {
              start: { type: 'string' },
              end: { type: 'string' },
            },
          },
        },
        required: ['query'],
      },
    },
    handler: async (
      request: FastifyRequest<{ Body: z.infer<typeof QueryRequestSchema> }>,
      reply: FastifyReply
    ) => {
      try {
        const body = QueryRequestSchema.parse(request.body);
        const userId = request.jwtPayload?.sub || 'anonymous';

        // Parse the query
        const parsedQuery = parseQuery(body.query);

        // Add time range if provided
        if (body.timeRange) {
          parsedQuery.entities.timeRange = body.timeRange;
        }

        // Add product filter
        if (body.productId) {
          parsedQuery.entities.products = [body.productId];
        }

        // Validate query
        const validation = validateQuery(parsedQuery);
        if (!validation.valid) {
          return reply.status(400).send({
            error: 'Invalid query',
            details: validation.errors,
          });
        }

        // Execute query
        const result = await executeQuery(parsedQuery, {
          productId: body.productId,
          userId,
        });

        // Save query for history
        await repository.saveNaturalLanguageQuery({
          id: `nq_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
          userId,
          productId: body.productId,
          rawQuery: body.query,
          parsedIntent: parsedQuery.intent,
          extractedEntities: parsedQuery.entities,
          aiResponse: result.aiResponse,
          confidence: result.confidence,
          supportingData: result.supportingData,
          dataSources: result.supportingData.map(s => s.type),
          executionTimeMs: result.executionTimeMs,
          createdAt: new Date().toISOString(),
          ttl: 30 * 86400, // 30 days
        });

        return reply.send({
          success: true,
          result,
          warnings: validation.warnings,
        });
      } catch (error) {
        request.log.error({ error }, 'Query execution failed');
        return reply.status(500).send({
          error: 'Query execution failed',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // GET /ai-diagnostics/clusters - List error clusters
  // ==========================================================================
  fastify.get('/clusters', {
    schema: {
      querystring: {
        type: 'object',
        properties: {
          productId: { type: 'string' },
          status: { type: 'string', enum: ['active', 'investigating', 'resolved', 'ignored'] },
          limit: { type: 'number', default: 50 },
          includeInsights: { type: 'boolean', default: false },
        },
        required: ['productId'],
      },
    },
    handler: async (
      request: FastifyRequest<{
        Querystring: {
          productId: string;
          status?: 'active' | 'investigating' | 'resolved' | 'ignored';
          limit?: number;
          includeInsights?: boolean;
        };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { productId, status, limit = 50, includeInsights = false } = request.query;

        const clusters = await repository.findClustersByProduct(productId, {
          status,
          limit,
        });

        let clustersWithInsights: Array<
          ErrorClusterDoc & { latestInsight?: DiagnosticInsightDoc }
        > = clusters;

        if (includeInsights) {
          clustersWithInsights = await Promise.all(
            clusters.map(async cluster => {
              const insight = await repository.getLatestInsightForCluster(cluster.id, productId);
              return { ...cluster, latestInsight: insight || undefined };
            })
          );
        }

        return reply.send({
          clusters: clustersWithInsights,
          total: clusters.length,
        });
      } catch (error) {
        request.log.error({ error }, 'Failed to fetch clusters');
        return reply.status(500).send({
          error: 'Failed to fetch clusters',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // GET /ai-diagnostics/clusters/:id - Get cluster detail
  // ==========================================================================
  fastify.get('/clusters/:id', {
    schema: {
      params: {
        type: 'object',
        properties: {
          id: { type: 'string' },
        },
        required: ['id'],
      },
      querystring: {
        type: 'object',
        properties: {
          productId: { type: 'string' },
        },
        required: ['productId'],
      },
    },
    handler: async (
      request: FastifyRequest<{
        Params: { id: string };
        Querystring: { productId: string };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { id } = request.params;
        const { productId } = request.query;

        const cluster = await repository.getErrorClusterById(id, productId);

        if (!cluster) {
          return reply.status(404).send({ error: 'Cluster not found' });
        }

        // Get latest insight
        const latestInsight = await repository.getLatestInsightForCluster(id, productId);

        // Get related clusters
        const relatedClusters = await repository.findRelatedClusters(id, productId, {
          limit: 5,
        });

        return reply.send({
          cluster,
          latestInsight,
          relatedClusters,
        });
      } catch (error) {
        request.log.error({ error }, 'Failed to fetch cluster');
        return reply.status(500).send({
          error: 'Failed to fetch cluster',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // POST /ai-diagnostics/clusters/:id/analyze - Trigger fresh analysis
  // ==========================================================================
  fastify.post('/clusters/:id/analyze', {
    schema: {
      params: {
        type: 'object',
        properties: {
          id: { type: 'string' },
        },
        required: ['id'],
      },
      querystring: {
        type: 'object',
        properties: {
          productId: { type: 'string' },
        },
        required: ['productId'],
      },
      body: {
        type: 'object',
        properties: {
          analysisType: { type: 'string', enum: ['root_cause', 'pattern', 'comparison', 'trend'] },
          modelPreference: { type: 'string', enum: ['gpt-4o-mini', 'gpt-4o'] },
        },
      },
    },
    handler: async (
      request: FastifyRequest<{
        Params: { id: string };
        Querystring: { productId: string };
        Body: z.infer<typeof AnalyzeClusterRequestSchema>;
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { id } = request.params;
        const { productId } = request.query;
        const body = AnalyzeClusterRequestSchema.parse(request.body || {});

        // Get cluster
        const cluster = await repository.getErrorClusterById(id, productId);
        if (!cluster) {
          return reply.status(404).send({ error: 'Cluster not found' });
        }

        // Aggregate context
        const contextSummary = await aggregateClusterContext(cluster, []);

        // Run analysis
        const analysis = await analyzeRootCause({
          cluster,
          context: contextSummary,
          sampleStackTraces: [cluster.stackSignature],
          relatedClusters: [],
          analysisType: body.analysisType,
        });

        // Save insight
        const insightId = `di_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
        const insight: DiagnosticInsightDoc = {
          id: insightId,
          clusterId: id,
          productId,
          ...analysis,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          ttl: 90 * 86400,
        } as DiagnosticInsightDoc;

        await repository.createDiagnosticInsight(insight);

        // Update cluster with insight reference
        cluster.lastAnalyzedAt = new Date().toISOString();
        cluster.insightId = insightId;
        await repository.updateErrorCluster(cluster);

        return reply.send({
          success: true,
          insight,
        });
      } catch (error) {
        request.log.error({ error }, 'Analysis failed');
        return reply.status(500).send({
          error: 'Analysis failed',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // GET /ai-diagnostics/clusters/:id/analysis - Get pre-computed insight
  // ==========================================================================
  fastify.get('/clusters/:id/analysis', {
    schema: {
      params: {
        type: 'object',
        properties: {
          id: { type: 'string' },
        },
        required: ['id'],
      },
      querystring: {
        type: 'object',
        properties: {
          productId: { type: 'string' },
        },
        required: ['productId'],
      },
    },
    handler: async (
      request: FastifyRequest<{
        Params: { id: string };
        Querystring: { productId: string };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { id } = request.params;
        const { productId } = request.query;

        const insight = await repository.getLatestInsightForCluster(id, productId);

        if (!insight) {
          return reply.status(404).send({
            error: 'No analysis found',
            message: 'Run POST /analyze to generate analysis',
          });
        }

        return reply.send({ insight });
      } catch (error) {
        request.log.error({ error }, 'Failed to fetch analysis');
        return reply.status(500).send({
          error: 'Failed to fetch analysis',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // POST /ai-diagnostics/feedback - Submit insight rating
  // ==========================================================================
  fastify.post('/feedback', {
    schema: {
      body: {
        type: 'object',
        properties: {
          insightId: { type: 'string' },
          clusterId: { type: 'string' },
          rating: { type: 'string', enum: ['helpful', 'not_helpful'] },
          note: { type: 'string' },
        },
        required: ['insightId', 'clusterId', 'rating'],
      },
    },
    handler: async (
      request: FastifyRequest<{ Body: z.infer<typeof FeedbackRequestSchema> }>,
      reply: FastifyReply
    ) => {
      try {
        const body = FeedbackRequestSchema.parse(request.body);
        const userId = request.jwtPayload?.sub || 'anonymous';

        await repository.updateInsightFeedback(body.insightId, body.clusterId, {
          helpful: body.rating === 'helpful',
          note: body.note,
        });

        return reply.send({
          success: true,
          message: 'Feedback recorded',
        });
      } catch (error) {
        request.log.error({ error }, 'Failed to record feedback');
        return reply.status(500).send({
          error: 'Failed to record feedback',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // GET /ai-diagnostics/suggestions - Get AI-suggested investigations
  // ==========================================================================
  fastify.get('/suggestions', {
    schema: {
      querystring: {
        type: 'object',
        properties: {
          productId: { type: 'string' },
          limit: { type: 'number', default: 5 },
        },
        required: ['productId'],
      },
    },
    handler: async (
      request: FastifyRequest<{
        Querystring: { productId: string; limit?: number };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { productId, limit = 5 } = request.query;

        // Get active alerts
        const alerts = await repository.getActiveAlerts(productId);

        // Get top error types for suggestions
        const topTypes = await repository.getTopErrorTypes(productId, 5);
        const platforms = ['ios', 'android', 'web'];

        const suggestions = generateQuerySuggestions(
          topTypes.map(t => t.errorType),
          platforms
        );

        return reply.send({
          alerts: alerts.slice(0, limit),
          suggestions: suggestions.slice(0, limit),
        });
      } catch (error) {
        request.log.error({ error }, 'Failed to get suggestions');
        return reply.status(500).send({
          error: 'Failed to get suggestions',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // GET /ai-diagnostics/query-history - Get user's query history
  // ==========================================================================
  fastify.get('/query-history', {
    schema: {
      querystring: {
        type: 'object',
        properties: {
          limit: { type: 'number', default: 20 },
        },
      },
    },
    handler: async (
      request: FastifyRequest<{
        Querystring: { limit?: number };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const userId = request.jwtPayload?.sub || 'anonymous';
        const { limit = 20 } = request.query;

        const history = await repository.getQueryHistory(userId, { limit });

        return reply.send({ history });
      } catch (error) {
        request.log.error({ error }, 'Failed to fetch query history');
        return reply.status(500).send({
          error: 'Failed to fetch query history',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // GET /ai-diagnostics/alerts - Get proactive alerts
  // ==========================================================================
  fastify.get('/alerts', {
    schema: {
      querystring: {
        type: 'object',
        properties: {
          productId: { type: 'string' },
          acknowledged: { type: 'boolean' },
          severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low'] },
          limit: { type: 'number', default: 50 },
        },
      },
    },
    handler: async (
      request: FastifyRequest<{
        Querystring: {
          productId?: string;
          acknowledged?: boolean;
          severity?: 'critical' | 'high' | 'medium' | 'low';
          limit?: number;
        };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { productId, acknowledged, severity, limit = 50 } = request.query;

        const alerts = await repository.getActiveAlerts(productId, {
          acknowledged,
          severity,
          limit,
        });

        return reply.send({ alerts, total: alerts.length });
      } catch (error) {
        request.log.error({ error }, 'Failed to fetch alerts');
        return reply.status(500).send({
          error: 'Failed to fetch alerts',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // POST /ai-diagnostics/alerts/:id/acknowledge - Acknowledge alert
  // ==========================================================================
  fastify.post('/alerts/:id/acknowledge', {
    schema: {
      params: {
        type: 'object',
        properties: {
          id: { type: 'string' },
        },
        required: ['id'],
      },
      body: {
        type: 'object',
        properties: {
          note: { type: 'string' },
        },
      },
    },
    handler: async (
      request: FastifyRequest<{
        Params: { id: string };
        Body: { note?: string };
      }>,
      reply: FastifyReply
    ) => {
      try {
        const { id } = request.params;
        const userId = request.jwtPayload?.sub || 'anonymous';
        const { note } = request.body;

        await repository.acknowledgeAlert(id, {
          acknowledgedBy: userId,
          note,
        });

        return reply.send({ success: true, message: 'Alert acknowledged' });
      } catch (error) {
        request.log.error({ error }, 'Failed to acknowledge alert');
        return reply.status(500).send({
          error: 'Failed to acknowledge alert',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });

  // ==========================================================================
  // POST /ai-diagnostics/alerts/:id/resolve - Resolve alert
  // ==========================================================================
  fastify.post('/alerts/:id/resolve', {
    schema: {
      params: {
        type: 'object',
        properties: {
          id: { type: 'string' },
        },
        required: ['id'],
      },
    },
    handler: async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
      try {
        const { id } = request.params;

        await repository.resolveAlert(id);

        return reply.send({ success: true, message: 'Alert resolved' });
      } catch (error) {
        request.log.error({ error }, 'Failed to resolve alert');
        return reply.status(500).send({
          error: 'Failed to resolve alert',
          message: error instanceof Error ? error.message : String(error),
        });
      }
    },
  });
}
