package glue

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"mind/internal/config"
	"mind/internal/db"
	"mind/internal/models"
)

type Glue struct {
	repo *db.Repo
	cfg  config.Config
}

// NewGlue now receives cfg
func NewGlue(r *db.Repo, cfg config.Config) *Glue { return &Glue{repo: r, cfg: cfg} }

func (g *Glue) CreateConversation(ctx context.Context, ownerID int64, title string) (int64, error) {
	return g.repo.CreateConversation(ctx, ownerID, title)
}

func (g *Glue) ListConversations(ctx context.Context, ownerID int64) ([]models.Conversation, error) {
	return g.repo.ListConversations(ctx, ownerID)
}

type CompletionReq struct {
	ConversationID int64  `json:"conversation_id"`
	BranchName     string `json:"branch"`
	Prompt         string `json:"prompt"`
}

type CompletionResp struct {
	PromptNodeID int64  `json:"prompt_node_id"`
	AnswerNodeID int64  `json:"answer_node_id"`
	Answer       string `json:"answer"`
}

// llama request/response (support two common shapes)
type llamaReq struct {
	Prompt string `json:"prompt"`
}
type llamaResp struct {
	Content string `json:"content"`
}

func (g *Glue) callLlama(ctx context.Context, prompt string) (string, error) {
	if g.cfg.LlamaURL == "" {
		return "", errors.New("LLAMA_URL not set")
	}
	body, _ := json.Marshal(llamaReq{Prompt: prompt})
	url := g.cfg.LlamaURL + "/completion"

	httpc := &http.Client{Timeout: 45 * time.Second}

	req, _ := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	resp, err := httpc.Do(req)
	if err != nil {		
		return "", err
	}
	defer resp.Body.Close()
	
	dec := json.NewDecoder(resp.Body)
	var a llamaResp
	if err := dec.Decode(&a); err == nil && a.Content != "" {
		return a.Content, nil
	}

	return "", errors.New("unexpected llama response")
}

func (g *Glue) AppendCompletion(ctx context.Context, req CompletionReq) (CompletionResp, error) {
	b, err := g.repo.GetBranch(ctx, req.ConversationID, req.BranchName)
	if err != nil {
		return CompletionResp{}, err
	}

	// 1) user prompt node
	promptID, err := g.repo.CreateNode(ctx, req.ConversationID, "user", req.Prompt)
	if err != nil {
		return CompletionResp{}, err
	}
	if err := g.repo.Link(ctx, b.HeadNodeID, promptID); err != nil {
		return CompletionResp{}, err
	}

	// 2) real llama call (fallback to stub if it fails)
	answerText, err := g.callLlama(ctx, req.Prompt)
	if err != nil {
		answerText = "(stub) You said: " + req.Prompt
	}

	answerID, err := g.repo.CreateNode(ctx, req.ConversationID, "assistant", answerText)
	if err != nil {
		return CompletionResp{}, err
	}
	if err := g.repo.Link(ctx, promptID, answerID); err != nil {
		return CompletionResp{}, err
	}

	// 3) move branch head
	if err := g.repo.MoveBranchHead(ctx, b.ID, answerID); err != nil {
		return CompletionResp{}, err
	}

	return CompletionResp{PromptNodeID: promptID, AnswerNodeID: answerID, Answer: answerText}, nil
}
