package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
	"github.com/steveyegge/gastown/internal/beads"
	"github.com/steveyegge/gastown/internal/config"
	"github.com/steveyegge/gastown/internal/constants"
	"github.com/steveyegge/gastown/internal/crew"
	"github.com/steveyegge/gastown/internal/runtime"
	"github.com/steveyegge/gastown/internal/session"
	"github.com/steveyegge/gastown/internal/style"
	"github.com/steveyegge/gastown/internal/tmux"
	"github.com/steveyegge/gastown/internal/workspace"
)

func runCrewAt(cmd *cobra.Command, args []string) error {
	var name string

	// Determine crew name: from arg, or auto-detect from cwd
	if len(args) > 0 {
		name = args[0]
		// Parse rig/name format (e.g., "beads/emma" -> rig=beads, name=emma)
		if rig, crewName, ok := parseRigSlashName(name); ok {
			if crewRig == "" {
				crewRig = rig
			}
			name = crewName
		}
	} else {
		// Try to detect from current directory
		detected, err := detectCrewFromCwd()
		if err != nil {
			// Try to show available crew members if we can detect the rig
			hint := "\n\nUsage: gt crew at <name>"
			if crewRig != "" {
				if mgr, _, mgrErr := getCrewManager(crewRig); mgrErr == nil {
					if members, listErr := mgr.List(); listErr == nil && len(members) > 0 {
						hint = fmt.Sprintf("\n\nAvailable crew in %s:", crewRig)
						for _, m := range members {
							hint += fmt.Sprintf("\n  %s", m.Name)
						}
					}
				}
			}
			return fmt.Errorf("could not detect crew workspace from current directory: %w%s", err, hint)
		}
		name = detected.crewName
		if crewRig == "" {
			crewRig = detected.rigName
		}
		fmt.Printf("Detected crew workspace: %s/%s\n", detected.rigName, name)
	}

	crewMgr, r, err := getCrewManager(crewRig)
	if err != nil {
		return err
	}

	// Get the crew worker
	worker, err := crewMgr.Get(name)
	if err != nil {
		if err == crew.ErrCrewNotFound {
			return fmt.Errorf("crew workspace '%s' not found", name)
		}
		return fmt.Errorf("getting crew worker: %w", err)
	}

	// Ensure crew workspace is on default branch (persistent roles should not use feature branches)
	ensureDefaultBranch(worker.ClonePath, fmt.Sprintf("Crew workspace %s/%s", r.Name, name), r.Path)

	// If --no-tmux, just print the path
	if crewNoTmux {
		fmt.Println(worker.ClonePath)
		return nil
	}

	// Resolve account for runtime config
	townRoot, err := workspace.FindFromCwd()
	if err != nil {
		return fmt.Errorf("finding town root: %w", err)
	}
	accountsPath := constants.MayorAccountsPath(townRoot)
	claudeConfigDir, accountHandle, err := config.ResolveAccountConfigDir(accountsPath, crewAccount)
	if err != nil {
		return fmt.Errorf("resolving account: %w", err)
	}
	if accountHandle != "" {
		fmt.Printf("Using account: %s\n", accountHandle)
	}

	runtimeConfig := config.LoadRuntimeConfig(r.Path)
	_ = runtime.EnsureSettingsForRole(worker.ClonePath, "crew", runtimeConfig)

	// Check if session exists
	t := tmux.NewTmux()
	sessionID := crewSessionName(r.Name, name)
	hasSession, err := t.HasSession(sessionID)
	if err != nil {
		return fmt.Errorf("checking session: %w", err)
	}

	// Before creating a new session, check if there's already a runtime session
	// running in this crew's directory (might have been started manually or via
	// a different mechanism)
	if !hasSession {
		existingSessions, err := t.FindSessionByWorkDir(worker.ClonePath, runtimeConfig.Tmux.ProcessNames)
		if err == nil && len(existingSessions) > 0 {
			// Found an existing session with runtime running in this directory
			existingSession := existingSessions[0]
			fmt.Printf("%s Found existing runtime session '%s' in crew directory\n",
				style.Warning.Render("⚠"),
				existingSession)
			fmt.Printf("  Attaching to existing session instead of creating a new one\n")

			// If inside tmux (but different session), inform user
			if tmux.IsInsideTmux() {
				fmt.Printf("Use C-b s to switch to '%s'\n", existingSession)
				return nil
			}

			// Outside tmux: attach unless --detached flag is set
			if crewDetached {
				fmt.Printf("Existing session: '%s'. Run 'tmux attach -t %s' to attach.\n",
					existingSession, existingSession)
				return nil
			}

			// Attach to existing session
			return attachToTmuxSession(existingSession)
		}
	}

	if !hasSession {
		// Create new session
		if err := t.NewSession(sessionID, worker.ClonePath); err != nil {
			return fmt.Errorf("creating session: %w", err)
		}

		// Set environment (non-fatal: session works without these)
		// Use centralized AgentEnv for consistency across all role startup paths
		envVars := config.AgentEnv(config.AgentEnvConfig{
			Role:             "crew",
			Rig:              r.Name,
			AgentName:        name,
			TownRoot:         townRoot,
			BeadsDir:         beads.ResolveBeadsDir(r.Path),
			RuntimeConfigDir: claudeConfigDir,
			BeadsNoDaemon:    true,
		})
		for k, v := range envVars {
			_ = t.SetEnvironment(sessionID, k, v)
		}

		// Apply rig-based theming (non-fatal: theming failure doesn't affect operation)
		// Note: ConfigureGasTownSession includes cycle bindings
		theme := getThemeForRig(r.Name)
		_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")

		// Wait for shell to be ready after session creation
		if err := t.WaitForShellReady(sessionID, constants.ShellReadyTimeout); err != nil {
			return fmt.Errorf("waiting for shell: %w", err)
		}

		// Get pane ID for respawn
		paneID, err := t.GetPaneID(sessionID)
		if err != nil {
			return fmt.Errorf("getting pane ID: %w", err)
		}

		// Build startup beacon for predecessor discovery via /resume
		// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
		// The SessionStart hook handles context injection (gt prime --hook)
		address := fmt.Sprintf("%s/crew/%s", r.Name, name)
		beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
			Recipient: address,
			Sender:    "human",
			Topic:     "start",
		})

		// Use respawn-pane to replace shell with runtime directly
		// This gives cleaner lifecycle: runtime exits → session ends (no intermediate shell)
		// Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes
		startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, beacon, crewAgentOverride)
		if err != nil {
			return fmt.Errorf("building startup command: %w", err)
		}
		// Prepend config dir env if available
		if runtimeConfig.Session != nil && runtimeConfig.Session.ConfigDirEnv != "" && claudeConfigDir != "" {
			startupCmd = config.PrependEnv(startupCmd, map[string]string{runtimeConfig.Session.ConfigDirEnv: claudeConfigDir})
		}
		if err := t.RespawnPane(paneID, startupCmd); err != nil {
			return fmt.Errorf("starting runtime: %w", err)
		}

		fmt.Printf("%s Created session for %s/%s\n",
			style.Bold.Render("✓"), r.Name, name)
	} else {
		// Session exists - check if runtime is still running
		// Uses both pane command check and UI marker detection to avoid
		// restarting when user is in a subshell spawned from the runtime
		agentCfg, _, err := config.ResolveAgentConfigWithOverride(townRoot, r.Path, crewAgentOverride)
		if err != nil {
			return fmt.Errorf("resolving agent: %w", err)
		}
		if !t.IsAgentRunning(sessionID, config.ExpectedPaneCommands(agentCfg)...) {
			// Runtime has exited, restart it using respawn-pane
			fmt.Printf("Runtime exited, restarting...\n")

			// Get pane ID for respawn
			paneID, err := t.GetPaneID(sessionID)
			if err != nil {
				return fmt.Errorf("getting pane ID: %w", err)
			}

			// Build startup beacon for predecessor discovery via /resume
			// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
			address := fmt.Sprintf("%s/crew/%s", r.Name, name)
			beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
				Recipient: address,
				Sender:    "human",
				Topic:     "restart",
			})

			// Use respawn-pane to replace shell with runtime directly
			// Export GT_ROLE and BD_ACTOR since tmux SetEnvironment only affects new panes
			startupCmd, err := config.BuildCrewStartupCommandWithAgentOverride(r.Name, name, r.Path, beacon, crewAgentOverride)
			if err != nil {
				return fmt.Errorf("building startup command: %w", err)
			}
			// Prepend config dir env if available
			if runtimeConfig.Session != nil && runtimeConfig.Session.ConfigDirEnv != "" && claudeConfigDir != "" {
				startupCmd = config.PrependEnv(startupCmd, map[string]string{runtimeConfig.Session.ConfigDirEnv: claudeConfigDir})
			}
			if err := t.RespawnPane(paneID, startupCmd); err != nil {
				return fmt.Errorf("restarting runtime: %w", err)
			}
		}
	}

	// Check if we're already in the target session
	if isInTmuxSession(sessionID) {
		// We're in the session at a shell prompt - just start the agent directly
		// Build startup beacon for predecessor discovery via /resume
		address := fmt.Sprintf("%s/crew/%s", r.Name, name)
		beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
			Recipient: address,
			Sender:    "human",
			Topic:     "start",
		})
		agentCfg, _, err := config.ResolveAgentConfigWithOverride(townRoot, r.Path, crewAgentOverride)
		if err != nil {
			return fmt.Errorf("resolving agent: %w", err)
		}
		fmt.Printf("Starting %s in current session...\n", agentCfg.Command)
		return execAgent(agentCfg, beacon)
	}

	// If inside tmux (but different session), don't switch - just inform user
	if tmux.IsInsideTmux() {
		fmt.Printf("Started %s/%s. Use C-b s to switch.\n", r.Name, name)
		return nil
	}

	// Outside tmux: attach unless --detached flag is set
	if crewDetached {
		fmt.Printf("Started %s/%s. Run 'gt crew at %s' to attach.\n", r.Name, name, name)
		return nil
	}

	// Attach to session
	return attachToTmuxSession(sessionID)
}
