import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { AiProvider, AiGenerateInput, AiGenerateOutput, StreamChunk } from './ai-provider.interface';

@Injectable()
export class DeepSeekProvider implements AiProvider {
  readonly name = 'deepseek';
  private readonly logger = new Logger(DeepSeekProvider.name);
  private readonly apiKey: string;
  private readonly baseUrl: string;

  constructor(private readonly config: ConfigService) {
    this.apiKey = this.config.get<string>('ai.deepseek.apiKey', '');
    this.baseUrl = this.config.get<string>('ai.deepseek.baseUrl', 'https://api.deepseek.com');
  }

  async generate(input: AiGenerateInput): Promise<AiGenerateOutput> {
    const start = Date.now();

    if (!this.apiKey) {
      throw new Error('DeepSeek API key not configured');
    }

    const body: Record<string, any> = {
      model: input.model,
      messages: input.messages,
      temperature: input.temperature ?? 0.3,
      max_tokens: input.maxTokens ?? 4096,
    };

    if (input.responseFormat?.type === 'json_object') {
      body.response_format = { type: 'json_object' };
    }

    const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.apiKey}`,
      },
      body: JSON.stringify(body),
      signal: input.signal,
    });

    if (!response.ok) {
      const errorText = await response.text().catch(() => 'unknown');
      this.logger.error(`DeepSeek API error ${response.status}: ${errorText}`);
      throw new Error(`DeepSeek API returned ${response.status}`);
    }

    const data = await response.json();
    const latencyMs = Date.now() - start;

    const content = data.choices?.[0]?.message?.content ?? '';
    const usage = data.usage ?? {};

    return {
      rawText: content,
      usage: {
        model: input.model,
        inputTokens: usage.prompt_tokens ?? 0,
        outputTokens: usage.completion_tokens ?? 0,
      },
      latencyMs,
    };
  }

  async *generateStream(input: AiGenerateInput): AsyncGenerator<StreamChunk, void, undefined> {
    if (!this.apiKey) {
      yield { type: 'error', content: 'DeepSeek API key not configured' };
      return;
    }

    const body: Record<string, any> = {
      model: input.model,
      messages: input.messages,
      temperature: input.temperature ?? 0.3,
      max_tokens: input.maxTokens ?? 4096,
      stream: true,
    };

    // When streaming, do NOT use response_format json_object — it disables reasoning_content
    const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.apiKey}`,
      },
      body: JSON.stringify(body),
      signal: input.signal,
    });

    if (!response.ok) {
      const errorText = await response.text().catch(() => 'unknown');
      this.logger.error(`DeepSeek stream error ${response.status}: ${errorText}`);
      yield { type: 'error', content: `DeepSeek API returned ${response.status}` };
      return;
    }

    const reader = response.body?.getReader();
    if (!reader) {
      yield { type: 'error', content: 'No response body' };
      return;
    }

    const decoder = new TextDecoder();
    let buffer = '';
    let inputTokens = 0;
    let outputTokens = 0;

    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';

        for (const line of lines) {
          if (!line.startsWith('data: ')) continue;
          const data = line.slice(6).trim();
          if (data === '[DONE]') {
            yield { type: 'done', usage: { inputTokens, outputTokens } };
            return;
          }

          try {
            const json = JSON.parse(data);
            const delta = json.choices?.[0]?.delta;
            if (!delta) continue;

            // Track usage
            if (json.usage) {
              inputTokens = json.usage.prompt_tokens ?? inputTokens;
              outputTokens = json.usage.completion_tokens ?? outputTokens;
            }

            // reasoning_content = thinking process (DeepSeek V4 Pro)
            if (delta.reasoning_content) {
              yield { type: 'thinking', content: delta.reasoning_content };
            }

            // content = actual response
            if (delta.content) {
              yield { type: 'content', content: delta.content };
            }
          } catch {
            // Skip unparseable chunks
          }
        }
      }
      yield { type: 'done', usage: { inputTokens, outputTokens } };
    } finally {
      reader.releaseLock();
    }
  }
}
