package dialog

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

	tea "github.com/charmbracelet/bubbletea"
	"github.com/sst/opencode/internal/config"
	"github.com/sst/opencode/internal/tui/util"
)

// Command prefix constants
const (
	UserCommandPrefix    = "user:"
	ProjectCommandPrefix = "project:"
)

// LoadCustomCommands loads custom commands from both XDG_CONFIG_HOME and project data directory
func LoadCustomCommands() ([]Command, error) {
	cfg := config.Get()
	if cfg == nil {
		return nil, fmt.Errorf("config not loaded")
	}

	var commands []Command

	// Load user commands from XDG_CONFIG_HOME/opencode/commands
	xdgConfigHome := os.Getenv("XDG_CONFIG_HOME")
	if xdgConfigHome == "" {
		// Default to ~/.config if XDG_CONFIG_HOME is not set
		home, err := os.UserHomeDir()
		if err == nil {
			xdgConfigHome = filepath.Join(home, ".config")
		}
	}

	if xdgConfigHome != "" {
		userCommandsDir := filepath.Join(xdgConfigHome, "opencode", "commands")
		userCommands, err := loadCommandsFromDir(userCommandsDir, UserCommandPrefix)
		if err != nil {
			// Log error but continue - we'll still try to load other commands
			fmt.Printf("Warning: failed to load user commands from XDG_CONFIG_HOME: %v\n", err)
		} else {
			commands = append(commands, userCommands...)
		}
	}

	// Load commands from $HOME/.opencode/commands
	home, err := os.UserHomeDir()
	if err == nil {
		homeCommandsDir := filepath.Join(home, ".opencode", "commands")
		homeCommands, err := loadCommandsFromDir(homeCommandsDir, UserCommandPrefix)
		if err != nil {
			// Log error but continue - we'll still try to load other commands
			fmt.Printf("Warning: failed to load home commands: %v\n", err)
		} else {
			commands = append(commands, homeCommands...)
		}
	}

	// Load project commands from data directory
	projectCommandsDir := filepath.Join(cfg.Data.Directory, "commands")
	projectCommands, err := loadCommandsFromDir(projectCommandsDir, ProjectCommandPrefix)
	if err != nil {
		// Log error but return what we have so far
		fmt.Printf("Warning: failed to load project commands: %v\n", err)
	} else {
		commands = append(commands, projectCommands...)
	}

	return commands, nil
}

// loadCommandsFromDir loads commands from a specific directory with the given prefix
func loadCommandsFromDir(commandsDir string, prefix string) ([]Command, error) {
	// Check if the commands directory exists
	if _, err := os.Stat(commandsDir); os.IsNotExist(err) {
		// Create the commands directory if it doesn't exist
		if err := os.MkdirAll(commandsDir, 0755); err != nil {
			return nil, fmt.Errorf("failed to create commands directory %s: %w", commandsDir, err)
		}
		// Return empty list since we just created the directory
		return []Command{}, nil
	}

	var commands []Command

	// Walk through the commands directory and load all .md files
	err := filepath.Walk(commandsDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Skip directories
		if info.IsDir() {
			return nil
		}

		// Only process markdown files
		if !strings.HasSuffix(strings.ToLower(info.Name()), ".md") {
			return nil
		}

		// Read the file content
		content, err := os.ReadFile(path)
		if err != nil {
			return fmt.Errorf("failed to read command file %s: %w", path, err)
		}

		// Get the command ID from the file name without the .md extension
		commandID := strings.TrimSuffix(info.Name(), filepath.Ext(info.Name()))

		// Get relative path from commands directory
		relPath, err := filepath.Rel(commandsDir, path)
		if err != nil {
			return fmt.Errorf("failed to get relative path for %s: %w", path, err)
		}

		// Create the command ID from the relative path
		// Replace directory separators with colons
		commandIDPath := strings.ReplaceAll(filepath.Dir(relPath), string(filepath.Separator), ":")
		if commandIDPath != "." {
			commandID = commandIDPath + ":" + commandID
		}

		// Create a command
		command := Command{
			ID:          prefix + commandID,
			Title:       prefix + commandID,
			Description: fmt.Sprintf("Custom command from %s", relPath),
			Handler: func(cmd Command) tea.Cmd {
				commandContent := string(content)

				// Check if the command contains $ARGUMENTS placeholder
				if strings.Contains(commandContent, "$ARGUMENTS") {
					// Show arguments dialog
					return util.CmdHandler(ShowArgumentsDialogMsg{
						CommandID: cmd.ID,
						Content:   commandContent,
					})
				}

				// No arguments needed, run command directly
				return util.CmdHandler(CommandRunCustomMsg{
					Content: commandContent,
				})
			},
		}

		commands = append(commands, command)
		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to load custom commands from %s: %w", commandsDir, err)
	}

	return commands, nil
}

// CommandRunCustomMsg is sent when a custom command is executed
type CommandRunCustomMsg struct {
	Content string
}
