import type { GenerateChunk, GenerateOpts, LLMProvider } from '../types';
import { ndjsonLines } from '../sse';

/**
 * Ollama: streaming NDJSON over POST /api/chat.
 *
 * No API key. baseUrl required (e.g. http://ollama.embassy:11434).
 * Each line is `{"message":{"content":"..."},"done":false}` until
 * `{"done":true,"prompt_eval_count":N,"eval_count":M}`.
 */
export const ollama: LLMProvider = {
  id: 'ollama',
  label: 'Ollama (self-hosted)',
  requiresApiKey: false,
  requiresBaseUrl: true,

  async *generate(opts: GenerateOpts): AsyncGenerator<GenerateChunk, void, void> {
    if (!opts.baseUrl) {
      yield { type: 'error', message: 'Ollama base URL is required.' };
      return;
    }
    const url = opts.baseUrl.replace(/\/$/, '') + '/api/chat';
    let res: Response;
    try {
      res = await fetch(url, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          model: opts.model,
          stream: true,
          messages: [
            { role: 'system', content: opts.systemPrompt },
            { role: 'user', content: opts.userPrompt },
          ],
          options: {
            temperature: 0.7,
          },
        }),
        signal: opts.signal,
      });
    } catch (e) {
      yield {
        type: 'error',
        message: `Ollama unreachable at ${opts.baseUrl}: ${(e as Error).message}`,
      };
      return;
    }
    if (!res.ok) {
      yield {
        type: 'error',
        message: `Ollama HTTP ${res.status}: ${(await res.text().catch(() => '')).slice(0, 500)}`,
      };
      return;
    }
    let tokensIn: number | undefined;
    let tokensOut: number | undefined;
    try {
      for await (const line of ndjsonLines(res)) {
        let evt: any;
        try {
          evt = JSON.parse(line);
        } catch {
          continue;
        }
        if (evt.message?.content) {
          yield { type: 'text', delta: evt.message.content };
        }
        if (evt.done) {
          tokensIn = evt.prompt_eval_count;
          tokensOut = evt.eval_count;
          break;
        }
      }
      yield { type: 'usage', tokensIn, tokensOut };
      yield { type: 'done' };
    } catch (e) {
      yield {
        type: 'error',
        message: `Ollama stream error: ${(e as Error).message}`,
      };
    }
  },
};
