package rag

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

const (
	EmbedModel = "qwen3-embedding:0.6b"
	ChatModel  = "qwen3:8b"
	GommeBoyut = 1024
)

// OllamaURL — OLLAMA_URL env değişkeniyle ayarlanır (docker-compose),
// yoksa docker-internal "ollama" hostname'i denenir.
var OllamaURL = func() string {
	if v := os.Getenv("OLLAMA_URL"); v != "" {
		return v
	}
	return "http://ollama:11434"
}()

var ollamaClient = &http.Client{Timeout: 10 * time.Minute}

func ollamaEmbed(metinler []string) ([][]float32, error) {
	payload, _ := json.Marshal(map[string]interface{}{
		"model":      ragAyar().EmbedModel,
		"input":      metinler,
		"keep_alive": "20m",
	})

	resp, err := ollamaClient.Post(OllamaURL+"/api/embed", "application/json", bytes.NewReader(payload))
	if err != nil {
		return nil, fmt.Errorf("ollama bağlantı hatası: %w", err)
	}
	defer resp.Body.Close()

	var cevap struct {
		Embeddings [][]float32 `json:"embeddings"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&cevap); err != nil {
		return nil, fmt.Errorf("ollama cevap parse hatası: %w", err)
	}

	return cevap.Embeddings, nil
}

func ollamaChat(mesajlar []map[string]string) (string, error) {
	ayar := ragAyar()
	payload, _ := json.Marshal(map[string]interface{}{
		"model":      ayar.ChatModel,
		"messages":   mesajlar,
		"stream":     false,
		"keep_alive": "20m",
		"options": map[string]interface{}{
			"num_ctx": ayar.NumCtx,
		},
		"think": ayar.DusunmeAktif && modelDusunebilirMi(ayar.ChatModel),
	})

	resp, err := ollamaClient.Post(OllamaURL+"/api/chat", "application/json", bytes.NewReader(payload))
	if err != nil {
		return "", fmt.Errorf("ollama chat hatası: %w", err)
	}
	defer resp.Body.Close()

	var cevap struct {
		Message struct {
			Content  string `json:"content"`
			Thinking string `json:"thinking"`
		} `json:"message"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&cevap); err != nil {
		return "", fmt.Errorf("ollama chat cevap hatası: %w", err)
	}

	return cevap.Message.Content, nil
}

// ollamaChatStream — resimler doluysa son kullanıcı mesajına base64 görüntüler eklenir (vision modeller).
func ollamaChatStream(mesajlar []map[string]string, resimler []string, callback func(tip string, parca string)) error {
	ayar := ragAyar()

	msgs := make([]map[string]interface{}, 0, len(mesajlar))
	for _, m := range mesajlar {
		msgs = append(msgs, map[string]interface{}{"role": m["role"], "content": m["content"]})
	}
	if len(resimler) > 0 {
		for i := len(msgs) - 1; i >= 0; i-- {
			if msgs[i]["role"] == "user" {
				msgs[i]["images"] = resimler
				break
			}
		}
	}

	payload, _ := json.Marshal(map[string]interface{}{
		"model":      ayar.ChatModel,
		"messages":   msgs,
		"stream":     true,
		"keep_alive": "20m",
		"options":    map[string]interface{}{"num_ctx": ayar.NumCtx},
		"think":      ayar.DusunmeAktif && modelDusunebilirMi(ayar.ChatModel),
	})

	resp, err := ollamaClient.Post(OllamaURL+"/api/chat", "application/json", bytes.NewReader(payload))
	if err != nil {
		return fmt.Errorf("ollama stream hatası: %w", err)
	}
	defer resp.Body.Close()

	scanner := bufio.NewScanner(resp.Body)
	scanner.Buffer(make([]byte, 64*1024), 64*1024)

	for scanner.Scan() {
		var parca struct {
			Message struct {
				Content  string `json:"content"`
				Thinking string `json:"thinking"`
			} `json:"message"`
			Done  bool   `json:"done"`
			Error string `json:"error"`
		}
		if err := json.Unmarshal(scanner.Bytes(), &parca); err != nil {
			continue
		}
		if parca.Error != "" {
			return fmt.Errorf("ollama: %s", parca.Error)
		}
		if parca.Message.Thinking != "" {
			callback("d", parca.Message.Thinking)
		}
		if parca.Message.Content != "" {
			callback("p", parca.Message.Content)
		}
		if parca.Done {
			break
		}
	}
	return scanner.Err()
}

func ollamaChatRetry(mesajlar []map[string]string, maxDeneme int) (string, error) {
	var sonHata error
	for i := 0; i < maxDeneme; i++ {
		cevap, err := ollamaChat(mesajlar)
		if err == nil && cevap != "" {
			return cevap, nil
		}
		sonHata = err
		if i < maxDeneme-1 {
			time.Sleep(time.Duration(i+1) * 2 * time.Second)
		}
	}
	return "", fmt.Errorf("ollama %d deneme sonrası başarısız: %w", maxDeneme, sonHata)
}
