package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
	"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
	"github.com/AvengeMedia/DankMaterialShell/core/internal/greeter"
	"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
	"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
	"github.com/spf13/cobra"
)

var setupCmd = &cobra.Command{
	Use:               "setup",
	Short:             "Deploy DMS configurations",
	Long:              "Deploy compositor and terminal configurations with interactive prompts",
	PersistentPreRunE: requireMutableSystemCommand,
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetup(); err != nil {
			log.Fatalf("Error during setup: %v", err)
		}
	},
}

var setupBindsCmd = &cobra.Command{
	Use:   "binds",
	Short: "Deploy default keybinds config",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("binds"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

var setupLayoutCmd = &cobra.Command{
	Use:   "layout",
	Short: "Deploy default layout config",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("layout"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

var setupColorsCmd = &cobra.Command{
	Use:   "colors",
	Short: "Deploy default colors config",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("colors"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

var setupAlttabCmd = &cobra.Command{
	Use:   "alttab",
	Short: "Deploy default alt-tab config (niri only)",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("alttab"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

var setupOutputsCmd = &cobra.Command{
	Use:   "outputs",
	Short: "Deploy default outputs config",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("outputs"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

var setupCursorCmd = &cobra.Command{
	Use:   "cursor",
	Short: "Deploy default cursor config",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("cursor"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

var setupWindowrulesCmd = &cobra.Command{
	Use:   "windowrules",
	Short: "Deploy default window rules config",
	Run: func(cmd *cobra.Command, args []string) {
		if err := runSetupDmsConfig("windowrules"); err != nil {
			log.Fatalf("Error: %v", err)
		}
	},
}

type dmsConfigSpec struct {
	niriFile    string
	hyprFile    string
	niriContent func(terminal string) string
	hyprContent func(terminal string) string
}

var dmsConfigSpecs = map[string]dmsConfigSpec{
	"binds": {
		niriFile: "binds.kdl",
		hyprFile: "binds.conf",
		niriContent: func(t string) string {
			return strings.ReplaceAll(config.NiriBindsConfig, "{{TERMINAL_COMMAND}}", t)
		},
		hyprContent: func(t string) string {
			return strings.ReplaceAll(config.HyprBindsConfig, "{{TERMINAL_COMMAND}}", t)
		},
	},
	"layout": {
		niriFile:    "layout.kdl",
		hyprFile:    "layout.conf",
		niriContent: func(_ string) string { return config.NiriLayoutConfig },
		hyprContent: func(_ string) string { return config.HyprLayoutConfig },
	},
	"colors": {
		niriFile:    "colors.kdl",
		hyprFile:    "colors.conf",
		niriContent: func(_ string) string { return config.NiriColorsConfig },
		hyprContent: func(_ string) string { return config.HyprColorsConfig },
	},
	"alttab": {
		niriFile:    "alttab.kdl",
		niriContent: func(_ string) string { return config.NiriAlttabConfig },
	},
	"outputs": {
		niriFile:    "outputs.kdl",
		hyprFile:    "outputs.conf",
		niriContent: func(_ string) string { return "" },
		hyprContent: func(_ string) string { return "" },
	},
	"cursor": {
		niriFile:    "cursor.kdl",
		hyprFile:    "cursor.conf",
		niriContent: func(_ string) string { return "" },
		hyprContent: func(_ string) string { return "" },
	},
	"windowrules": {
		niriFile:    "windowrules.kdl",
		hyprFile:    "windowrules.conf",
		niriContent: func(_ string) string { return "" },
		hyprContent: func(_ string) string { return "" },
	},
}

func detectTerminal() (string, error) {
	terminals := []string{"ghostty", "foot", "kitty", "alacritty"}
	var found []string
	for _, t := range terminals {
		if utils.CommandExists(t) {
			found = append(found, t)
		}
	}

	switch len(found) {
	case 0:
		return "ghostty", nil
	case 1:
		return found[0], nil
	}

	fmt.Println("Multiple terminals detected:")
	for i, t := range found {
		fmt.Printf("%d) %s\n", i+1, t)
	}
	fmt.Printf("\nChoice (1-%d): ", len(found))

	var response string
	fmt.Scanln(&response)
	response = strings.TrimSpace(response)

	choice := 0
	fmt.Sscanf(response, "%d", &choice)
	if choice < 1 || choice > len(found) {
		return "", fmt.Errorf("invalid choice")
	}
	return found[choice-1], nil
}

func detectCompositorForSetup() (string, error) {
	compositors := greeter.DetectCompositors()

	switch len(compositors) {
	case 0:
		return "", fmt.Errorf("no supported compositors found (niri or Hyprland required)")
	case 1:
		return strings.ToLower(compositors[0]), nil
	}

	selected, err := greeter.PromptCompositorChoice(compositors)
	if err != nil {
		return "", err
	}
	return strings.ToLower(selected), nil
}

func runSetupDmsConfig(name string) error {
	spec, ok := dmsConfigSpecs[name]
	if !ok {
		return fmt.Errorf("unknown config: %s", name)
	}

	compositor, err := detectCompositorForSetup()
	if err != nil {
		return err
	}

	var filename string
	var contentFn func(string) string
	switch compositor {
	case "niri":
		filename = spec.niriFile
		contentFn = spec.niriContent
	case "hyprland":
		filename = spec.hyprFile
		contentFn = spec.hyprContent
	default:
		return fmt.Errorf("unsupported compositor: %s", compositor)
	}

	if filename == "" {
		return fmt.Errorf("%s is not supported for %s", name, compositor)
	}

	var dmsDir string
	switch compositor {
	case "niri":
		dmsDir = filepath.Join(os.Getenv("HOME"), ".config", "niri", "dms")
	case "hyprland":
		dmsDir = filepath.Join(os.Getenv("HOME"), ".config", "hypr", "dms")
	}

	if err := os.MkdirAll(dmsDir, 0o755); err != nil {
		return fmt.Errorf("failed to create dms directory: %w", err)
	}

	path := filepath.Join(dmsDir, filename)
	if info, err := os.Stat(path); err == nil && info.Size() > 0 {
		return fmt.Errorf("%s already exists and is not empty: %s", name, path)
	}

	terminal := "ghostty"
	if contentFn != nil && name == "binds" {
		terminal, err = detectTerminal()
		if err != nil {
			return err
		}
	}

	content := contentFn(terminal)
	if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
		return fmt.Errorf("failed to write %s: %w", filename, err)
	}

	fmt.Printf("Deployed %s to %s\n", name, path)
	return nil
}

func runSetup() error {
	fmt.Println("=== DMS Configuration Setup ===")

	wm, wmSelected := promptCompositor()
	terminal, terminalSelected := promptTerminal()
	useSystemd := promptSystemd()

	if !wmSelected && !terminalSelected {
		fmt.Println("No configurations selected. Exiting.")
		return nil
	}

	if wmSelected || terminalSelected {
		willBackup := checkExistingConfigs(wm, wmSelected, terminal, terminalSelected)
		if willBackup {
			fmt.Println("\n⚠ Existing configurations will be backed up with timestamps.")
		}

		fmt.Print("\nProceed with deployment? (y/N): ")
		var response string
		fmt.Scanln(&response)
		response = strings.ToLower(strings.TrimSpace(response))

		if response != "y" && response != "yes" {
			fmt.Println("Setup cancelled.")
			return nil
		}
	}

	fmt.Println("\nDeploying configurations...")
	logChan := make(chan string, 100)
	deployer := config.NewConfigDeployer(logChan)

	go func() {
		for msg := range logChan {
			fmt.Println("  " + msg)
		}
	}()

	ctx := context.Background()
	var results []config.DeploymentResult
	var err error

	if wmSelected && terminalSelected {
		results, err = deployer.DeployConfigurationsWithSystemd(ctx, wm, terminal, useSystemd)
	} else if wmSelected {
		results, err = deployer.DeployConfigurationsWithSystemd(ctx, wm, deps.TerminalGhostty, useSystemd)
		if len(results) > 1 {
			results = results[:1]
		}
	} else if terminalSelected {
		results, err = deployer.DeployConfigurationsWithSystemd(ctx, deps.WindowManagerNiri, terminal, useSystemd)
		if len(results) > 0 && results[0].ConfigType == "Niri" {
			results = results[1:]
		}
	}

	close(logChan)

	if err != nil {
		return fmt.Errorf("deployment failed: %w", err)
	}

	fmt.Println("\n=== Deployment Complete ===")
	for _, result := range results {
		if result.Deployed {
			fmt.Printf("✓ %s: %s\n", result.ConfigType, result.Path)
			if result.BackupPath != "" {
				fmt.Printf("  Backup: %s\n", result.BackupPath)
			}
		}
	}

	return nil
}

func promptCompositor() (deps.WindowManager, bool) {
	fmt.Println("Select compositor:")
	fmt.Println("1) Niri")
	fmt.Println("2) Hyprland")
	fmt.Println("3) None")

	var response string
	fmt.Print("\nChoice (1-3): ")
	fmt.Scanln(&response)
	response = strings.TrimSpace(response)

	switch response {
	case "1":
		return deps.WindowManagerNiri, true
	case "2":
		return deps.WindowManagerHyprland, true
	default:
		return deps.WindowManagerNiri, false
	}
}

func promptTerminal() (deps.Terminal, bool) {
	fmt.Println("\nSelect terminal:")
	fmt.Println("1) Ghostty")
	fmt.Println("2) Kitty")
	fmt.Println("3) Alacritty")
	fmt.Println("4) None")

	var response string
	fmt.Print("\nChoice (1-4): ")
	fmt.Scanln(&response)
	response = strings.TrimSpace(response)

	switch response {
	case "1":
		return deps.TerminalGhostty, true
	case "2":
		return deps.TerminalKitty, true
	case "3":
		return deps.TerminalAlacritty, true
	default:
		return deps.TerminalGhostty, false
	}
}

func promptSystemd() bool {
	fmt.Println("\nUse systemd for session management?")
	fmt.Println("1) Yes (recommended for most distros)")
	fmt.Println("2) No (standalone, no systemd integration)")

	var response string
	fmt.Print("\nChoice (1-2): ")
	fmt.Scanln(&response)
	response = strings.TrimSpace(response)

	return response != "2"
}

func checkExistingConfigs(wm deps.WindowManager, wmSelected bool, terminal deps.Terminal, terminalSelected bool) bool {
	homeDir := os.Getenv("HOME")
	willBackup := false

	if wmSelected {
		var configPath string
		switch wm {
		case deps.WindowManagerNiri:
			configPath = filepath.Join(homeDir, ".config", "niri", "config.kdl")
		case deps.WindowManagerHyprland:
			configPath = filepath.Join(homeDir, ".config", "hypr", "hyprland.conf")
		}

		if _, err := os.Stat(configPath); err == nil {
			willBackup = true
		}
	}

	if terminalSelected {
		var configPath string
		switch terminal {
		case deps.TerminalGhostty:
			configPath = filepath.Join(homeDir, ".config", "ghostty", "config")
		case deps.TerminalKitty:
			configPath = filepath.Join(homeDir, ".config", "kitty", "kitty.conf")
		case deps.TerminalAlacritty:
			configPath = filepath.Join(homeDir, ".config", "alacritty", "alacritty.toml")
		}

		if _, err := os.Stat(configPath); err == nil {
			willBackup = true
		}
	}

	return willBackup
}
