package api

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/muyue/muyue/internal/config"
)

// ConversationMeta represents metadata for a conversation (used for listing).
type ConversationMeta struct {
	ID        string    `json:"id"`
	Title     string    `json:"title"`
	CreatedAt string    `json:"created_at"`
	UpdatedAt string    `json:"updated_at"`
	MessageCount int    `json:"message_count"`
}

// ConversationStoreMulti manages multiple conversations.
type ConversationStoreMulti struct {
	mu         sync.RWMutex
	dir        string
	currentID  string
	conversations map[string]*Conversation
}

func NewConversationStoreMulti() *ConversationStoreMulti {
	dir, err := config.ConfigDir()
	if err != nil {
		dir = "/tmp/muyue"
	}
	dir = filepath.Join(dir, "conversations")
	
	cs := &ConversationStoreMulti{
		dir:        dir,
		conversations: make(map[string]*Conversation),
	}
	cs.loadIndex()
	return cs
}

func (cs *ConversationStoreMulti) loadIndex() {
	os.MkdirAll(cs.dir, 0755)
	
	// Load index file if exists
	indexPath := filepath.Join(cs.dir, "index.json")
	data, err := os.ReadFile(indexPath)
	if err != nil {
		// Create default conversation
		cs.createDefault()
		return
	}
	
	var index struct {
		CurrentID      string             `json:"current_id"`
		Conversations []ConversationMeta `json:"conversations"`
	}
	if err := json.Unmarshal(data, &index); err != nil {
		cs.createDefault()
		return
	}
	
	cs.currentID = index.CurrentID
	if cs.currentID == "" {
		cs.createDefault()
		return
	}
	
	// Load all conversations
	for _, meta := range index.Conversations {
		convPath := filepath.Join(cs.dir, fmt.Sprintf("conv_%s.json", meta.ID))
		data, err := os.ReadFile(convPath)
		if err != nil {
			continue
		}
		var conv Conversation
		if err := json.Unmarshal(data, &conv); err != nil {
			continue
		}
		cs.conversations[meta.ID] = &conv
	}
	
	// Ensure current conversation exists
	if _, ok := cs.conversations[cs.currentID]; !ok {
		cs.createDefault()
	}
}

func (cs *ConversationStoreMulti) createDefault() {
	cs.currentID = uuid.New().String()
	cs.conversations[cs.currentID] = &Conversation{
		Messages:  []FeedMessage{},
		CreatedAt: time.Now().Format(time.RFC3339),
		UpdatedAt: time.Now().Format(time.RFC3339),
	}
	cs.saveIndex()
}

func (cs *ConversationStoreMulti) saveIndex() error {
	var metas []ConversationMeta
	for id, conv := range cs.conversations {
		title := "Nouvelle conversation"
		if len(conv.Messages) > 0 {
			// Use first user message as title
			for _, m := range conv.Messages {
				if m.Role == "user" {
					if len(m.Content) > 50 {
						title = m.Content[:50] + "..."
					} else {
						title = m.Content
					}
					break
				}
			}
		}
		metas = append(metas, ConversationMeta{
			ID:           id,
			Title:        title,
			CreatedAt:    conv.CreatedAt,
			UpdatedAt:   conv.UpdatedAt,
			MessageCount: len(conv.Messages),
		})
	}
	
	index := struct {
		CurrentID      string             `json:"current_id"`
		Conversations []ConversationMeta `json:"conversations"`
	}{
		CurrentID:      cs.currentID,
		Conversations: metas,
	}
	
	data, err := json.MarshalIndent(index, "", "  ")
	if err != nil {
		return err
	}
	
	return os.WriteFile(filepath.Join(cs.dir, "index.json"), data, 0600)
}

func (cs *ConversationStoreMulti) saveCurrent() error {
	conv, ok := cs.conversations[cs.currentID]
	if !ok {
		return fmt.Errorf("no current conversation")
	}
	
	conv.UpdatedAt = time.Now().Format(time.RFC3339)
	data, err := json.MarshalIndent(conv, "", "  ")
	if err != nil {
		return err
	}
	
	convPath := filepath.Join(cs.dir, fmt.Sprintf("conv_%s.json", cs.currentID))
	if err := os.WriteFile(convPath, data, 0600); err != nil {
		return err
	}
	
	return cs.saveIndex()
}

// Current returns the current conversation store.
func (cs *ConversationStoreMulti) Current() *ConversationStore {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	
	conv, ok := cs.conversations[cs.currentID]
	if !ok {
		return &ConversationStore{
			conv: &Conversation{
				Messages:  []FeedMessage{},
				CreatedAt: time.Now().Format(time.RFC3339),
				UpdatedAt: time.Now().Format(time.RFC3339),
			},
		}
	}
	
	return &ConversationStore{
		conv: conv,
	}
}

// Get returns the current conversation messages.
func (cs *ConversationStoreMulti) Get() []FeedMessage {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	
	conv, ok := cs.conversations[cs.currentID]
	if !ok {
		return []FeedMessage{}
	}
	
	out := make([]FeedMessage, len(conv.Messages))
	copy(out, conv.Messages)
	return out
}

// Add adds a message to the current conversation.
func (cs *ConversationStoreMulti) Add(role, content string) FeedMessage {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	
	conv, ok := cs.conversations[cs.currentID]
	if !ok {
		cs.currentID = uuid.New().String()
		conv = &Conversation{
			Messages:  []FeedMessage{},
			CreatedAt: time.Now().Format(time.RFC3339),
			UpdatedAt: time.Now().Format(time.RFC3339),
		}
		cs.conversations[cs.currentID] = conv
	}
	
	msg := FeedMessage{
		ID:      generateMsgID(),
		Role:    role,
		Content: content,
		Time:    time.Now().Format(time.RFC3339),
	}
	conv.Messages = append(conv.Messages, msg)

	cs.saveCurrent()

	return msg
}

// Clear clears the current conversation.
func (cs *ConversationStoreMulti) Clear() {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	
	conv, ok := cs.conversations[cs.currentID]
	if !ok {
		return
	}
	
	conv.Messages = []FeedMessage{}
	conv.Summary = ""
	conv.CreatedAt = time.Now().Format(time.RFC3339)
	conv.UpdatedAt = time.Now().Format(time.RFC3339)
	
	cs.saveCurrent()
}

// List returns all conversations.
func (cs *ConversationStoreMulti) List() []ConversationMeta {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	
	var metas []ConversationMeta
	for id, conv := range cs.conversations {
		title := "Nouvelle conversation"
		if len(conv.Messages) > 0 {
			for _, m := range conv.Messages {
				if m.Role == "user" {
					if len(m.Content) > 50 {
						title = m.Content[:50] + "..."
					} else {
						title = m.Content
					}
					break
				}
			}
		}
		metas = append(metas, ConversationMeta{
			ID:           id,
			Title:        title,
			CreatedAt:    conv.CreatedAt,
			UpdatedAt:   conv.UpdatedAt,
			MessageCount: len(conv.Messages),
		})
	}
	
	return metas
}

// Create creates a new conversation and switches to it.
func (cs *ConversationStoreMulti) Create() string {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	
	id := uuid.New().String()
	cs.conversations[id] = &Conversation{
		Messages:  []FeedMessage{},
		CreatedAt: time.Now().Format(time.RFC3339),
		UpdatedAt: time.Now().Format(time.RFC3339),
	}
	cs.currentID = id
	cs.saveIndex()
	
	return id
}

// Switch switches to a different conversation.
func (cs *ConversationStoreMulti) Switch(id string) error {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	
	if _, ok := cs.conversations[id]; !ok {
		return fmt.Errorf("conversation not found: %s", id)
	}
	
	cs.currentID = id
	cs.saveIndex()
	
	return nil
}

// GetByID returns a conversation by ID.
func (cs *ConversationStoreMulti) GetByID(id string) (*Conversation, error) {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	
	conv, ok := cs.conversations[id]
	if !ok {
		return nil, fmt.Errorf("conversation not found: %s", id)
	}
	
	return conv, nil
}

// Delete deletes a conversation.
func (cs *ConversationStoreMulti) Delete(id string) error {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	
	if _, ok := cs.conversations[id]; !ok {
		return fmt.Errorf("conversation not found: %s", id)
	}
	
	delete(cs.conversations, id)
	
	// Delete file
	convPath := filepath.Join(cs.dir, fmt.Sprintf("conv_%s.json", id))
	os.Remove(convPath)
	
	// If deleted current, switch to another
	if cs.currentID == id {
		if len(cs.conversations) > 0 {
			for newID := range cs.conversations {
				cs.currentID = newID
				break
			}
		} else {
			// Create new default
			cs.currentID = uuid.New().String()
			cs.conversations[cs.currentID] = &Conversation{
				Messages:  []FeedMessage{},
				CreatedAt: time.Now().Format(time.RFC3339),
				UpdatedAt: time.Now().Format(time.RFC3339),
			}
		}
	}
	
	cs.saveIndex()
	
	return nil
}

// CurrentID returns the current conversation ID.
func (cs *ConversationStoreMulti) CurrentID() string {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	
	return cs.currentID
}