package api

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/muyue/muyue/internal/agent"
	"github.com/muyue/muyue/internal/lsp"
	"github.com/muyue/muyue/internal/mcp"
	"github.com/muyue/muyue/internal/scanner"
	"github.com/muyue/muyue/internal/skills"
	"github.com/muyue/muyue/internal/version"
)

func (s *Server) handleInfo(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, map[string]interface{}{
		"name":    version.Name,
		"version": version.Version,
		"author":  version.Author,
		"sudo":    !agent.NeedsSudoPassword(),
	})
}

func (s *Server) handleSystem(w http.ResponseWriter, r *http.Request) {
	if s.scanResult == nil {
		s.scanResult = scanner.ScanSystem()
	}
	writeJSON(w, map[string]interface{}{
		"system": s.scanResult.System,
	})
}

func (s *Server) handleTools(w http.ResponseWriter, r *http.Request) {
	if s.scanResult == nil {
		s.scanResult = scanner.ScanSystem()
	}
	type toolInfo struct {
		Name      string `json:"name"`
		Installed bool   `json:"installed"`
		Version   string `json:"version"`
		Path      string `json:"path"`
	}
	tools := make([]toolInfo, len(s.scanResult.Tools))
	for i, t := range s.scanResult.Tools {
		tools[i] = toolInfo{
			Name:      t.Name,
			Installed: t.Installed,
			Version:   t.Version,
			Path:      t.Path,
		}
	}
	writeJSON(w, map[string]interface{}{
		"tools": tools,
		"total": len(tools),
	})
}

func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
	if s.config == nil {
		writeError(w, "no config", http.StatusNotFound)
		return
	}
	writeJSON(w, map[string]interface{}{
		"profile":  s.config.Profile,
		"terminal": s.config.Terminal,
		"bmad":     s.config.BMAD,
	})
}

func (s *Server) handleProviders(w http.ResponseWriter, r *http.Request) {
	if s.config == nil {
		writeError(w, "no config", http.StatusNotFound)
		return
	}
	masked := make([]map[string]interface{}, 0, len(s.config.AI.Providers))
	for _, p := range s.config.AI.Providers {
		entry := map[string]interface{}{
			"name":     p.Name,
			"model":    p.Model,
			"base_url": p.BaseURL,
			"active":   p.Active,
		}
		if p.APIKey != "" {
			entry["api_key"] = "***"
		} else {
			entry["api_key"] = ""
		}
		masked = append(masked, entry)
	}
	writeJSON(w, map[string]interface{}{
		"providers": masked,
	})
}

func (s *Server) handleSkills(w http.ResponseWriter, r *http.Request) {
	list, err := skills.List()
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	for i := range list {
		list[i].Deployed = skills.IsDeployed(list[i].Name)
	}
	writeJSON(w, map[string]interface{}{
		"skills": list,
		"count":  len(list),
	})
}

func (s *Server) handleLSP(w http.ResponseWriter, r *http.Request) {
	servers := lsp.ScanServers()
	writeJSON(w, map[string]interface{}{
		"servers": servers,
	})
}

func (s *Server) handleMCP(w http.ResponseWriter, r *http.Request) {
	servers := mcp.ScanServers()
	home, _ := os.UserHomeDir()
	editors := mcp.DetectInstalledEditors(home)
	statuses := mcp.GetAllStatuses()
	writeJSON(w, map[string]interface{}{
		"servers":         servers,
		"configured":      true,
		"detected_editors": editors,
		"statuses":        statuses,
	})
}

func (s *Server) handleMCPConfigure(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		Editor string `json:"editor,omitempty"`
	}
	if r.Body != nil {
		json.NewDecoder(r.Body).Decode(&body)
	}

	if body.Editor != "" {
		if err := mcp.ConfigureForEditor(s.config, body.Editor); err != nil {
			writeError(w, err.Error(), http.StatusInternalServerError)
			return
		}
	} else {
		if err := mcp.ConfigureAll(s.config); err != nil {
			writeError(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	writeJSON(w, map[string]string{"status": "ok"})
}

func (s *Server) handleMCPStatus(w http.ResponseWriter, r *http.Request) {
	statuses := mcp.GetAllStatuses()
	writeJSON(w, map[string]interface{}{
		"statuses": statuses,
	})
}

func (s *Server) handleMCPRegistry(w http.ResponseWriter, r *http.Request) {
	reg, err := mcp.LoadRegistry()
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]interface{}{
		"registry": reg,
	})
}

func (s *Server) handleLSPHealth(w http.ResponseWriter, r *http.Request) {
	servers := lsp.ScanServers()
	type healthInfo struct {
		Name      string `json:"name"`
		Language  string `json:"language"`
		Installed bool   `json:"installed"`
		Healthy   bool   `json:"healthy"`
		Detail    string `json:"detail,omitempty"`
	}
	var results []healthInfo
	for _, srv := range servers {
		healthy, detail := lsp.HealthCheck(srv.Name)
		results = append(results, healthInfo{
			Name:      srv.Name,
			Language:  srv.Language,
			Installed: srv.Installed,
			Healthy:   healthy,
			Detail:    detail,
		})
	}
	writeJSON(w, map[string]interface{}{
		"servers": results,
	})
}

func (s *Server) handleLSPAutoInstall(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		ProjectDir string `json:"project_dir,omitempty"`
	}
	if r.Body != nil {
		json.NewDecoder(r.Body).Decode(&body)
	}

	home, _ := os.UserHomeDir()
	if body.ProjectDir == "" {
		body.ProjectDir = home
	} else {
		abs, err := filepath.Abs(body.ProjectDir)
		if err != nil {
			writeError(w, "invalid project_dir", http.StatusBadRequest)
			return
		}
		body.ProjectDir = abs
		if home != "" && !strings.HasPrefix(abs, home+string(filepath.Separator)) && abs != home {
			writeError(w, "project_dir must be within user home", http.StatusBadRequest)
			return
		}
	}

	results, err := lsp.AutoInstallForProject(body.ProjectDir)
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]interface{}{
		"results": results,
	})
}

func (s *Server) handleLSPEditorConfig(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		Editor string   `json:"editor"`
		Names  []string `json:"names,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	allServers := lsp.ScanServers()
	var selected []lsp.LSPServer
	if len(body.Names) > 0 {
		nameSet := map[string]bool{}
		for _, n := range body.Names {
			nameSet[n] = true
		}
		for _, srv := range allServers {
			if nameSet[srv.Name] {
				selected = append(selected, srv)
			}
		}
	} else {
		for _, srv := range allServers {
			if srv.Installed {
				selected = append(selected, srv)
			}
		}
	}

	config, err := lsp.GenerateEditorConfigs(selected, body.Editor, "")
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJSON(w, map[string]interface{}{
		"editor": body.Editor,
		"config": config,
	})
}

func (s *Server) handleSkillValidate(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		Name string `json:"name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	skill, err := skills.Get(body.Name)
	if err != nil {
		writeError(w, err.Error(), http.StatusNotFound)
		return
	}

	errs := skills.Validate(skill)
	writeJSON(w, map[string]interface{}{
		"name":         body.Name,
		"valid":        len(errs) == 0,
		"errors":       errs,
		"dependencies": skills.CheckDependencies(skill),
	})
}

func (s *Server) handleSkillTest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		Name       string `json:"name"`
		SampleTask string `json:"sample_task,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	result := skills.DryRun(body.Name, body.SampleTask)
	writeJSON(w, result)
}

func (s *Server) handleSkillExport(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		Name       string `json:"name"`
		ExportPath string `json:"export_path"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	home, _ := os.UserHomeDir()
	if body.ExportPath == "" {
		body.ExportPath = home + "/.muyue/exports/" + body.Name + ".md"
	}

	if err := skills.Export(body.Name, body.ExportPath); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]string{"status": "ok", "path": body.ExportPath})
}

func (s *Server) handleSkillImport(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}

	var body struct {
		ImportPath string `json:"import_path"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	skill, err := skills.Import(body.ImportPath)
	if err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err := skills.Create(skill); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]interface{}{"status": "ok", "skill": skill.Name})
}

func (s *Server) handleDashboardStatus(w http.ResponseWriter, r *http.Request) {
	mcpStatuses := mcp.GetAllStatuses()
	lspServers := lsp.ScanServers()
	skillList, _ := skills.List()

	mcpHealthy := 0
	mcpTotal := len(mcpStatuses)
	for _, st := range mcpStatuses {
		if st.Healthy {
			mcpHealthy++
		}
	}

	lspInstalled := 0
	lspTotal := len(lspServers)
	for _, srv := range lspServers {
		if srv.Installed {
			lspInstalled++
		}
	}

	skillsDeployed := len(skillList)
	var skillIssues []string
	for _, sk := range skillList {
		missing := skills.CheckDependencies(&sk)
		if len(missing) > 0 {
			for _, dep := range missing {
				skillIssues = append(skillIssues, sk.Name+": missing "+dep.Type+" "+dep.Name)
			}
		}
	}

	writeJSON(w, map[string]interface{}{
		"mcp": map[string]interface{}{
			"total":   mcpTotal,
			"healthy": mcpHealthy,
			"servers": mcpStatuses,
		},
		"lsp": map[string]interface{}{
			"total":     lspTotal,
			"installed": lspInstalled,
			"servers":   lspServers,
		},
		"skills": map[string]interface{}{
			"total":    skillsDeployed,
			"issues":   skillIssues,
			"deployed": skillList,
		},
	})
}

func (s *Server) handleScan(w http.ResponseWriter, r *http.Request) {
	s.scanResult = scanner.ScanSystem()
	writeJSON(w, map[string]string{"status": "ok"})
}

func (s *Server) handleEditors(w http.ResponseWriter, r *http.Request) {
	editors := scanner.ScanEditors()
	writeJSON(w, map[string]interface{}{"editors": editors})
}

func (s *Server) handleProvidersQuota(w http.ResponseWriter, r *http.Request) {
	type providerQuota struct {
		Name    string                 `json:"name"`
		Active  bool                   `json:"active"`
		Healthy bool                   `json:"healthy"`
		Data    map[string]interface{} `json:"data,omitempty"`
		Error   string                 `json:"error,omitempty"`
	}
	var results []providerQuota
	client := &http.Client{Timeout: 8 * time.Second}
	for _, p := range s.config.AI.Providers {
		q := providerQuota{Name: p.Name, Active: p.Active}
		switch p.Name {
		case "minimax":
			if p.APIKey == "" {
				q.Error = "no API key"
				results = append(results, q)
				continue
			}
			req, _ := http.NewRequest("GET", "https://api.minimax.io/v1/token_plan/remains", nil)
			req.Header.Set("Authorization", "Bearer "+p.APIKey)
			req.Header.Set("Content-Type", "application/json")
			resp, err := client.Do(req)
			if err != nil {
				q.Error = err.Error()
			} else {
				body, _ := io.ReadAll(resp.Body)
				resp.Body.Close()
				var data map[string]interface{}
				if json.Unmarshal(body, &data) == nil {
					if models, ok := data["model_remains"].([]interface{}); ok {
						filtered := make([]map[string]interface{}, 0)
						for _, m := range models {
							if mm, ok := m.(map[string]interface{}); ok {
								usage, _ := mm["current_interval_usage_count"].(float64)
								total, _ := mm["current_interval_total_count"].(float64)
								if total > 0 {
									filtered = append(filtered, map[string]interface{}{
										"model":       mm["model_name"],
										"used":        usage,
										"total":       total,
										"remaining":   total - usage,
										"weekly_used": mm["current_weekly_usage_count"],
										"weekly_total": mm["current_weekly_total_count"],
									})
								}
							}
						}
						q.Data = map[string]interface{}{"models": filtered}
						q.Healthy = true
					}
				}
			}
		case "zai":
			if p.APIKey == "" {
				q.Error = "no API key"
				results = append(results, q)
				continue
			}
			req, _ := http.NewRequest("GET", "https://api.z.ai/api/monitor/usage/quota/limit", nil)
			req.Header.Set("Authorization", "Bearer "+p.APIKey)
			req.Header.Set("Accept", "application/json")
			resp, err := client.Do(req)
			if err != nil {
				q.Error = err.Error()
			} else {
				body, _ := io.ReadAll(resp.Body)
				resp.Body.Close()
				var data map[string]interface{}
				if json.Unmarshal(body, &data) == nil {
					if d, ok := data["data"].(map[string]interface{}); ok {
						if limits, ok := d["limits"].([]interface{}); ok {
							models := make([]map[string]interface{}, 0)
							for _, l := range limits {
								if lm, ok := l.(map[string]interface{}); ok {
									name := "Z.AI"
									if model, ok := lm["model"].(string); ok && model != "" {
										name = model
									} else if t, ok := lm["type"].(string); ok && t != "TIME_LIMIT" {
										name = t
									}
									usage, _ := lm["usage"].(float64)
									remaining, _ := lm["remaining"].(float64)
									limitVal, hasLimit := lm["limit"].(float64)
									total := usage + remaining
									if hasLimit && limitVal > 0 {
										total = limitVal
									}
									if total > 0 {
										models = append(models, map[string]interface{}{
											"model":     name,
											"used":      usage,
											"total":     total,
											"remaining": remaining,
										})
									}
								}
							}
							if len(models) > 0 {
								q.Data = map[string]interface{}{"models": models}
								q.Healthy = true
							}
						}
					}
				}
			}
		case "mimo":
			q.Healthy = p.APIKey != ""
			if p.APIKey == "" {
				q.Error = "no API key"
				results = append(results, q)
				continue
			}
			mimoBase := p.BaseURL
			if mimoBase == "" {
				mimoBase = "https://token-plan-ams.xiaomimimo.com/v1"
			}
			req, _ := http.NewRequest("GET", strings.TrimRight(mimoBase, "/")+"/models", nil)
			req.Header.Set("Authorization", "Bearer "+p.APIKey)
			resp, err := client.Do(req)
			if err != nil {
				q.Error = err.Error()
			} else {
				body, _ := io.ReadAll(resp.Body)
				resp.Body.Close()
				var data map[string]interface{}
				if json.Unmarshal(body, &data) == nil {
					if modelList, ok := data["data"].([]interface{}); ok {
						models := make([]map[string]interface{}, 0)
						for _, m := range modelList {
							if mm, ok := m.(map[string]interface{}); ok {
								id, _ := mm["id"].(string)
								if id != "" {
									models = append(models, map[string]interface{}{
										"model": id,
									})
								}
							}
						}
						q.Data = map[string]interface{}{"models": models, "available": len(models)}
						q.Healthy = true
					}
				}
			}
		case "claude", "anthropic":
			// Claude Code n'a pas d'API externe, vérifier l'installation
			claudePath := "/usr/bin/claude"
			if _, err := os.Stat(claudePath); err == nil {
				q.Healthy = true
			} else {
				q.Error = "claude code not installed"
			}
		default:
			q.Error = "quota not supported"
		}
		results = append(results, q)
	}
	writeJSON(w, map[string]interface{}{"providers": results})
}

func (s *Server) handleProvidersConsumption(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		writeError(w, "GET only", http.StatusMethodNotAllowed)
		return
	}
	data := s.consumption.GetAll()
	writeJSON(w, map[string]interface{}{"providers": data})
}

func (s *Server) handleRecentCommands(w http.ResponseWriter, r *http.Request) {
	home, _ := os.UserHomeDir()
	type cmdEntry struct {
		Cmd     string `json:"cmd"`
		Shell   string `json:"shell"`
	}

	var entries []cmdEntry

	for _, histFile := range []string{".bash_history", ".zsh_history"} {
		path := filepath.Join(home, histFile)
		data, err := os.ReadFile(path)
		if err != nil {
			continue
		}
		shell := "bash"
		if strings.Contains(histFile, "zsh") {
			shell = "zsh"
		}
		lines := strings.Split(string(data), "\n")
		start := len(lines) - 50
		if start < 0 {
			start = 0
		}

		for i := len(lines) - 1; i >= start; i-- {
			line := strings.TrimSpace(lines[i])
			if line == "" || strings.HasPrefix(line, "#") {
				continue
			}
			if strings.HasPrefix(line, ": ") {
				parts := strings.SplitN(line, ";", 2)
				if len(parts) == 2 {
					line = strings.TrimSpace(parts[1])
				} else {
					continue
				}
			}
			if line == "" {
				continue
			}

			base := strings.Fields(line)[0]
			if len(base) < 2 {
				continue
			}
			if !regexp.MustCompile(`^[a-zA-Z@./]`).MatchString(base) {
				continue
			}

			entries = append(entries, cmdEntry{Cmd: line, Shell: shell})
		}
	}

	max := 20
	if len(entries) > max {
		entries = entries[:max]
	}

	writeJSON(w, map[string]interface{}{"commands": entries})
}

func (s *Server) handleRunningProcesses(w http.ResponseWriter, r *http.Request) {
	type proc struct {
		PID     int    `json:"pid"`
		Name    string `json:"name"`
		Command string `json:"command"`
		CPU     string `json:"cpu"`
		Mem     string `json:"mem"`
	}
	var procs []proc

	editors := []string{"code", "nvim", "vim", "emacs", "hx", "subl", "zed", "cursor"}
	langs := []string{"node", "python", "java", "go", "rustc", "cargo", "ruby", "php"}
	interesting := append(editors, langs...)
	interesting = append(interesting, "muyue")

	cmd := exec.Command("ps", "aux")
	out, err := cmd.Output()
	if err != nil {
		writeJSON(w, map[string]interface{}{"processes": procs})
		return
	}

	lines := strings.Split(string(out), "\n")
	for _, line := range lines[1:] {
		fields := strings.Fields(line)
		if len(fields) < 11 {
			continue
		}
		fullCmd := strings.Join(fields[10:], " ")
		name := filepath.Base(fields[10])
		matched := false
		for _, pattern := range interesting {
			if strings.Contains(name, pattern) || strings.Contains(strings.ToLower(fullCmd), pattern) {
				matched = true
				break
			}
		}
		if !matched {
			continue
		}
		var pid int
		fmt.Sscanf(fields[1], "%d", &pid)
		procs = append(procs, proc{
			PID:     pid,
			Name:    name,
			Command: fullCmd,
			CPU:     fields[2],
			Mem:     fields[3],
		})
	}

	writeJSON(w, map[string]interface{}{"processes": procs})
}

type sysMetrics struct {
	CPUPercent float64 `json:"cpu_percent"`
	MemPercent float64 `json:"mem_percent"`
	MemUsedMB  float64 `json:"mem_used_mb"`
	MemTotalMB float64 `json:"mem_total_mb"`
	NetRxKBs   float64 `json:"net_rx_kbs"`
	NetTxKBs   float64 `json:"net_tx_kbs"`
}

var (
	lastCPU    [2]float64
	lastNet    [2]float64
	lastNetTs  time.Time
	lastCPUSet bool
)

func (s *Server) handleSystemMetrics(w http.ResponseWriter, r *http.Request) {
	m := collectSystemMetrics()
	writeJSON(w, m)
}
