// @lifecycle canonical - Observability resource handler for MCP resources protocol.
/**
 * Observability Resources Handler
 *
 * Registers MCP resources for token-efficient access to session state and metrics.
 * These provide read-only observability into the runtime system.
 *
 * URI Patterns:
 * - resource://session/              → List active chain sessions
 * - resource://session/{chainId}     → Individual session state
 * - resource://metrics/pipeline      → Pipeline execution metrics and analytics
 */

import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';

import { ResourceNotFoundError, RESOURCE_URI_PATTERNS } from '../types.js';

import type { SessionResourceMetadata, ResourceDependencies } from '../types.js';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';

/**
 * Build resource URI from pattern and optional ID
 * Patterns already include the full URI scheme (resource://...)
 */
function buildUri(pattern: string, id?: string): string {
  if (id !== undefined && id !== '') {
    return pattern.replace('{chainId}', id);
  }
  return pattern;
}

/**
 * Register observability-related MCP resources (sessions and metrics).
 *
 * Resources read from managers at request time to ensure
 * hot-reload compatibility and real-time data.
 */
export function registerObservabilityResources(
  server: McpServer,
  dependencies: ResourceDependencies
): void {
  const { logger, chainSessionManager, metricsCollector } = dependencies;

  // Register session resources if ChainSessionManager is available
  if (chainSessionManager !== undefined) {
    registerSessionResources(server, dependencies);
    logger.debug('[ObservabilityResources] Session resources registered');
  } else {
    logger.warn(
      '[ObservabilityResources] ChainSessionManager not available, skipping session resources'
    );
  }

  // Register metrics resources if MetricsCollector is available
  if (metricsCollector !== undefined) {
    registerMetricsResources(server, dependencies);
    logger.debug('[ObservabilityResources] Metrics resources registered');
  } else {
    logger.warn(
      '[ObservabilityResources] MetricsCollector not available, skipping metrics resources'
    );
  }

  // Register telemetry status resource if TelemetryRuntime is available
  if (dependencies.telemetryRuntime !== undefined) {
    registerTelemetryResources(server, dependencies);
    logger.debug('[ObservabilityResources] Telemetry resources registered');
  }

  // Register log resources if LogManager is available
  if (dependencies.logManager !== undefined) {
    registerLogResources(server, dependencies);
    logger.debug('[ObservabilityResources] Log resources registered');
  } else {
    logger.warn('[ObservabilityResources] LogManager not available, skipping log resources');
  }

  logger.info('[ObservabilityResources] Registered observability resources');
}

/**
 * Register session-related resources
 */
function registerSessionResources(server: McpServer, dependencies: ResourceDependencies): void {
  const { logger, chainSessionManager } = dependencies;

  if (chainSessionManager === undefined) {
    return;
  }

  // Resource: List active sessions (minimal metadata for token efficiency)
  server.registerResource(
    'sessions',
    new ResourceTemplate(RESOURCE_URI_PATTERNS.SESSION_LIST, {
      list: async () => {
        const sessions = chainSessionManager.listActiveSessions(50);
        logger.debug(`[ObservabilityResources] Listing ${sessions.length} active sessions`);

        // Use chainId (user-facing) in URIs, not internal sessionId
        const resources: SessionResourceMetadata[] = sessions.map((s) => ({
          uri: buildUri(RESOURCE_URI_PATTERNS.SESSION_ITEM, s.chainId),
          name: s.chainId,
          title: s.promptName ?? s.chainId,
          description: `Step ${s.currentStep}/${s.totalSteps}${s.pendingReview ? ' (pending review)' : ''}`,
          mimeType: 'application/json',
          chainId: s.chainId,
          currentStep: s.currentStep,
          totalSteps: s.totalSteps,
          pendingReview: s.pendingReview,
          lastActivity: s.lastActivity,
        }));

        return { resources };
      },
    }),
    {
      description: 'List of active chain sessions with status',
      mimeType: 'application/json',
    },
    async (): Promise<ReadResourceResult> => {
      const sessions = chainSessionManager.listActiveSessions(50);
      // Use chainId (user-facing) in URIs, not internal sessionId
      const list: SessionResourceMetadata[] = sessions.map((s) => ({
        uri: buildUri(RESOURCE_URI_PATTERNS.SESSION_ITEM, s.chainId),
        name: s.chainId,
        title: s.promptName ?? s.chainId,
        description: `Step ${s.currentStep}/${s.totalSteps}${s.pendingReview ? ' (pending review)' : ''}`,
        mimeType: 'application/json',
        chainId: s.chainId,
        currentStep: s.currentStep,
        totalSteps: s.totalSteps,
        pendingReview: s.pendingReview,
        lastActivity: s.lastActivity,
      }));

      return {
        contents: [
          {
            uri: buildUri(RESOURCE_URI_PATTERNS.SESSION_LIST),
            mimeType: 'application/json',
            text: JSON.stringify(list, null, 2),
          },
        ],
      };
    }
  );

  // Resource: Individual session state
  server.registerResource(
    'session',
    new ResourceTemplate(RESOURCE_URI_PATTERNS.SESSION_ITEM, {
      list: undefined, // Individual items discovered via list resource
    }),
    {
      description: 'Individual chain session state and progress',
      mimeType: 'application/json',
    },
    async (uri, variables): Promise<ReadResourceResult> => {
      const chainId = variables['chainId'] as string;

      // Use chainId lookup (user-facing identifier like chain-quick_decision#1)
      const session = chainSessionManager.getSessionByChainIdentifier(chainId);

      if (session === undefined) {
        throw new ResourceNotFoundError('Session', chainId);
      }

      logger.debug(`[ObservabilityResources] Reading session: ${chainId}`);

      // Build session content (JSON format for structured data)
      const content = buildSessionContent(session);

      return {
        contents: [
          {
            uri: uri.toString(),
            mimeType: 'application/json',
            text: content,
          },
        ],
      };
    }
  );
}

/**
 * Register metrics-related resources
 */
function registerMetricsResources(server: McpServer, dependencies: ResourceDependencies): void {
  const { logger, metricsCollector, chainSessionManager } = dependencies;

  if (metricsCollector === undefined) {
    return;
  }

  // Resource: Pipeline metrics and analytics
  server.registerResource(
    'metrics-pipeline',
    new ResourceTemplate(RESOURCE_URI_PATTERNS.METRICS_PIPELINE, {
      list: undefined, // Single resource, no list needed
    }),
    {
      description: 'Pipeline execution metrics and analytics summary',
      mimeType: 'application/json',
    },
    async (uri): Promise<ReadResourceResult> => {
      logger.debug('[ObservabilityResources] Reading pipeline metrics');

      const analytics = metricsCollector.getAnalyticsSummary();
      const sessionStats = chainSessionManager?.getSessionStats();

      // Lean metrics: exclude performanceTrends (token fodder), include useful aggregates
      const { performanceTrends: _trends, ...leanSystemMetrics } = analytics.systemMetrics;

      const metrics = {
        execution: analytics.executionStats,
        system: leanSystemMetrics,
        framework: analytics.frameworkUsage,
        gates: analytics.gateValidationStats,
        recommendations: analytics.recommendations,
        sessions: sessionStats ?? {
          totalSessions: 0,
          totalChains: 0,
          averageStepsPerChain: 0,
          oldestSessionAge: 0,
        },
        timestamp: Date.now(),
      };

      return {
        contents: [
          {
            uri: uri.toString(),
            mimeType: 'application/json',
            text: JSON.stringify(metrics, null, 2),
          },
        ],
      };
    }
  );
}

/**
 * Build formatted session content as JSON
 */
function buildSessionContent(session: {
  sessionId: string;
  chainId: string;
  state: {
    currentStep: number;
    totalSteps: number;
    stepStates?: Map<number, unknown>;
  };
  startTime: number;
  lastActivity: number;
  originalArgs: Record<string, unknown>;
  pendingGateReview?: unknown;
}): string {
  // Convert step states Map to object if present
  const stepStates: Record<string, unknown> = {};
  if (session.state.stepStates !== undefined) {
    for (const [step, state] of session.state.stepStates) {
      stepStates[String(step)] = state;
    }
  }

  const content = {
    sessionId: session.sessionId,
    chainId: session.chainId,
    progress: {
      currentStep: session.state.currentStep,
      totalSteps: session.state.totalSteps,
      percentComplete: Math.round((session.state.currentStep / session.state.totalSteps) * 100),
    },
    timing: {
      startTime: session.startTime,
      lastActivity: session.lastActivity,
      durationMs: session.lastActivity - session.startTime,
      startTimeISO: new Date(session.startTime).toISOString(),
      lastActivityISO: new Date(session.lastActivity).toISOString(),
    },
    originalArgs: session.originalArgs,
    stepStates: Object.keys(stepStates).length > 0 ? stepStates : undefined,
    hasPendingReview: session.pendingGateReview !== undefined,
  };

  return JSON.stringify(content, null, 2);
}

// ─────────────────────────────────────────────────────────────────────────────
// Logs Resources
// ─────────────────────────────────────────────────────────────────────────────

/**
 * Register log-related MCP resources for debugging and observability.
 *
 * URI Patterns:
 * - resource://logs/              → List recent logs (all levels)
 * - resource://logs/{level}       → Filter by level (error/warn/info/debug)
 * - resource://logs/entry/{id}    → Individual log entry details
 */
export function registerLogResources(server: McpServer, dependencies: ResourceDependencies): void {
  const { logger, logManager } = dependencies;

  if (logManager === undefined) {
    logger.warn('[LogResources] LogManager not available, skipping log resources');
    return;
  }

  // Resource: List recent logs (newest first)
  server.registerResource(
    'logs',
    new ResourceTemplate(RESOURCE_URI_PATTERNS.LOGS_LIST, {
      list: undefined, // Single resource, no enumeration
    }),
    {
      description: 'Recent server logs for debugging (newest first)',
      mimeType: 'application/json',
    },
    async (uri): Promise<ReadResourceResult> => {
      const logs = logManager.getRecentLogs({ limit: 100 });
      const stats = logManager.getBufferStats();
      logger.debug(`[LogResources] Listing ${logs.length} recent logs`);

      // Compact format for token efficiency
      const compactLogs = logs.map((e) => ({
        id: e.id,
        ts: e.timestamp,
        level: e.level,
        msg: e.message.substring(0, 200), // Truncate long messages
        ...(e.context !== undefined ? { ctx: e.context } : {}),
      }));

      return {
        contents: [
          {
            uri: uri.toString(),
            mimeType: 'application/json',
            text: JSON.stringify({ logs: compactLogs, stats }, null, 2),
          },
        ],
      };
    }
  );

  // Resource: Logs filtered by level
  server.registerResource(
    'logs-by-level',
    new ResourceTemplate(RESOURCE_URI_PATTERNS.LOGS_BY_LEVEL, {
      list: undefined,
    }),
    {
      description: 'Server logs filtered by level (error/warn/info/debug)',
      mimeType: 'application/json',
    },
    async (uri, variables): Promise<ReadResourceResult> => {
      const level = variables['level'] as string;

      // Validate level
      const validLevels = ['error', 'warn', 'info', 'debug'];
      if (!validLevels.includes(level)) {
        throw new ResourceNotFoundError('Log level', level);
      }

      const logs = logManager.getRecentLogs({
        level: level as 'error' | 'warn' | 'info' | 'debug',
        limit: 100,
      });
      logger.debug(`[LogResources] Listing ${logs.length} logs at level: ${level}`);

      // Compact format
      const compactLogs = logs.map((e) => ({
        id: e.id,
        ts: e.timestamp,
        level: e.level,
        msg: e.message.substring(0, 200),
        ...(e.context !== undefined ? { ctx: e.context } : {}),
      }));

      return {
        contents: [
          {
            uri: uri.toString(),
            mimeType: 'application/json',
            text: JSON.stringify({ level, count: logs.length, logs: compactLogs }, null, 2),
          },
        ],
      };
    }
  );

  // Resource: Individual log entry
  server.registerResource(
    'log-entry',
    new ResourceTemplate(RESOURCE_URI_PATTERNS.LOGS_ENTRY, {
      list: undefined,
    }),
    {
      description: 'Individual log entry with full details',
      mimeType: 'application/json',
    },
    async (uri, variables): Promise<ReadResourceResult> => {
      const id = variables['id'] as string;
      const entry = logManager.getLogEntry(id);

      if (entry === undefined) {
        throw new ResourceNotFoundError('Log entry', id);
      }

      logger.debug(`[LogResources] Reading log entry: ${id}`);

      // Full format for individual entry
      const fullEntry = {
        id: entry.id,
        timestamp: entry.timestamp,
        timestampISO: new Date(entry.timestamp).toISOString(),
        level: entry.level,
        message: entry.message,
        context: entry.context,
      };

      return {
        contents: [
          {
            uri: uri.toString(),
            mimeType: 'application/json',
            text: JSON.stringify(fullEntry, null, 2),
          },
        ],
      };
    }
  );

  logger.info('[LogResources] Registered log resources');
}

/**
 * Register telemetry runtime status resource.
 * Exposes runtime health/config for operator visibility (no sensitive payload).
 */
function registerTelemetryResources(server: McpServer, dependencies: ResourceDependencies): void {
  const { logger, telemetryRuntime } = dependencies;

  if (telemetryRuntime === undefined) {
    return;
  }

  server.registerResource(
    'telemetry-status',
    RESOURCE_URI_PATTERNS.TELEMETRY_STATUS,
    {
      description: 'OpenTelemetry runtime status: enabled state, mode, endpoint, sampling rate',
      mimeType: 'application/json',
    },
    async (): Promise<ReadResourceResult> => {
      logger.debug('[ObservabilityResources] Reading telemetry status');

      const status = telemetryRuntime.getStatus();
      const uptimeMs = status.startedAt !== undefined ? Date.now() - status.startedAt : undefined;

      return {
        contents: [
          {
            uri: RESOURCE_URI_PATTERNS.TELEMETRY_STATUS,
            mimeType: 'application/json',
            text: JSON.stringify(
              {
                enabled: status.enabled,
                mode: status.mode,
                exporterEndpoint: status.exporterEndpoint,
                samplingRate: status.samplingRate,
                uptimeMs,
              },
              null,
              2
            ),
          },
        ],
      };
    }
  );

  logger.info('[TelemetryResources] Registered telemetry status resource');
}
