package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"gf-lt/models"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"time"
)

var (
	chatMap = make(map[string]*models.Chat)
)

func historyToSJSON(msgs []models.RoleMsg) (string, error) {
	data, err := json.Marshal(msgs)
	if err != nil {
		return "", err
	}
	if data == nil {
		return "", errors.New("nil data")
	}
	return string(data), nil
}

func exportChat() error {
	data, err := json.MarshalIndent(chatBody.Messages, "", "  ")
	if err != nil {
		return err
	}
	// Ensure the export directory exists
	if err := os.MkdirAll(exportDir, 0755); err != nil {
		return fmt.Errorf("failed to create export directory %s: %w", exportDir, err)
	}
	fp := path.Join(exportDir, activeChatName+".json")
	return os.WriteFile(fp, data, 0666)
}

func importChat(filename string) error {
	data, err := os.ReadFile(filename)
	if err != nil {
		return err
	}
	messages := []models.RoleMsg{}
	if err := json.Unmarshal(data, &messages); err != nil {
		return err
	}
	activeChatName = filepath.Base(filename)
	if _, ok := chatMap[activeChatName]; !ok {
		addNewChat(activeChatName)
	}
	chatBody.Messages = messages
	cfg.AssistantRole = messages[1].Role
	if cfg.AssistantRole == cfg.UserRole {
		cfg.AssistantRole = messages[2].Role
	}
	return nil
}

func updateStorageChat(name string, msgs []models.RoleMsg) error {
	var err error
	chat, ok := chatMap[name]
	if !ok {
		err = fmt.Errorf("failed to find active chat; map:%v; key:%s", chatMap, name)
		logger.Error("failed to find active chat", "map", chatMap, "key", name)
		return err
	}
	chat.Msgs, err = historyToSJSON(msgs)
	if err != nil {
		return err
	}
	chat.UpdatedAt = time.Now()
	// if new chat will create id
	_, err = store.UpsertChat(chat)
	return err
}

func loadHistoryChats() ([]string, error) {
	chats, err := store.ListChats()
	if err != nil {
		return nil, err
	}
	resp := make([]string, len(chats))
	for i, chat := range chats {
		if chat.Name == "" {
			chat.Name = fmt.Sprintf("%d_%v", chat.ID, chat.Agent)
		}
		resp[i] = chat.Name
		chatMap[chat.Name] = &chat
	}
	return resp, nil
}

func loadHistoryChat(chatName string) ([]models.RoleMsg, error) {
	chat, ok := chatMap[chatName]
	if !ok {
		err := errors.New("failed to read chat")
		logger.Error("failed to read chat", "name", chatName)
		return nil, err
	}
	activeChatName = chatName
	cfg.AssistantRole = chat.Agent
	return chat.ToHistory()
}

func loadAgentsLastChat(agent string) ([]models.RoleMsg, error) {
	chat, err := store.GetLastChatByAgent(agent)
	if err != nil {
		return nil, err
	}
	history, err := chat.ToHistory()
	if err != nil {
		return nil, err
	}
	if chat.Name == "" {
		logger.Warn("empty chat name", "id", chat.ID)
		chat.Name = fmt.Sprintf("%s_%d", chat.Agent, chat.ID)
	}
	chatMap[chat.Name] = chat
	activeChatName = chat.Name
	return history, nil
}

func loadOldChatOrGetNew() []models.RoleMsg {
	// find last chat
	chat, err := store.GetLastChat()
	if err != nil {
		logger.Warn("failed to load history chat", "error", err)
		maxID, err := store.ChatGetMaxID()
		if err != nil {
			logger.Error("failed to fetch max chat id", "error", err)
		}
		maxID++
		chat := &models.Chat{
			ID:        maxID,
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			Agent:     cfg.AssistantRole,
		}
		chat.Name = fmt.Sprintf("%s_%v", chat.Agent, chat.ID)
		activeChatName = chat.Name
		chatMap[chat.Name] = chat
		return defaultStarter
	}
	history, err := chat.ToHistory()
	if err != nil {
		logger.Warn("failed to load history chat", "error", err)
		activeChatName = chat.Name
		chatMap[chat.Name] = chat
		return defaultStarter
	}
	chatMap[chat.Name] = chat
	activeChatName = chat.Name
	cfg.AssistantRole = chat.Agent
	return history
}

func copyToClipboard(text string) error {
	var cmd *exec.Cmd
	if _, err := exec.LookPath("xclip"); err == nil {
		cmd = exec.Command("xclip", "-selection", "clipboard")
	} else if _, err := exec.LookPath("wl-copy"); err == nil {
		cmd = exec.Command("wl-copy")
	} else {
		return errors.New("no clipboard tool found (install xclip or wl-clipboard)")
	}
	cmd.Stdin = strings.NewReader(text)
	cmd.Stdout = nil
	cmd.Stderr = nil
	return cmd.Run()
}

func readFromClipboard() (string, error) {
	var cmd *exec.Cmd
	if _, err := exec.LookPath("xclip"); err == nil {
		cmd = exec.Command("xclip", "-selection", "clipboard", "-out")
	} else if _, err := exec.LookPath("wl-paste"); err == nil {
		cmd = exec.Command("wl-paste")
	} else {
		return "", errors.New("no clipboard tool found (install xclip or wl-clipboard)")
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	return out.String(), err
}
