package dev

import (
	"context"
	"os"
	"os/exec"
	"sort"
	"strings"

	"github.com/host-uk/core/pkg/agentic"
	"github.com/host-uk/core/pkg/cli"
	"github.com/host-uk/core/pkg/git"
	"github.com/host-uk/core/pkg/i18n"
)

// Work command flags
var (
	workStatusOnly   bool
	workAutoCommit   bool
	workRegistryPath string
)

// AddWorkCommand adds the 'work' command to the given parent command.
func AddWorkCommand(parent *cli.Command) {
	workCmd := &cli.Command{
		Use:   "work",
		Short: i18n.T("cmd.dev.work.short"),
		Long:  i18n.T("cmd.dev.work.long"),
		RunE: func(cmd *cli.Command, args []string) error {
			return runWork(workRegistryPath, workStatusOnly, workAutoCommit)
		},
	}

	workCmd.Flags().BoolVar(&workStatusOnly, "status", false, i18n.T("cmd.dev.work.flag.status"))
	workCmd.Flags().BoolVar(&workAutoCommit, "commit", false, i18n.T("cmd.dev.work.flag.commit"))
	workCmd.Flags().StringVar(&workRegistryPath, "registry", "", i18n.T("common.flag.registry"))

	parent.AddCommand(workCmd)
}

func runWork(registryPath string, statusOnly, autoCommit bool) error {
	ctx := context.Background()

	// Build worker bundle with required services
	bundle, err := NewWorkBundle(WorkBundleOptions{
		RegistryPath: registryPath,
	})
	if err != nil {
		return err
	}

	// Start services (registers handlers)
	if err := bundle.Start(ctx); err != nil {
		return err
	}
	defer bundle.Stop(ctx)

	// Load registry and get paths
	paths, names, err := func() ([]string, map[string]string, error) {
		reg, _, err := loadRegistryWithConfig(registryPath)
		if err != nil {
			return nil, nil, err
		}
		var paths []string
		names := make(map[string]string)
		for _, repo := range reg.List() {
			if repo.IsGitRepo() {
				paths = append(paths, repo.Path)
				names[repo.Path] = repo.Name
			}
		}
		return paths, names, nil
	}()
	if err != nil {
		return err
	}

	if len(paths) == 0 {
		cli.Text(i18n.T("cmd.dev.no_git_repos"))
		return nil
	}

	// QUERY git status
	result, handled, err := bundle.Core.QUERY(git.QueryStatus{
		Paths: paths,
		Names: names,
	})
	if !handled {
		return cli.Err("git service not available")
	}
	if err != nil {
		return err
	}
	statuses := result.([]git.RepoStatus)

	// Sort by repo name for consistent output
	sort.Slice(statuses, func(i, j int) bool {
		return statuses[i].Name < statuses[j].Name
	})

	// Display status table
	printStatusTable(statuses)

	// Collect dirty and ahead repos
	var dirtyRepos []git.RepoStatus
	var aheadRepos []git.RepoStatus

	for _, s := range statuses {
		if s.Error != nil {
			continue
		}
		if s.IsDirty() {
			dirtyRepos = append(dirtyRepos, s)
		}
		if s.HasUnpushed() {
			aheadRepos = append(aheadRepos, s)
		}
	}

	// Auto-commit dirty repos if requested
	if autoCommit && len(dirtyRepos) > 0 {
		cli.Blank()
		cli.Print("%s\n", cli.TitleStyle.Render(i18n.T("cmd.dev.commit.committing")))
		cli.Blank()

		for _, s := range dirtyRepos {
			// PERFORM commit via agentic service
			_, handled, err := bundle.Core.PERFORM(agentic.TaskCommit{
				Path: s.Path,
				Name: s.Name,
			})
			if !handled {
				cli.Print("  %s %s: %s\n", warningStyle.Render("!"), s.Name, "agentic service not available")
				continue
			}
			if err != nil {
				cli.Print("  %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
			} else {
				cli.Print("  %s %s\n", successStyle.Render("v"), s.Name)
			}
		}

		// Re-QUERY status after commits
		result, _, _ = bundle.Core.QUERY(git.QueryStatus{
			Paths: paths,
			Names: names,
		})
		statuses = result.([]git.RepoStatus)

		// Rebuild ahead repos list
		aheadRepos = nil
		for _, s := range statuses {
			if s.Error == nil && s.HasUnpushed() {
				aheadRepos = append(aheadRepos, s)
			}
		}
	}

	// If status only, we're done
	if statusOnly {
		if len(dirtyRepos) > 0 && !autoCommit {
			cli.Blank()
			cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.dev.work.use_commit_flag")))
		}
		return nil
	}

	// Push repos with unpushed commits
	if len(aheadRepos) == 0 {
		cli.Blank()
		cli.Text(i18n.T("cmd.dev.work.all_up_to_date"))
		return nil
	}

	cli.Blank()
	cli.Print("%s\n", i18n.T("common.count.repos_unpushed", map[string]interface{}{"Count": len(aheadRepos)}))
	for _, s := range aheadRepos {
		cli.Print("  %s: %s\n", s.Name, i18n.T("common.count.commits", map[string]interface{}{"Count": s.Ahead}))
	}

	cli.Blank()
	if !cli.Confirm(i18n.T("cmd.dev.push.confirm")) {
		cli.Text(i18n.T("cli.aborted"))
		return nil
	}

	cli.Blank()

	// PERFORM push for each repo
	var divergedRepos []git.RepoStatus

	for _, s := range aheadRepos {
		_, handled, err := bundle.Core.PERFORM(git.TaskPush{
			Path: s.Path,
			Name: s.Name,
		})
		if !handled {
			cli.Print("  %s %s: %s\n", errorStyle.Render("x"), s.Name, "git service not available")
			continue
		}
		if err != nil {
			if git.IsNonFastForward(err) {
				cli.Print("  %s %s: %s\n", warningStyle.Render("!"), s.Name, i18n.T("cmd.dev.push.diverged"))
				divergedRepos = append(divergedRepos, s)
			} else {
				cli.Print("  %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
			}
		} else {
			cli.Print("  %s %s\n", successStyle.Render("v"), s.Name)
		}
	}

	// Handle diverged repos - offer to pull and retry
	if len(divergedRepos) > 0 {
		cli.Blank()
		cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help"))
		if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) {
			cli.Blank()
			for _, s := range divergedRepos {
				cli.Print("  %s %s...\n", dimStyle.Render("↓"), s.Name)

				// PERFORM pull
				_, _, err := bundle.Core.PERFORM(git.TaskPull{Path: s.Path, Name: s.Name})
				if err != nil {
					cli.Print("  %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
					continue
				}

				cli.Print("  %s %s...\n", dimStyle.Render("↑"), s.Name)

				// PERFORM push
				_, _, err = bundle.Core.PERFORM(git.TaskPush{Path: s.Path, Name: s.Name})
				if err != nil {
					cli.Print("  %s %s: %s\n", errorStyle.Render("x"), s.Name, err)
					continue
				}

				cli.Print("  %s %s\n", successStyle.Render("v"), s.Name)
			}
		}
	}

	return nil
}

func printStatusTable(statuses []git.RepoStatus) {
	// Calculate column widths
	nameWidth := 4 // "Repo"
	for _, s := range statuses {
		if len(s.Name) > nameWidth {
			nameWidth = len(s.Name)
		}
	}

	// Print header with fixed-width formatting
	cli.Print("%-*s  %8s  %9s  %6s  %5s\n",
		nameWidth,
		cli.TitleStyle.Render(i18n.Label("repo")),
		cli.TitleStyle.Render(i18n.T("cmd.dev.work.table_modified")),
		cli.TitleStyle.Render(i18n.T("cmd.dev.work.table_untracked")),
		cli.TitleStyle.Render(i18n.T("cmd.dev.work.table_staged")),
		cli.TitleStyle.Render(i18n.T("cmd.dev.work.table_ahead")),
	)

	// Print separator
	cli.Text(strings.Repeat("-", nameWidth+2+10+11+8+7))

	// Print rows
	for _, s := range statuses {
		if s.Error != nil {
			paddedName := cli.Sprintf("%-*s", nameWidth, s.Name)
			cli.Print("%s  %s\n",
				repoNameStyle.Render(paddedName),
				errorStyle.Render(i18n.T("cmd.dev.work.error_prefix")+" "+s.Error.Error()),
			)
			continue
		}

		// Style numbers based on values
		modStr := cli.Sprintf("%d", s.Modified)
		if s.Modified > 0 {
			modStr = dirtyStyle.Render(modStr)
		} else {
			modStr = cleanStyle.Render(modStr)
		}

		untrackedStr := cli.Sprintf("%d", s.Untracked)
		if s.Untracked > 0 {
			untrackedStr = dirtyStyle.Render(untrackedStr)
		} else {
			untrackedStr = cleanStyle.Render(untrackedStr)
		}

		stagedStr := cli.Sprintf("%d", s.Staged)
		if s.Staged > 0 {
			stagedStr = aheadStyle.Render(stagedStr)
		} else {
			stagedStr = cleanStyle.Render(stagedStr)
		}

		aheadStr := cli.Sprintf("%d", s.Ahead)
		if s.Ahead > 0 {
			aheadStr = aheadStyle.Render(aheadStr)
		} else {
			aheadStr = cleanStyle.Render(aheadStr)
		}

		// Pad name before styling to avoid ANSI code length issues
		paddedName := cli.Sprintf("%-*s", nameWidth, s.Name)
		cli.Print("%s  %8s  %9s  %6s  %5s\n",
			repoNameStyle.Render(paddedName),
			modStr,
			untrackedStr,
			stagedStr,
			aheadStr,
		)
	}
}

// claudeCommit shells out to claude for committing (legacy helper for other commands)
func claudeCommit(ctx context.Context, repoPath, repoName, registryPath string) error {
	prompt := agentic.Prompt("commit")

	cmd := exec.CommandContext(ctx, "claude", "-p", prompt, "--allowedTools", "Bash,Read,Glob,Grep")
	cmd.Dir = repoPath
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	return cmd.Run()
}

// claudeEditCommit shells out to claude with edit permissions (legacy helper)
func claudeEditCommit(ctx context.Context, repoPath, repoName, registryPath string) error {
	prompt := agentic.Prompt("commit")

	cmd := exec.CommandContext(ctx, "claude", "-p", prompt, "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep")
	cmd.Dir = repoPath
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	return cmd.Run()
}
