package cli

import (
	"fmt"
	"strings"

	"github.com/alecthomas/kong"
	cliContext "github.com/mudler/LocalAI/core/cli/context"
)

type CompletionCMD struct {
	Shell string `arg:"" enum:"bash,zsh,fish" help:"Shell to generate completions for (bash, zsh, fish)"`

	app *kong.Application `kong:"-"`
}

func (c *CompletionCMD) SetApplication(app *kong.Application) {
	c.app = app
}

func (c *CompletionCMD) Run(_ *cliContext.Context) error {
	if c.app == nil {
		return fmt.Errorf("application model not available")
	}

	var script string
	switch c.Shell {
	case "bash":
		script = generateBashCompletion(c.app)
	case "zsh":
		script = generateZshCompletion(c.app)
	case "fish":
		script = generateFishCompletion(c.app)
	default:
		return fmt.Errorf("unsupported shell: %s", c.Shell)
	}

	fmt.Print(script)
	return nil
}

func collectCommands(node *kong.Node, prefix string) []commandInfo {
	var cmds []commandInfo
	for _, child := range node.Children {
		if child.Hidden {
			continue
		}
		name := child.Name
		fullName := name
		if prefix != "" {
			fullName = prefix + " " + name
		}
		help := child.Help
		cmds = append(cmds, commandInfo{
			name:     name,
			fullName: fullName,
			help:     help,
			node:     child,
		})
		cmds = append(cmds, collectCommands(child, fullName)...)
	}
	return cmds
}

type commandInfo struct {
	name     string
	fullName string
	help     string
	node     *kong.Node
}

func collectFlags(node *kong.Node) []flagInfo {
	var flags []flagInfo
	seen := make(map[string]bool)

	// Collect flags from this node and its ancestors
	for n := node; n != nil; n = n.Parent {
		for _, flag := range n.Flags {
			if flag.Hidden || seen[flag.Name] {
				continue
			}
			seen[flag.Name] = true
			flags = append(flags, flagInfo{
				name:  flag.Name,
				short: flag.Short,
				help:  flag.Help,
			})
		}
	}
	return flags
}

type flagInfo struct {
	name  string
	short rune
	help  string
}

func generateBashCompletion(app *kong.Application) string {
	var sb strings.Builder

	cmds := collectCommands(app.Node, "")
	topLevelCmds := []string{}
	for _, cmd := range cmds {
		if !strings.Contains(cmd.fullName, " ") {
			topLevelCmds = append(topLevelCmds, cmd.name)
		}
	}

	globalFlags := collectFlags(app.Node)
	globalFlagNames := []string{}
	for _, f := range globalFlags {
		globalFlagNames = append(globalFlagNames, "--"+f.name)
		if f.short != 0 {
			globalFlagNames = append(globalFlagNames, "-"+string(f.short))
		}
	}

	sb.WriteString(`# bash completion for local-ai
# Generated by local-ai completion bash

_local_ai_completions()
{
    local cur prev words cword
    _init_completion || return

    local commands="` + strings.Join(topLevelCmds, " ") + `"
    local global_flags="` + strings.Join(globalFlagNames, " ") + `"

    # Find the subcommand
    local subcmd=""
    local subcmd_idx=0
    for ((i=1; i < cword; i++)); do
        case "${words[i]}" in
            -*)
                # Skip flags and their values
                ;;
            *)
                if [[ -z "$subcmd" ]]; then
                    subcmd="${words[i]}"
                    subcmd_idx=$i
                fi
                ;;
        esac
    done

    # If completing a flag value, don't suggest anything special
    if [[ "$cur" == -* ]]; then
        case "$subcmd" in
`)

	// Generate flag completions per top-level command
	for _, cmd := range cmds {
		if strings.Contains(cmd.fullName, " ") {
			continue
		}
		flags := collectFlags(cmd.node)
		flagNames := []string{}
		for _, f := range flags {
			flagNames = append(flagNames, "--"+f.name)
			if f.short != 0 {
				flagNames = append(flagNames, "-"+string(f.short))
			}
		}
		sb.WriteString(fmt.Sprintf("            %s)\n", cmd.name))
		sb.WriteString(fmt.Sprintf("                COMPREPLY=($(compgen -W \"%s\" -- \"$cur\"))\n", strings.Join(flagNames, " ")))
		sb.WriteString("                return\n")
		sb.WriteString("                ;;\n")
	}

	sb.WriteString(`            *)
                COMPREPLY=($(compgen -W "$global_flags" -- "$cur"))
                return
                ;;
        esac
    fi

    # Complete subcommands for top-level commands
    case "$subcmd" in
`)

	// Generate subcommand completions
	for _, cmd := range cmds {
		if strings.Contains(cmd.fullName, " ") {
			continue
		}
		subcmds := []string{}
		for _, sub := range cmds {
			parts := strings.SplitN(sub.fullName, " ", 2)
			if len(parts) == 2 && parts[0] == cmd.name && !strings.Contains(parts[1], " ") {
				subcmds = append(subcmds, parts[1])
			}
		}
		if len(subcmds) > 0 {
			sb.WriteString(fmt.Sprintf("        %s)\n", cmd.name))
			sb.WriteString(fmt.Sprintf("            COMPREPLY=($(compgen -W \"%s\" -- \"$cur\"))\n", strings.Join(subcmds, " ")))
			sb.WriteString("            return\n")
			sb.WriteString("            ;;\n")
		}
	}

	sb.WriteString(`        "")
            COMPREPLY=($(compgen -W "$commands" -- "$cur"))
            return
            ;;
    esac
}

complete -F _local_ai_completions local-ai
`)

	return sb.String()
}

func generateZshCompletion(app *kong.Application) string {
	var sb strings.Builder

	cmds := collectCommands(app.Node, "")
	globalFlags := collectFlags(app.Node)

	sb.WriteString(`#compdef local-ai
# Generated by local-ai completion zsh

_local_ai() {
    local -a commands
    local -a global_flags

    global_flags=(
`)

	for _, f := range globalFlags {
		help := strings.ReplaceAll(f.help, "'", "'\\''")
		help = strings.ReplaceAll(help, "[", "\\[")
		help = strings.ReplaceAll(help, "]", "\\]")
		sb.WriteString(fmt.Sprintf("        '--%s[%s]'\n", f.name, help))
		if f.short != 0 {
			sb.WriteString(fmt.Sprintf("        '-%s[%s]'\n", string(f.short), help))
		}
	}

	sb.WriteString(`    )

    commands=(
`)

	for _, cmd := range cmds {
		if strings.Contains(cmd.fullName, " ") {
			continue
		}
		help := strings.ReplaceAll(cmd.help, "'", "'\\''")
		help = strings.ReplaceAll(help, "[", "\\[")
		help = strings.ReplaceAll(help, "]", "\\]")
		sb.WriteString(fmt.Sprintf("        '%s:%s'\n", cmd.name, help))
	}

	sb.WriteString(`    )

    _arguments -C \
        $global_flags \
        '1:command:->command' \
        '*::arg:->args'

    case $state in
        command)
            _describe -t commands 'local-ai commands' commands
            ;;
        args)
            case $words[1] in
`)

	// Per-command completions
	for _, cmd := range cmds {
		if strings.Contains(cmd.fullName, " ") {
			continue
		}

		sb.WriteString(fmt.Sprintf("                %s)\n", cmd.name))

		// Check for subcommands
		subcmds := []commandInfo{}
		for _, sub := range cmds {
			parts := strings.SplitN(sub.fullName, " ", 2)
			if len(parts) == 2 && parts[0] == cmd.name && !strings.Contains(parts[1], " ") {
				subcmds = append(subcmds, sub)
			}
		}

		if len(subcmds) > 0 {
			sb.WriteString("                    local -a subcmds\n")
			sb.WriteString("                    subcmds=(\n")
			for _, sub := range subcmds {
				parts := strings.SplitN(sub.fullName, " ", 2)
				help := strings.ReplaceAll(sub.help, "'", "'\\''")
				help = strings.ReplaceAll(help, "[", "\\[")
				help = strings.ReplaceAll(help, "]", "\\]")
				sb.WriteString(fmt.Sprintf("                        '%s:%s'\n", parts[1], help))
			}
			sb.WriteString("                    )\n")
			sb.WriteString("                    _describe -t commands 'subcommands' subcmds\n")
		}

		flags := collectFlags(cmd.node)
		if len(flags) > 0 {
			sb.WriteString("                    _arguments \\\n")
			for i, f := range flags {
				help := strings.ReplaceAll(f.help, "'", "'\\''")
				help = strings.ReplaceAll(help, "[", "\\[")
				help = strings.ReplaceAll(help, "]", "\\]")
				suffix := " \\"
				if i == len(flags)-1 {
					suffix = ""
				}
				sb.WriteString(fmt.Sprintf("                        '--%s[%s]'%s\n", f.name, help, suffix))
			}
		}

		sb.WriteString("                    ;;\n")
	}

	sb.WriteString(`            esac
            ;;
    esac
}

_local_ai "$@"
`)

	return sb.String()
}

func generateFishCompletion(app *kong.Application) string {
	var sb strings.Builder

	cmds := collectCommands(app.Node, "")
	globalFlags := collectFlags(app.Node)

	sb.WriteString("# fish completion for local-ai\n")
	sb.WriteString("# Generated by local-ai completion fish\n\n")

	// Disable file completions by default
	sb.WriteString("complete -c local-ai -f\n\n")

	// Global flags
	for _, f := range globalFlags {
		help := strings.ReplaceAll(f.help, "'", "\\'")
		args := fmt.Sprintf("complete -c local-ai -l %s", f.name)
		if f.short != 0 {
			args += fmt.Sprintf(" -s %s", string(f.short))
		}
		args += fmt.Sprintf(" -d '%s'", help)
		sb.WriteString(args + "\n")
	}
	sb.WriteString("\n")

	// Top-level commands (no condition means they show when no subcommand is given)
	topLevelCmds := []string{}
	for _, cmd := range cmds {
		if strings.Contains(cmd.fullName, " ") {
			continue
		}
		topLevelCmds = append(topLevelCmds, cmd.name)
		help := strings.ReplaceAll(cmd.help, "'", "\\'")
		sb.WriteString(fmt.Sprintf("complete -c local-ai -n '__fish_use_subcommand' -a %s -d '%s'\n", cmd.name, help))
	}
	sb.WriteString("\n")

	// Subcommands and per-command flags
	for _, cmd := range cmds {
		if strings.Contains(cmd.fullName, " ") {
			continue
		}

		// Subcommands
		for _, sub := range cmds {
			parts := strings.SplitN(sub.fullName, " ", 2)
			if len(parts) == 2 && parts[0] == cmd.name && !strings.Contains(parts[1], " ") {
				help := strings.ReplaceAll(sub.help, "'", "\\'")
				sb.WriteString(fmt.Sprintf("complete -c local-ai -n '__fish_seen_subcommand_from %s' -a %s -d '%s'\n", cmd.name, parts[1], help))
			}
		}

		// Per-command flags
		flags := collectFlags(cmd.node)
		for _, f := range flags {
			help := strings.ReplaceAll(f.help, "'", "\\'")
			args := fmt.Sprintf("complete -c local-ai -n '__fish_seen_subcommand_from %s' -l %s", cmd.name, f.name)
			if f.short != 0 {
				args += fmt.Sprintf(" -s %s", string(f.short))
			}
			args += fmt.Sprintf(" -d '%s'", help)
			sb.WriteString(args + "\n")
		}
	}

	return sb.String()
}
