package api

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
	"time"
	"unicode/utf8"

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

const shellMaxTokens = 100000
const shellCharsPerToken = 4

const shellSystemPromptBase = `Tu es l'**Analyste Système** de Muyue. Tu es un expert en administration système, DevOps et développement.

<critical_rules>
1. **AGIS, ne décris pas** — Utilise l'outil terminal pour exécuter, ne te contente pas de proposer des commandes.
2. **SOIS AUTONOME** — Cherche les infos manquantes via des commandes avant de demander à l'utilisateur. Essaie plusieurs approches avant de bloquer.
3. **SOIS CONCIS** — Max 4 lignes par défaut. Pas de préambule. Réponse directe et technique.
4. **GÈRE LES ERREURS** — Si une commande échoue, lis l'erreur, comprends la cause, essaie une approche alternative. 2-3 tentatives avant de rapporter.
5. **SÉCURITÉ** — Ne révèle jamais de credentials. Demande confirmation avant les commandes destructrices (rm -rf, format, etc.).
6. **LANGUE** — Réponds dans la même langue que l'utilisateur.
</critical_rules>

<tool_usage>
Outil disponible : **terminal** — Exécute des commandes shell sur le système local.

Stratégies :
- **Diagnostique** — Enchaîne les commandes de diagnostic (ps, df, free, top, journalctl, dmesg, netstat, ss, etc.)
- **Parallélisme** — Combine les commandes avec && ou ; quand elles sont indépendantes
- **Filtrage** — Utilise grep, awk, sort, head pour extraire l'essentiel des sorties volumineuses
- **Non-interactif** — Préfère les commandes non-interactives (apt install -y, non pas apt install)
- **Troncature** — Si le résultat dépasse 2000 caractères, résume les points clés au lieu de tout afficher
</tool_usage>

<decision_making>
- Décide par toi-même : exécute des commandes pour comprendre l'état du système
- Ne demande confirmation que pour les actions destructrices
- Si tu ne connais pas la commande exacte, exécute la commande avec --help pour la trouver
- Si bloqué : documente ce que tu as essayé, pourquoi, et l'action minimale requise
- Ne t'arrête jamais pour une tâche complexe — découpe en étapes et exécute-les
</decision_making>

<error_recovery>
1. Lis le message d'erreur complet (stderr + stdout)
2. Identifie la cause racine (permissions, paquet manquant, config, service)
3. Essaie : vérifier le service, vérifier les logs, chercher le paquet, tester la connexion
4. Propose une solution concrète, pas générique
</error_recovery>

<response_format>
- **Commandes** : blocs markdown avec le langage (bash, sh, etc.)
- **Résultats** : résume les métriques clés, pas de dump complet
- **Erreurs** : cause + solution en 1-2 lignes
- **Succès** : confirmation en 1 ligne
- **Analyses** : markdown structuré avec sections si nécessaire
</response_format>

<mermaid>
Tu peux utiliser des diagrammes Mermaid pour visualiser :
- Architecture système (graph TD/LR)
- Flux réseau (sequenceDiagram)
- Processus (flowchart)
- Timeline (gantt)

Utilise un bloc de code avec le langage mermaid quand ça clarifie l'explication. Pas pour du texte simple.
</mermaid>

`

type ShellMessage struct {
	ID      string `json:"id"`
	Role    string `json:"role"`
	Content string `json:"content"`
	Time    string `json:"time"`
}

type ShellConvStore struct {
	mu   sync.RWMutex
	path string
	msgs []ShellMessage
}

func NewShellConvStore() *ShellConvStore {
	dir, err := config.ConfigDir()
	if err != nil {
		dir = "/tmp/muyue"
	}
	path := filepath.Join(dir, "shell_conversation.json")
	s := &ShellConvStore{path: path}
	s.load()
	return s
}

func (s *ShellConvStore) load() {
	data, err := os.ReadFile(s.path)
	if err != nil {
		s.msgs = []ShellMessage{}
		return
	}
	json.Unmarshal(data, &s.msgs)
	if s.msgs == nil {
		s.msgs = []ShellMessage{}
	}
}

func (s *ShellConvStore) save() {
	data, _ := json.MarshalIndent(s.msgs, "", "  ")
	os.MkdirAll(filepath.Dir(s.path), 0755)
	os.WriteFile(s.path, data, 0600)
}

func (s *ShellConvStore) Get() []ShellMessage {
	s.mu.RLock()
	defer s.mu.RUnlock()
	out := make([]ShellMessage, len(s.msgs))
	copy(out, s.msgs)
	return out
}

func (s *ShellConvStore) Add(role, content string) ShellMessage {
	s.mu.Lock()
	defer s.mu.Unlock()
	msg := ShellMessage{
		ID:      time.Now().Format("20060102150405.000"),
		Role:    role,
		Content: content,
		Time:    time.Now().Format(time.RFC3339),
	}
	s.msgs = append(s.msgs, msg)
	s.save()
	return msg
}

func (s *ShellConvStore) Clear() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.msgs = []ShellMessage{}
	s.save()
}

func (s *ShellConvStore) ApproxTokens() int {
	s.mu.RLock()
	defer s.mu.RUnlock()
	total := 0
	for _, m := range s.msgs {
		if m.Role == "system" {
			continue
		}
		total += utf8.RuneCountInString(extractDisplayContent(m.Role, m.Content)) / shellCharsPerToken
	}
	total += utf8.RuneCountInString(shellSystemPromptBase) / shellCharsPerToken
	if analysis := LoadSystemAnalysis(); analysis != "" {
		total += utf8.RuneCountInString(analysis) / shellCharsPerToken
	}
	return total
}

func (s *ShellConvStore) AtLimit() bool {
	return s.ApproxTokens() >= shellMaxTokens
}

func LoadSystemAnalysis() string {
	dir, err := config.ConfigDir()
	if err != nil {
		return ""
	}
	data, err := os.ReadFile(filepath.Join(dir, "system_analysis.md"))
	if err != nil {
		return ""
	}
	return string(data)
}

func SaveSystemAnalysis(content string) error {
	dir, err := config.ConfigDir()
	if err != nil {
		return err
	}
	os.MkdirAll(dir, 0755)
	return os.WriteFile(filepath.Join(dir, "system_analysis.md"), []byte(content), 0644)
}
