// bot/src/providers/llm/ollama.ts — Ollama local provider (native fetch)

import type { LLMConfig } from '../../types.js'
import type { LLMProvider } from './index.js'

const DEFAULT_BASE_URL = 'http://localhost:11434'
const TIMEOUT_MS = 10_000

interface OllamaGenerateRequest {
  model: string
  prompt: string
  stream: false
  system?: string
}

interface OllamaGenerateResponse {
  response: string
  done: boolean
}

export class OllamaProvider implements LLMProvider {
  private baseUrl: string

  constructor(private config: LLMConfig) {
    this.baseUrl = (config.ollama_base_url ?? DEFAULT_BASE_URL).replace(/\/$/, '')
  }

  private async generate(prompt: string, timeoutMs = TIMEOUT_MS, system?: string): Promise<string> {
    const body: OllamaGenerateRequest = {
      model: this.config.model,
      prompt,
      stream: false,
      ...(system ? { system } : {}),
    }

    const controller = new AbortController()
    const timer = setTimeout(() => controller.abort(), timeoutMs)

    let response: Response
    try {
      response = await fetch(`${this.baseUrl}/api/generate`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
        signal: controller.signal,
      })
    } catch (err) {
      clearTimeout(timer)
      if (err instanceof Error && err.name === 'AbortError') {
        throw new Error(`Ollama request timed out after ${TIMEOUT_MS}ms`)
      }
      throw new Error(
        `Failed to connect to Ollama at ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`,
      )
    } finally {
      clearTimeout(timer)
    }

    if (!response.ok) {
      const body = await response.text().catch(() => '<unreadable>')
      throw new Error(`Ollama responded with HTTP ${response.status}: ${body}`)
    }

    const data = (await response.json()) as OllamaGenerateResponse
    if (!data.response) {
      throw new Error('Ollama returned empty response')
    }

    return data.response.trim()
  }

  async generateTitle(description: string): Promise<string> {
    const prompt = `Generate a concise card title (maximum 80 characters) for the following feature description. Return ONLY the title text, no quotes, no explanation.\n\nFeature description:\n${description}`
    const title = await this.generate(prompt)
    return title.slice(0, 80)
  }

  async mergeContext(previous: string, newText: string): Promise<string> {
    const prompt = `Merge the previous context and the new text into a single, coherent, and concise summary that preserves all important information. Return ONLY the merged context, no preamble.\n\nPrevious context:\n${previous}\n\nNew text:\n${newText}`
    return this.generate(prompt)
  }

  async generateArtifact(systemPrompt: string, userInput: string): Promise<string> {
    // timeout maior (geração longa) e system prompt do subagent.
    return this.generate(userInput, 120_000, systemPrompt)
  }
}
