package config

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"

	"github.com/ollama/ollama/api"
	"github.com/ollama/ollama/envconfig"
)

// Claude implements Runner and AliasConfigurer for Claude Code integration
type Claude struct{}

// Compile-time check that Claude implements AliasConfigurer
var _ AliasConfigurer = (*Claude)(nil)

func (c *Claude) String() string { return "Claude Code" }

func (c *Claude) args(model string, extra []string) []string {
	var args []string
	if model != "" {
		args = append(args, "--model", model)
	}
	args = append(args, extra...)
	return args
}

func (c *Claude) findPath() (string, error) {
	if p, err := exec.LookPath("claude"); err == nil {
		return p, nil
	}
	home, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}
	name := "claude"
	if runtime.GOOS == "windows" {
		name = "claude.exe"
	}
	fallback := filepath.Join(home, ".claude", "local", name)
	if _, err := os.Stat(fallback); err != nil {
		return "", err
	}
	return fallback, nil
}

func (c *Claude) Run(model string, args []string) error {
	claudePath, err := c.findPath()
	if err != nil {
		return fmt.Errorf("claude is not installed, install from https://code.claude.com/docs/en/quickstart")
	}

	cmd := exec.Command(claudePath, c.args(model, args)...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	env := append(os.Environ(),
		"ANTHROPIC_BASE_URL="+envconfig.Host().String(),
		"ANTHROPIC_API_KEY=",
		"ANTHROPIC_AUTH_TOKEN=ollama",
	)

	env = append(env, c.modelEnvVars(model)...)

	cmd.Env = env
	return cmd.Run()
}

// modelEnvVars returns Claude Code env vars that route all model tiers through Ollama.
func (c *Claude) modelEnvVars(model string) []string {
	primary := model
	fast := model
	if cfg, err := loadIntegration("claude"); err == nil && cfg.Aliases != nil {
		if p := cfg.Aliases["primary"]; p != "" {
			primary = p
		}
		if f := cfg.Aliases["fast"]; f != "" {
			fast = f
		}
	}
	return []string{
		"ANTHROPIC_DEFAULT_OPUS_MODEL=" + primary,
		"ANTHROPIC_DEFAULT_SONNET_MODEL=" + primary,
		"ANTHROPIC_DEFAULT_HAIKU_MODEL=" + fast,
		"CLAUDE_CODE_SUBAGENT_MODEL=" + primary,
	}
}

// ConfigureAliases sets up model aliases for Claude Code.
// model: the model to use (if empty, user will be prompted to select)
// aliases: existing alias configuration to preserve/update
// Cloud-only: subagent routing (fast model) is gated to cloud models only until
// there is a better strategy for prompt caching on local models.
func (c *Claude) ConfigureAliases(ctx context.Context, model string, existingAliases map[string]string, force bool) (map[string]string, bool, error) {
	aliases := make(map[string]string)
	for k, v := range existingAliases {
		aliases[k] = v
	}

	if model != "" {
		aliases["primary"] = model
	}

	if !force && aliases["primary"] != "" {
		if isCloudModelName(aliases["primary"]) {
			aliases["fast"] = aliases["primary"]
			return aliases, false, nil
		}
		delete(aliases, "fast")
		return aliases, false, nil
	}

	items, existingModels, cloudModels, client, err := listModels(ctx)
	if err != nil {
		return nil, false, err
	}

	fmt.Fprintf(os.Stderr, "\n%sModel Configuration%s\n\n", ansiBold, ansiReset)

	if aliases["primary"] == "" || force {
		primary, err := DefaultSingleSelector("Select model:", items, aliases["primary"])
		if err != nil {
			return nil, false, err
		}
		if err := pullIfNeeded(ctx, client, existingModels, primary); err != nil {
			return nil, false, err
		}
		if err := ensureAuth(ctx, client, cloudModels, []string{primary}); err != nil {
			return nil, false, err
		}
		aliases["primary"] = primary
	}

	if isCloudModelName(aliases["primary"]) {
		aliases["fast"] = aliases["primary"]
	} else {
		delete(aliases, "fast")
	}

	return aliases, true, nil
}

// SetAliases syncs the configured aliases to the Ollama server using prefix matching.
// Cloud-only: for local models (fast is empty), we delete any existing aliases to
// prevent stale routing to a previous cloud model.
func (c *Claude) SetAliases(ctx context.Context, aliases map[string]string) error {
	client, err := api.ClientFromEnvironment()
	if err != nil {
		return err
	}

	prefixes := []string{"claude-sonnet-", "claude-haiku-"}

	if aliases["fast"] == "" {
		for _, prefix := range prefixes {
			_ = client.DeleteAliasExperimental(ctx, &api.AliasDeleteRequest{Alias: prefix})
		}
		return nil
	}

	prefixAliases := map[string]string{
		"claude-sonnet-": aliases["primary"],
		"claude-haiku-":  aliases["fast"],
	}

	var errs []string
	for prefix, target := range prefixAliases {
		req := &api.AliasRequest{
			Alias:          prefix,
			Target:         target,
			PrefixMatching: true,
		}
		if err := client.SetAliasExperimental(ctx, req); err != nil {
			errs = append(errs, prefix)
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("failed to set aliases: %v", errs)
	}
	return nil
}
