package hazmat

import (
	"encoding/json"
	"fmt"

	"github.com/spf13/cobra"
)

func newExplainCmd() *cobra.Command {
	var target string
	var project string
	var readDirs []string
	var writeDirs []string
	var integrationNames []string
	var skipHarnessAssetsSync bool
	var noBackup bool
	var github bool
	var useSandbox bool
	var allowDocker bool
	var dockerModeValue string
	var networkModeValue string
	var backendValue string
	var imageValue string
	var outputJSON bool

	cmd := &cobra.Command{
		Use:   "explain",
		Short: "Preview the session contract without launching an agent",
		Long: `Show what Hazmat would do for a session without launching the agent.

This prints the same session contract, planned host changes, and
mode explanation that a real launch would show, but stops before snapshots,
sandbox setup, permission repair, or process execution.

Examples:
  hazmat explain
  hazmat explain --json
  hazmat explain -C ~/workspace/my-project --integration node
  hazmat explain --github -C ~/workspace/my-project
  hazmat explain --for shell --docker=sandbox -C ~/workspace/docker-app
  hazmat explain --for opencode --docker=none -C ~/workspace/repo
  hazmat explain --for codex --network none -C ~/workspace/repo
  hazmat explain --for gemini --integration go -C ~/workspace/my-go-project
  hazmat explain --for hermes -C ~/workspace/repo
  hazmat explain --for qwen -C ~/workspace/repo
  hazmat explain --for cursor-agent -C ~/workspace/repo
  hazmat explain --backend=apple-container --image ghcr.io/example/hazmat-codex:latest --for codex`,
		Args: cobra.NoArgs,
		RunE: func(cmd *cobra.Command, _ []string) error {
			sessionOpts := harnessSessionOpts{
				project:               project,
				readDirs:              readDirs,
				writeDirs:             writeDirs,
				integrations:          integrationNames,
				skipHarnessAssetsSync: skipHarnessAssetsSync,
				noBackup:              noBackup,
				github:                github,
				useSandbox:            useSandbox,
				allowDocker:           allowDocker,
				dockerMode:            dockerModeValue,
				dockerModeExplicit:    cmd.Flags().Changed("docker"),
				networkMode:           networkModeValue,
				networkModeExplicit:   cmd.Flags().Changed("network"),
				planOnly:              true,
			}

			switch backendValue {
			case "":
			case explainBackendAppleContainer:
				return runExplainAppleContainer(cmd, target, imageValue, sessionOpts, outputJSON)
			default:
				return fmt.Errorf("unknown preview backend %q (want apple-container)", backendValue)
			}
			if imageValue != "" {
				return fmt.Errorf("--image requires --backend=apple-container")
			}

			cfg, mode, err := resolveExplainSession(target, sessionOpts)
			if err != nil {
				return err
			}

			if outputJSON {
				preview := buildExplainJSON(target, cfg, mode, noBackup)
				enc := json.NewEncoder(cmd.OutOrStdout())
				enc.SetIndent("", "  ")
				enc.SetEscapeHTML(false)
				return enc.Encode(preview)
			}

			printSessionContract(cfg, mode, noBackup)
			fmt.Fprint(cmd.ErrOrStderr(), renderRepoSetupDetails(cfg.RepoSetup))
			printSessionMutationDetails(cfg.PlannedHostMutations)
			fmt.Fprint(cmd.ErrOrStderr(), renderIntegrationDetails(cfg.IntegrationDetails))
			return nil
		},
	}

	cmd.Flags().StringVar(&target, "for", "claude",
		"Preview target (claude, shell, exec, opencode, codex, gemini, hermes, qwen, cursor-agent)")
	cmd.Flags().StringVarP(&project, "project", "C", "",
		"Writable project directory (defaults to current directory)")
	cmd.Flags().StringArrayVarP(&readDirs, "read", "R", nil,
		"Read-only directory to expose to the agent (repeatable)")
	cmd.Flags().StringArrayVarP(&writeDirs, "write", "W", nil,
		"Read-write directory to expose to the agent (repeatable)")
	cmd.Flags().StringArrayVar(&integrationNames, "integration", nil,
		"Activate a session integration (repeatable, e.g. --integration go)")
	cmd.Flags().BoolVar(&skipHarnessAssetsSync, "skip-harness-assets-sync", false,
		"Preview without managed harness prompt-asset sync")
	cmd.Flags().BoolVar(&noBackup, "no-backup", false,
		"Preview without a pre-session snapshot")
	cmd.Flags().BoolVar(&github, "github", false,
		"Preview a session with the configured GitHub API token granted as GH_TOKEN")
	cmd.Flags().StringVar(&dockerModeValue, "docker", string(dockerModeNone),
		"Docker routing: none (default), sandbox, or auto")
	cmd.Flags().StringVar(&networkModeValue, "network", string(sessionNetworkDefault),
		"Native network policy: default or none")
	cmd.Flags().BoolVar(&useSandbox, "sandbox", false,
		"Preview Docker Sandbox support")
	cmd.Flags().BoolVar(&allowDocker, "ignore-docker", false,
		"Preview native containment even if Docker markers are present")
	cmd.Flags().StringVar(&backendValue, "backend", "",
		"Preview an alternate plan-only backend (apple-container)")
	cmd.Flags().StringVar(&imageValue, "image", "",
		"Explicit Linux image for --backend=apple-container previews")
	cmd.Flags().BoolVar(&outputJSON, "json", false,
		"Emit a machine-readable JSON preview instead of human-oriented text")
	cmd.SetFlagErrorFunc(legacyIntegrationFlagError)
	_ = cmd.Flags().MarkDeprecated("sandbox", "use --docker=sandbox")
	_ = cmd.Flags().MarkDeprecated("ignore-docker", "use --docker=none")

	return cmd
}

func resolveExplainSession(target string, opts harnessSessionOpts) (sessionConfig, sessionMode, error) {
	switch target {
	case "claude", "shell", "exec", "opencode", "codex", "gemini", "hermes", "qwen", "cursor-agent":
	default:
		return sessionConfig{}, "", fmt.Errorf("unknown preview target %q (want claude, shell, exec, opencode, codex, gemini, hermes, qwen, or cursor-agent)", target)
	}

	prepared, err := resolvePreparedSession(target, opts, true)
	if err != nil {
		return sessionConfig{}, "", err
	}
	return prepared.Config, prepared.Mode, nil
}
