package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/pelletier/go-toml/v2"
	"github.com/spf13/cobra"
	"go.yaml.in/yaml/v3"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agentview"
)

// ---------------------------------------------------------------------------
// Flags
// ---------------------------------------------------------------------------

var agentOpts struct {
	format string
}

// ---------------------------------------------------------------------------
// Serialization struct (for JSON/YAML/TOML output)
// ---------------------------------------------------------------------------

type agentInfo struct {
	ID              string `json:"id"               yaml:"id"               toml:"ID"`
	Name            string `json:"name,omitempty"    yaml:"name,omitempty"   toml:"Name,omitempty"`
	Status          string `json:"status"            yaml:"status"           toml:"Status"`
	Runtime         string `json:"runtime"           yaml:"runtime"          toml:"Runtime"`
	Model           string `json:"model,omitempty"   yaml:"model,omitempty"  toml:"Model,omitempty"`
	Description     string `json:"description,omitempty" yaml:"description,omitempty" toml:"Description,omitempty"`
	ReasoningEffort string `json:"reasoning_effort,omitempty" yaml:"reasoning_effort,omitempty" toml:"ReasoningEffort,omitempty"`
	Source          string `json:"source,omitempty"  yaml:"source,omitempty" toml:"Source,omitempty"`
}

func agentSnapshotToInfo(snapshot agentview.Snapshot) agentInfo {
	return agentInfo{
		ID:              snapshot.ID,
		Name:            snapshot.Name,
		Status:          snapshot.Status,
		Runtime:         snapshot.Runtime,
		Model:           snapshot.Model,
		Description:     snapshot.Description,
		ReasoningEffort: snapshot.ReasoningEffort,
		Source:          snapshot.Source,
	}
}

// ---------------------------------------------------------------------------
// Commands
// ---------------------------------------------------------------------------

var agentCmd = &cobra.Command{
	Use:   "agent",
	Short: "Inspect synced AI agents",
	Long: `Inspect AI agent definitions synced from remote AgentTeam servers.

Agents are synced from remote AgentTeam servers. Local CLI management is read-only;
use a AgentTeam server to create, update, or remove agents.

Examples:
  agentteam agent list
  agentteam agent get my-agent`,
	Args: func(_ *cobra.Command, args []string) error {
		if len(args) > 0 {
			return fmt.Errorf("unknown agent command %q (supported: list, get)", args[0])
		}
		return nil
	},
	RunE: func(cmd *cobra.Command, _ []string) error {
		return cmd.Help()
	},
}

var agentListCmd = &cobra.Command{
	Use:   "list",
	Short: "List all agents",
	Long: `List all synced agents.

Use --format/-f to control the output format.

Examples:
  agentteam agent list
  agentteam agent list -f json
  agentteam agent list --format yaml`,
	RunE: runAgentList,
}

var agentGetCmd = &cobra.Command{
	Use:   "get <id>",
	Short: "Show agent details",
	Long: `Show detailed information about a specific agent.

Examples:
  agentteam agent get my-agent
  agentteam agent get my-agent -f json`,
	Args: cobra.ExactArgs(1),
	RunE: runAgentGet,
}

func init() {
	agentListCmd.Flags().StringVarP(&agentOpts.format, "format", "f", "table", "output format (table|json|jsonc|yaml|toml)")
	agentGetCmd.Flags().StringVarP(&agentOpts.format, "format", "f", "table", "output format (table|json|jsonc|yaml|toml)")

	agentCmd.AddCommand(agentListCmd)
	agentCmd.AddCommand(agentGetCmd)
}

// ---------------------------------------------------------------------------
// RunE implementations
// ---------------------------------------------------------------------------

func runAgentList(_ *cobra.Command, _ []string) error {
	op, err := GetOperator()
	if err != nil {
		return err
	}
	agents, err := op.Agents().All()
	if err != nil {
		return err
	}

	switch strings.ToLower(agentOpts.format) {
	case "table":
		return printAgentListTable(agents)
	case "json":
		return printAgentListJSON(agents, false)
	case "jsonc":
		return printAgentListJSON(agents, true)
	case "yaml":
		return printAgentListYAML(agents)
	case "toml":
		return printAgentListTOML(agents)
	default:
		return fmt.Errorf("unsupported format: %s (valid: table, json, jsonc, yaml, toml)", agentOpts.format)
	}
}

func runAgentGet(_ *cobra.Command, args []string) error {
	op, err := GetOperator()
	if err != nil {
		return err
	}
	ag, ok, err := op.Agents().Get(args[0])
	if err != nil {
		return err
	}
	if !ok {
		return fmt.Errorf("agent %q not found", args[0])
	}

	info := agentSnapshotToInfo(ag)

	switch strings.ToLower(agentOpts.format) {
	case "table":
		return printAgentDetailTable(info)
	case "json":
		return printJSON(info, false)
	case "jsonc":
		return printJSON(info, true)
	case "yaml":
		return printYAML(info)
	case "toml":
		return printTOML(info)
	default:
		return fmt.Errorf("unsupported format: %s (valid: table, json, jsonc, yaml, toml)", agentOpts.format)
	}
}

// ---------------------------------------------------------------------------
// Output helpers
// ---------------------------------------------------------------------------

func printAgentListTable(agents []agentview.Snapshot) error {
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintln(w, "ID\tNAME\tSTATUS\tRUNTIME\tMODEL\tSOURCE")
	for _, ag := range agents {
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", ag.ID, ag.Name, ag.Status, ag.Runtime, ag.Model, ag.Source)
	}
	return w.Flush()
}

func printAgentDetailTable(info agentInfo) error {
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintf(w, "ID\t%s\n", info.ID)
	if info.Name != "" {
		fmt.Fprintf(w, "Name\t%s\n", info.Name)
	}
	fmt.Fprintf(w, "Status\t%s\n", info.Status)
	fmt.Fprintf(w, "Runtime\t%s\n", info.Runtime)
	if info.Model != "" {
		fmt.Fprintf(w, "Model\t%s\n", info.Model)
	}
	if info.Description != "" {
		fmt.Fprintf(w, "Description\t%s\n", info.Description)
	}
	if info.ReasoningEffort != "" {
		fmt.Fprintf(w, "ReasoningEffort\t%s\n", info.ReasoningEffort)
	}
	if info.Source != "" {
		fmt.Fprintf(w, "Source\t%s\n", info.Source)
	}
	return w.Flush()
}

func printAgentListJSON(agents []agentview.Snapshot, pretty bool) error {
	infos := make([]agentInfo, len(agents))
	for i, ag := range agents {
		infos[i] = agentSnapshotToInfo(ag)
	}
	return printJSON(infos, pretty)
}

func printAgentListYAML(agents []agentview.Snapshot) error {
	infos := make([]agentInfo, len(agents))
	for i, ag := range agents {
		infos[i] = agentSnapshotToInfo(ag)
	}
	return printYAML(infos)
}

func printAgentListTOML(agents []agentview.Snapshot) error {
	infos := make([]agentInfo, len(agents))
	for i, ag := range agents {
		infos[i] = agentSnapshotToInfo(ag)
	}
	return printTOML(infos)
}

// ---------------------------------------------------------------------------
// Shared output helpers
// ---------------------------------------------------------------------------

func printJSON(v interface{}, pretty bool) error {
	var data []byte
	var err error
	if pretty {
		data, err = json.MarshalIndent(v, "", "  ")
	} else {
		data, err = json.Marshal(v)
	}
	if err != nil {
		return err
	}
	fmt.Println(string(data))
	return nil
}

func printYAML(v interface{}) error {
	data, err := yaml.Marshal(v)
	if err != nil {
		return err
	}
	fmt.Print(string(data))
	return nil
}

func printTOML(v interface{}) error {
	data, err := toml.Marshal(v)
	if err != nil {
		return err
	}
	fmt.Print(string(data))
	return nil
}
