package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"charm.land/bubbles/v2/list"
)

type SessionData struct {
	ID        string    `json:"id"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Title     string    `json:"title"`
	Provider  string    `json:"provider"`
	Model     string    `json:"model"`
	Messages  []message `json:"messages"`
	History   []string  `json:"history"`
}

type sessionFile struct {
	Session SessionData `json:"session"`
	Deleted bool        `json:"-"`
}

func currentSession(m *chatbot) *SessionData {
	if m.sessionIdx < 0 || m.sessionIdx >= len(m.sessions) {
		return nil
	}
	return &m.sessions[m.sessionIdx]
}

func dataDir() string {
	dir := os.Getenv("XDG_DATA_HOME")
	if dir == "" {
		home, _ := os.UserHomeDir()
		dir = filepath.Join(home, ".local", "share")
	}
	return filepath.Join(dir, "ludmila-ai-tui")
}

func sessionsDir() string {
	return filepath.Join(dataDir(), "sessions")
}

func sessionPath(id string) string {
	return filepath.Join(sessionsDir(), id+".json")
}

func ListSessions() ([]SessionData, error) {
	dir := sessionsDir()
	entries, err := os.ReadDir(dir)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("read sessions dir: %w", err)
	}

	var sessions []SessionData
	for _, entry := range entries {
		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
			continue
		}
		data, err := os.ReadFile(filepath.Join(dir, entry.Name()))
		if err != nil {
			continue
		}
		var file sessionFile
		if err := json.Unmarshal(data, &file); err != nil {
			continue
		}
		if file.Deleted {
			continue
		}
		sessions = append(sessions, file.Session)
	}

	sort.Slice(sessions, func(i, j int) bool {
		return sessions[i].UpdatedAt.After(sessions[j].UpdatedAt)
	})

	return sessions, nil
}

func SaveSession(s SessionData) error {
	dir := sessionsDir()
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return fmt.Errorf("create sessions dir: %w", err)
	}

	s.UpdatedAt = time.Now()
	file := sessionFile{Session: s}

	data, err := json.MarshalIndent(file, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal session: %w", err)
	}

	if err := os.WriteFile(sessionPath(s.ID), data, 0o644); err != nil {
		return fmt.Errorf("write session: %w", err)
	}

	return nil
}

func DeleteSession(id string) error {
	data, err := os.ReadFile(sessionPath(id))
	if err != nil {
		return fmt.Errorf("read session: %w", err)
	}

	var file sessionFile
	if err := json.Unmarshal(data, &file); err != nil {
		return fmt.Errorf("unmarshal session: %w", err)
	}

	file.Deleted = true
	updatedData, err := json.MarshalIndent(file, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal deleted session: %w", err)
	}

	return os.WriteFile(sessionPath(id), updatedData, 0o644)
}

func NewSessionData(provider, model string) SessionData {
	now := time.Now()

	return SessionData{
		ID:        now.Format("20060102_150405"),
		CreatedAt: now,
		UpdatedAt: now,
		Title:     "New session",
		Provider:  provider,
		Model:     model,
	}
}

func sessionTitle(s SessionData) string {
	if s.Title != "" && s.Title != "New session" {
		return s.Title
	}
	return "New session"
}

func (m *chatbot) saveCurrentSession() {
	if cs := currentSession(m); cs != nil {
		cs.UpdatedAt = time.Now()
		cs.Messages = m.messages
		cs.History = m.history
		cs.Provider = m.currentProvider
		cs.Model = m.currentModel

		if len(cs.Messages) > 0 && (cs.Title == "" || cs.Title == "New session") {
			cs.Title = sessionTitleFromMessages(cs.Messages)
		}

		m.sessions[m.sessionIdx] = *cs
		_ = SaveSession(*cs)
	}
}

func sessionTitleFromMessages(msgs []message) string {
	if len(msgs) == 0 {
		return "New session"
	}
	title := strings.TrimSpace(msgs[0].Content)
	if idx := strings.IndexAny(title, ".\n"); idx > 0 {
		title = title[:idx]
	}
	runes := []rune(title)
	if len(runes) > 40 {
		title = string(runes[:37]) + "..."
	}
	return title
}

type sessionItem struct {
	session SessionData
	active  bool
}

func (s sessionItem) Title() string {
	prefix := "  "
	if s.active {
		prefix = "* "
	}
	return prefix + sessionTitle(s.session)
}

func (s sessionItem) Description() string {
	parts := []string{
		s.session.Provider + "/" + s.session.Model,
		"(" + fmt.Sprintf("%d msgs", len(s.session.Messages)) + ")",
		s.session.UpdatedAt.Format("Jan 02 15:04"),
	}
	return strings.Join(parts, " · ")
}

func (s sessionItem) FilterValue() string {
	return s.session.Title + " " + s.session.Provider + " " + s.session.Model
}

func buildSessionItems(sessions []SessionData, activeIdx int) []list.Item {
	items := make([]list.Item, len(sessions))
	for i, s := range sessions {
		items[i] = sessionItem{session: s, active: i == activeIdx}
	}
	return items
}
