import { Logger } from '@nestjs/common';
import OpenAI from 'openai';
import type {
  CompletionEvent,
  CompletionParams,
  IProviderAdapter,
  ModelInfo,
  ProviderHealth,
} from '@mosaicstack/types';

const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1';

interface OpenRouterModel {
  id: string;
  name?: string;
  context_length?: number;
  top_provider?: {
    max_completion_tokens?: number;
  };
  pricing?: {
    prompt?: string | number;
    completion?: string | number;
  };
  architecture?: {
    input_modalities?: string[];
  };
}

interface OpenRouterModelsResponse {
  data?: OpenRouterModel[];
}

/**
 * OpenRouter provider adapter.
 *
 * Routes completions through OpenRouter's OpenAI-compatible API.
 * Configuration is driven by the OPENROUTER_API_KEY environment variable.
 */
export class OpenRouterAdapter implements IProviderAdapter {
  readonly name = 'openrouter';

  private readonly logger = new Logger(OpenRouterAdapter.name);
  private client: OpenAI | null = null;
  private registeredModels: ModelInfo[] = [];

  async register(): Promise<void> {
    const apiKey = process.env['OPENROUTER_API_KEY'];
    if (!apiKey) {
      this.logger.debug('Skipping OpenRouter provider registration: OPENROUTER_API_KEY not set');
      return;
    }

    this.client = new OpenAI({
      apiKey,
      baseURL: OPENROUTER_BASE_URL,
      defaultHeaders: {
        'HTTP-Referer': 'https://mosaic.ai',
        'X-Title': 'Mosaic',
      },
    });

    try {
      this.registeredModels = await this.fetchModels(apiKey);
      this.logger.log(`OpenRouter provider registered with ${this.registeredModels.length} models`);
    } catch (err) {
      this.logger.warn(
        `OpenRouter model discovery failed: ${err instanceof Error ? err.message : String(err)}. Registering with empty model list.`,
      );
      this.registeredModels = [];
    }
  }

  listModels(): ModelInfo[] {
    return this.registeredModels;
  }

  async healthCheck(): Promise<ProviderHealth> {
    const apiKey = process.env['OPENROUTER_API_KEY'];
    if (!apiKey) {
      return {
        status: 'down',
        lastChecked: new Date().toISOString(),
        error: 'OPENROUTER_API_KEY not configured',
      };
    }

    const start = Date.now();
    try {
      const res = await fetch(`${OPENROUTER_BASE_URL}/models`, {
        method: 'GET',
        headers: {
          Authorization: `Bearer ${apiKey}`,
          Accept: 'application/json',
        },
        signal: AbortSignal.timeout(5000),
      });
      const latencyMs = Date.now() - start;

      if (!res.ok) {
        return {
          status: 'degraded',
          latencyMs,
          lastChecked: new Date().toISOString(),
          error: `HTTP ${res.status}`,
        };
      }

      return { status: 'healthy', latencyMs, lastChecked: new Date().toISOString() };
    } catch (err) {
      const latencyMs = Date.now() - start;
      const error = err instanceof Error ? err.message : String(err);
      return { status: 'down', latencyMs, lastChecked: new Date().toISOString(), error };
    }
  }

  /**
   * Stream a completion through OpenRouter's OpenAI-compatible API.
   */
  async *createCompletion(params: CompletionParams): AsyncIterable<CompletionEvent> {
    if (!this.client) {
      throw new Error('OpenRouterAdapter is not initialized. Ensure OPENROUTER_API_KEY is set.');
    }

    const stream = await this.client.chat.completions.create({
      model: params.model,
      messages: params.messages.map((m) => ({ role: m.role, content: m.content })),
      temperature: params.temperature,
      max_tokens: params.maxTokens,
      stream: true,
    });

    let inputTokens = 0;
    let outputTokens = 0;

    for await (const chunk of stream) {
      const choice = chunk.choices[0];
      if (!choice) continue;

      const delta = choice.delta;

      if (delta.content) {
        yield { type: 'text_delta', content: delta.content };
      }

      if (choice.finish_reason === 'stop') {
        const usage = (chunk as { usage?: { prompt_tokens?: number; completion_tokens?: number } })
          .usage;
        if (usage) {
          inputTokens = usage.prompt_tokens ?? 0;
          outputTokens = usage.completion_tokens ?? 0;
        }
      }
    }

    yield {
      type: 'done',
      usage: { inputTokens, outputTokens },
    };
  }

  // ---------------------------------------------------------------------------
  // Private helpers
  // ---------------------------------------------------------------------------

  private async fetchModels(apiKey: string): Promise<ModelInfo[]> {
    const res = await fetch(`${OPENROUTER_BASE_URL}/models`, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${apiKey}`,
        Accept: 'application/json',
      },
      signal: AbortSignal.timeout(10000),
    });

    if (!res.ok) {
      throw new Error(`OpenRouter models endpoint returned HTTP ${res.status}`);
    }

    const json = (await res.json()) as OpenRouterModelsResponse;
    const data = json.data ?? [];

    return data.map((model): ModelInfo => {
      const inputPrice = model.pricing?.prompt
        ? parseFloat(String(model.pricing.prompt)) * 1000
        : 0;
      const outputPrice = model.pricing?.completion
        ? parseFloat(String(model.pricing.completion)) * 1000
        : 0;

      const inputModalities = model.architecture?.input_modalities ?? ['text'];
      const inputTypes = inputModalities.includes('image')
        ? (['text', 'image'] as const)
        : (['text'] as const);

      return {
        id: model.id,
        provider: 'openrouter',
        name: model.name ?? model.id,
        reasoning: false,
        contextWindow: model.context_length ?? 4096,
        maxTokens: model.top_provider?.max_completion_tokens ?? 4096,
        inputTypes: [...inputTypes],
        cost: {
          input: inputPrice,
          output: outputPrice,
          cacheRead: 0,
          cacheWrite: 0,
        },
      };
    });
  }
}
