package api

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

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

func (s *Server) handleUpdatePreferences(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		writeError(w, "PUT only", http.StatusMethodNotAllowed)
		return
	}
	if s.config == nil {
		writeError(w, "no config", http.StatusNotFound)
		return
	}
	var body struct {
		Language       string `json:"language"`
		KeyboardLayout string `json:"keyboard_layout"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if body.Language != "" {
		s.config.Profile.Preferences.Language = body.Language
	}
	if body.KeyboardLayout != "" {
		s.config.Profile.Preferences.KeyboardLayout = body.KeyboardLayout
	}
	if err := config.Save(s.config); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]string{"status": "ok"})
}

func (s *Server) handleSaveProfile(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		writeError(w, "PUT only", http.StatusMethodNotAllowed)
		return
	}
	if s.config == nil {
		writeError(w, "no config", http.StatusNotFound)
		return
	}

	currentJSON, err := json.Marshal(s.config.Profile)
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	var currentMap map[string]interface{}
	if err := json.Unmarshal(currentJSON, &currentMap); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var updates map[string]interface{}
	body, err := io.ReadAll(r.Body)
	if err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if err := json.Unmarshal(body, &updates); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}

	deepMerge(currentMap, updates)

	mergedJSON, err := json.Marshal(currentMap)
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err := json.Unmarshal(mergedJSON, &s.config.Profile); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

func deepMerge(dst, src map[string]interface{}) {
	for k, sv := range src {
		if dv, ok := dst[k]; ok {
			dstMap, dOk := dv.(map[string]interface{})
			srcMap, sOk := sv.(map[string]interface{})
			if dOk && sOk {
				deepMerge(dstMap, srcMap)
				continue
			}
		}
		dst[k] = sv
	}
}

func (s *Server) handleSaveProvider(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		writeError(w, "PUT only", http.StatusMethodNotAllowed)
		return
	}
	if s.config == nil {
		writeError(w, "no config", http.StatusNotFound)
		return
	}
	var body struct {
		Name    string `json:"name"`
		APIKey  string `json:"api_key"`
		Model   string `json:"model"`
		BaseURL string `json:"base_url"`
		Active  *bool  `json:"active"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if body.Name == "" {
		writeError(w, "name required", http.StatusBadRequest)
		return
	}
	found := false
	for i := range s.config.AI.Providers {
		if s.config.AI.Providers[i].Name == body.Name {
			if body.APIKey != "" && body.APIKey != "***" {
				s.config.AI.Providers[i].APIKey = body.APIKey
			}
			if body.Model != "" {
				s.config.AI.Providers[i].Model = body.Model
			}
			if body.BaseURL != "" {
				s.config.AI.Providers[i].BaseURL = body.BaseURL
			}
			if body.Active != nil {
				if *body.Active {
					for j := range s.config.AI.Providers {
						s.config.AI.Providers[j].Active = false
					}
				}
				s.config.AI.Providers[i].Active = *body.Active
			}
			found = true
			break
		}
	}
	if !found {
		writeError(w, "provider not found", http.StatusNotFound)
		return
	}
	if err := config.Save(s.config); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]string{"status": "ok"})
}

func (s *Server) handleValidateProvider(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	var body struct {
		Name    string `json:"name"`
		APIKey  string `json:"api_key"`
		Model   string `json:"model"`
		BaseURL string `json:"base_url"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if body.APIKey == "" {
		writeError(w, "api_key required", http.StatusBadRequest)
		return
	}
	if body.APIKey == "***" {
		for _, p := range s.config.AI.Providers {
			if p.Name == body.Name {
				body.APIKey = p.APIKey
				break
			}
		}
	}

	baseURL := body.BaseURL
	if baseURL == "" {
		for _, p := range s.config.AI.Providers {
			if p.Name == body.Name {
				baseURL = p.BaseURL
				break
			}
		}
	}
	if baseURL == "" {
		switch body.Name {
		case "minimax":
			baseURL = "https://api.minimax.io/v1"
		case "mimo":
			baseURL = "https://token-plan-ams.xiaomimimo.com/v1"
		case "openai":
			baseURL = "https://api.openai.com/v1"
		case "anthropic":
			baseURL = "https://api.anthropic.com/v1"
		default:
			baseURL = "https://api.minimax.io/v1"
		}
	}

	model := body.Model
	if model == "" {
		for _, p := range s.config.AI.Providers {
			if p.Name == body.Name {
				model = p.Model
				break
			}
		}
	}
	if model == "" {
		model = "MiniMax-M2.7"
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"model":      model,
		"messages":   []map[string]string{{"role": "user", "content": "Hi"}},
		"max_tokens": 5,
		"stream":     false,
	})

	url := strings.TrimRight(baseURL, "/") + "/chat/completions"
	req, err := http.NewRequest("POST", url, bytes.NewReader(reqBody))
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+body.APIKey)

	client := &http.Client{Timeout: 15 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		writeError(w, "connection failed: "+err.Error(), http.StatusBadGateway)
		return
	}
	defer resp.Body.Close()
	respBody, _ := io.ReadAll(resp.Body)

	if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
		writeError(w, "invalid_api_key", http.StatusUnauthorized)
		return
	}
	if resp.StatusCode != http.StatusOK {
		writeError(w, "api_error: "+string(respBody), http.StatusBadGateway)
		return
	}

	writeJSON(w, map[string]interface{}{"status": "valid"})
}

func (s *Server) handleSaveTerminalSettings(w http.ResponseWriter, r *http.Request) {
	if r.Method != "PUT" {
		writeError(w, "PUT only", http.StatusMethodNotAllowed)
		return
	}
	if s.config == nil {
		writeError(w, "no config", http.StatusNotFound)
		return
	}
	var body struct {
		FontSize   int    `json:"font_size"`
		FontFamily string `json:"font_family"`
		Theme      string `json:"theme"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if body.FontSize > 0 && body.FontSize <= 72 {
		s.config.Terminal.FontSize = body.FontSize
	}
	if body.FontFamily != "" {
		s.config.Terminal.FontFamily = body.FontFamily
	}
	if body.Theme != "" {
		s.config.Terminal.Theme = body.Theme
	}
	if err := config.Save(s.config); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]interface{}{
		"status": "ok",
		"theme":  config.GetTerminalTheme(s.config.Terminal.Theme),
	})
}

func (s *Server) handleGetTerminalThemes(w http.ResponseWriter, r *http.Request) {
	themes := make([]map[string]string, 0, len(config.DEFAULT_TERMINAL_THEMES))
	for id, theme := range config.DEFAULT_TERMINAL_THEMES {
		themes = append(themes, map[string]string{
			"id":   id,
			"name": theme.Name,
		})
	}
	writeJSON(w, map[string]interface{}{"themes": themes})
}

func (s *Server) handleResetConfig(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	dir, err := config.ConfigDir()
	if err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	path := filepath.Join(dir, "config.yaml")
	if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	s.config = config.Default()
	if err := config.Save(s.config); err != nil {
		writeError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJSON(w, map[string]string{"status": "ok"})
}

func (s *Server) handleApplyStarshipTheme(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeError(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	var body struct {
		Theme string `json:"theme"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, err.Error(), http.StatusBadRequest)
		return
	}
	if body.Theme == "" {
		body.Theme = s.config.Terminal.PromptTheme
	}

	themeFile := ApplyStarshipTheme(body.Theme)

	s.config.Terminal.PromptTheme = body.Theme
	config.Save(s.config)

	writeJSON(w, map[string]interface{}{"status": "ok", "config": themeFile})
}

func ApplyStarshipTheme(theme string) string {
	cfgDir, _ := config.ConfigDir()
	starshipDir := filepath.Join(cfgDir, "starship")
	os.MkdirAll(starshipDir, 0755)
	themeFile := filepath.Join(starshipDir, "starship.toml")

	themeContent := getStarshipThemeConfig(theme)
	os.WriteFile(themeFile, []byte(themeContent), 0644)

	home, _ := os.UserHomeDir()
	for _, rc := range []string{filepath.Join(home, ".bashrc"), filepath.Join(home, ".zshrc")} {
		if _, err := os.Stat(rc); err != nil {
			continue
		}
		content, _ := os.ReadFile(rc)
		if strings.Contains(string(content), "STARSHIP_CONFIG") {
			continue
		}
		exportLine := fmt.Sprintf("\n# Muyue Starship config\nexport STARSHIP_CONFIG=%s\n", themeFile)
		f, err := os.OpenFile(rc, os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			continue
		}
		f.WriteString(exportLine)
		f.Close()
	}

	return themeFile
}

func getStarshipThemeConfig(theme string) string {
	switch theme {
	case "charm":
		return `[format]
before_format = "$"
format = """
$username$directory$git_branch$git_status$cmd_duration$line_break$character"""

[character]
success_symbol = "[➜](bold #00E676)"
error_symbol = "[✗](bold #FF0033)"

[directory]
truncation_length = 3
truncation_symbol = "…/"
style = "bold #00BCD4"

[username]
show_on_left = false
style_user = "bold #FF0033"
style_root = "bold #FF0033"

[git_branch]
symbol = " "
format = "on [$symbol$branch]($style)"
style = "bold #FFD740"

[git_status]
format = "[$all_status$ahead_behind]($style) "
style = "bold #FF1A5E"
conflicted = "!"
untracked = "?"
modified = "~"
staged = "[+]"
renamed = "»"
deleted = "-"

[cmd_duration]
min_time = 500
format = "took [$duration]($style)"
style = "bold #75715E"
`
	case "zerotwo":
		return `[format]
before_format = "$"
format = """
$username$directory$git_branch$git_status$cmd_duration$line_break$character"""

[character]
success_symbol = "[❯](bold #3B82F6)"
error_symbol = "[❯](bold #EF4444)"

[directory]
truncation_length = 3
truncation_symbol = "…/"
style = "bold #8B5CF6"

[username]
show_on_left = false
style_user = "bold #EC4899"
style_root = "bold #EF4444"

[git_branch]
symbol = " "
format = "on [$symbol$branch]($style)"
style = "bold #F472B6"

[git_status]
format = "[$all_status$ahead_behind]($style) "
style = "bold #EF4444"
conflicted = "!"
untracked = "?"
modified = "~"
staged = "[+]"
renamed = "»"
deleted = "-"

[cmd_duration]
min_time = 500
format = "took [$duration]($style)"
style = "bold #6B7280"
`
	default:
		return `[format]
before_format = "$"
format = """
$username$directory$git_branch$git_status$line_break$character"""

[character]
success_symbol = "[❯](bold green)"
error_symbol = "[❯](bold red)"

[directory]
truncation_length = 3
truncation_symbol = "…/"
style = "bold cyan"

[username]
show_on_left = false
style_user = "bold red"
style_root = "bold red"

[git_branch]
symbol = " "
format = "on [$symbol$branch]($style)"
style = "bold yellow"

[cmd_duration]
min_time = 500
format = "took [$duration]($style)"
style = "bold bright-black"
`
	}
}
