// Package register provides the project register subcommand.
package register

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

	"github.com/schmitthub/clawker/internal/cmd/project/shared"
	"github.com/schmitthub/clawker/internal/cmdutil"
	"github.com/schmitthub/clawker/internal/config"
	"github.com/schmitthub/clawker/internal/iostreams"
	"github.com/schmitthub/clawker/internal/project"
	prompterpkg "github.com/schmitthub/clawker/internal/prompter"
	"github.com/spf13/cobra"
)

// RegisterOptions contains the options for the project register command.
type RegisterOptions struct {
	IOStreams      *iostreams.IOStreams
	Prompter       func() *prompterpkg.Prompter
	Config         func() (config.Config, error)
	ProjectManager func() (project.ProjectManager, error)

	Name string // Positional arg: project name
	Yes  bool
}

// NewCmdProjectRegister creates the project register command.
func NewCmdProjectRegister(f *cmdutil.Factory, runF func(context.Context, *RegisterOptions) error) *cobra.Command {
	opts := &RegisterOptions{
		IOStreams:      f.IOStreams,
		Prompter:       f.Prompter,
		ProjectManager: f.ProjectManager,
		Config:         f.Config,
	}

	cmd := &cobra.Command{
		Use:   "register [project-name]",
		Short: "Register an existing clawker project in the local registry",
		Long: `Registers the project in the current directory in the local project registry
without modifying the configuration file.

If no project name is provided, you will be prompted to enter one (or accept the
current directory name as the default). Use --yes to accept the directory name
without prompting.

This is useful when a .clawker.yaml was manually created, copied from another
machine, or already exists and you want to register it locally.`,
		Example: `  # Register with interactive prompt for project name
  clawker project register

  # Register with a specific project name
  clawker project register my-project

  # Register using directory name without prompting
  clawker project register --yes`,
		Args: cobra.MaximumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) > 0 {
				opts.Name = args[0]
			}
			if runF != nil {
				return runF(cmd.Context(), opts)
			}
			return projectRegisterRun(cmd.Context(), opts)
		},
	}

	cmd.Flags().BoolVarP(&opts.Yes, "yes", "y", false, "Non-interactive mode, use directory name as project name")

	return cmd
}

func projectRegisterRun(ctx context.Context, opts *RegisterOptions) error {
	ios := opts.IOStreams
	cs := ios.ColorScheme()

	// Get current working directory (where the project to register is located)
	wd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("failed to get working directory: %w", err)
	}

	cfgGateway, err := opts.Config()
	if err != nil {
		return fmt.Errorf("loading config: %w", err)
	}
	projectManager, err := opts.ProjectManager()
	if err != nil {
		return fmt.Errorf("initializing project manager: %w", err)
	}

	// Require a project config in the current directory.
	if !shared.HasLocalProjectConfig(cfgGateway, wd) {
		return fmt.Errorf("no project config found in %s — run 'clawker project init' first to create one", wd)
	}

	// Determine project name. Hierarchy (highest priority first):
	//   1. positional arg (opts.Name)
	//   2. clawker.yaml::name override
	//   3. interactive prompt (or directory name in non-interactive mode)
	// Every source is run through `cmdutil.ProjectSlugify` so the value
	// persisted to the registry is Docker/x509-safe regardless of how it
	// was sourced.
	absPath, err := filepath.Abs(wd)
	if err != nil {
		return fmt.Errorf("failed to get absolute path: %w", err)
	}
	dirName := filepath.Base(absPath)

	var raw string
	switch {
	case opts.Name != "":
		raw = opts.Name
	case cfgGateway.Project().Name != "":
		raw = cfgGateway.Project().Name
	case opts.Yes || !ios.IsInteractive():
		raw = dirName
	default:
		prompter := opts.Prompter()
		raw, err = prompter.String(prompterpkg.PromptConfig{
			Message:  "Project Name",
			Default:  dirName,
			Required: true,
		})
		if err != nil {
			return fmt.Errorf("failed to get project name: %w", err)
		}
	}
	projectName := cmdutil.ProjectSlugify(raw)
	if projectName == "" {
		return fmt.Errorf("project name resolved to empty after normalization (raw input %q); set `name` in clawker.yaml or pass a name argument", raw)
	}

	registeredProject, err := projectManager.Register(ctx, projectName, wd)
	if err != nil {
		return fmt.Errorf("could not register project: %w", err)
	}

	if registeredProject != nil {
		fmt.Fprintf(ios.Out, "%s Registered project '%s'\n", cs.SuccessIcon(), projectName)
	}

	return nil
}
