// Package llm LLM sağlayıcıları için soyutlama katmanıdır.
// Phase 1: Generate, GenerateStream, ListModels. ToolCall (Phase 5) ve Embed (Phase 3) sonra eklenecek.
package llm

import (
	"context"
	"errors"
	"fmt"
	"time"
)

type Role string

const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
)

// Message tek bir LLM mesajıdır.
//
// DİKKAT: Tool çağrıları burada TUTULMAZ. Çağrı geçmişi, ajanın döngüsünde düz metin
// olarak temsil edilir (bkz. toolResultText): (1) Message karşılaştırılabilir kalır
// (ctxmgr pencereleme testleri buna dayanır), (2) her sağlayıcı için ayrı bir
// "tool_calls + tool_name" gönderim kuralı gerekmez.
type Message struct {
	Role    Role
	Content string
}

// Options model parametreleridir; sıfır değer "sağlayıcı varsayılanı" demektir.
type Options struct {
	Temperature *float64
	TopP        *float64
	MaxTokens   int
	ContextSize int
}

type Request struct {
	Model    string // boşsa sağlayıcının varsayılan modeli
	Messages []Message
	Options  Options
}

// Result bir üretimin sonucudur.
// GenerateStream iptal/hata durumunda bile o ana kadar üretilen kısmi içeriği döndürür.
// ToolCalls yalnızca tool destekleyen akışlarda dolar (bkz. ToolProvider).
type Result struct {
	Model            string
	Content          string
	DoneReason       string
	PromptTokens     int
	CompletionTokens int
	ToolCalls        []ToolCall
}

type ModelInfo struct {
	Name       string
	SizeBytes  int64
	ModifiedAt time.Time
}

// DeltaFunc akıştaki her metin parçası için çağrılır. Hata dönerse üretim durdurulur.
type DeltaFunc func(delta string) error

// Provider bir LLM arka ucunu (Ollama, OpenAI-uyumlu, ...) temsil eder.
type Provider interface {
	Name() string
	Generate(ctx context.Context, req Request) (Result, error)
	GenerateStream(ctx context.Context, req Request, onDelta DeltaFunc) (Result, error)
	ListModels(ctx context.Context) ([]ModelInfo, error)
}

// Embedder metni vektöre çevirir (semantik arama için). Provider'dan AYRI küçük bir arayüzdür:
// her sağlayıcının embedding desteği olmayabilir ve sohbet bileşenleri buna bağımlı olmamalıdır.
// Vektör boyutu modele bağlıdır; model değişirse eski vektörlerle karşılaştırılamaz (yeniden indeksle).
type Embedder interface {
	Embed(ctx context.Context, text string) ([]float32, error)
}

// BatchEmbedder birden çok metni tek çağrıda vektörleştirir (toplu giriş: 140 firma tek mesajda gelebilir).
// Sonuç girdiyle AYNI sırada ve aynı uzunluktadır; metinlerden biri boşsa hata döner (ErrEmptyText),
// sessizce atlanmaz, çünkü atlamak vektörleri yanlış kayıtlara hizalar.
// Embedder'dan AYRIDIR: her sağlayıcı toplu isteği desteklemeyebilir (bkz. EmbedAll).
type BatchEmbedder interface {
	EmbedBatch(ctx context.Context, texts []string) ([][]float32, error)
}

// EmbedAll toplu destek varsa EmbedBatch'i, yoksa metinleri tek tek Embed ile vektörleştirir.
// Yazma servisi (Faz 3) sağlayıcıdan bağımsız bu fonksiyonu çağırır.
func EmbedAll(ctx context.Context, e Embedder, texts []string) ([][]float32, error) {
	if b, ok := e.(BatchEmbedder); ok {
		return b.EmbedBatch(ctx, texts)
	}
	out := make([][]float32, 0, len(texts))
	for _, t := range texts {
		v, err := e.Embed(ctx, t)
		if err != nil {
			return nil, err
		}
		out = append(out, v)
	}
	return out, nil
}

var (
	ErrNoModel       = errors.New("llm: model belirtilmedi (OLLAMA_MODEL ayarla ya da istekte model ver)")
	ErrNoEmbedModel  = errors.New("llm: embedding modeli belirtilmedi (OLLAMA_EMBED_MODEL ayarla)")
	ErrEmptyMessages = errors.New("llm: mesaj listesi boş")
	ErrEmptyText     = errors.New("llm: embedding için metin boş")
)

// ProviderError sağlayıcının döndürdüğü hatadır.
type ProviderError struct {
	Provider   string
	StatusCode int // 0: HTTP dışı (akış içi) hata
	Message    string
}

func (e *ProviderError) Error() string {
	if e.StatusCode != 0 {
		return fmt.Sprintf("%s: HTTP %d: %s", e.Provider, e.StatusCode, e.Message)
	}
	return fmt.Sprintf("%s: %s", e.Provider, e.Message)
}
