package cmd

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"github.com/tidbcloud/investibook/cli/internal/api"
	"github.com/tidbcloud/investibook/cli/internal/config"
	"github.com/tidbcloud/investibook/cli/internal/updater"
)

// version holds the build-time version string set via main.SetVersion.
var version = "dev"

// buildTime holds the Unix timestamp string injected at compile time.
var buildTime = "0"

// SetVersion is called from main() to inject the build-time version and build timestamp.
func SetVersion(v, bt string) {
	version = v
	buildTime = bt
}

var workspaceFlag string
var profileFlag string

var rootCmd = &cobra.Command{
	Use:   "investibook",
	Short: "Investibook CLI - AI-powered investigation platform",
	Long: `investibook-cli brings the full investigation capability of the
Investibook platform to the terminal. Run investigations, browse results,
and interact with the AI agent without switching to a browser.`,
	// Check for updates when run with no subcommand.
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
	// Check for updates before every subcommand (except "version" itself).
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		if cmd.Name() != "version" {
			updater.CheckAndUpdate(buildTime)
		}
	},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func init() {
	rootCmd.PersistentFlags().StringVarP(&workspaceFlag, "workspace", "w", "", "workspace ID (overrides default)")
	rootCmd.PersistentFlags().StringVarP(&profileFlag, "profile", "p", "", "profile name (overrides active profile)")

	rootCmd.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Print the current version and build time",
		Run: func(cmd *cobra.Command, args []string) {
			buildTimeStr := "dev build"
			if ts, err := strconv.ParseInt(buildTime, 10, 64); err == nil && ts > 0 {
				buildTimeStr = time.Unix(ts, 0).UTC().Format("2006-01-02 15:04:05 UTC")
			}
			fmt.Printf("investibook-cli %s (built: %s)\n", version, buildTimeStr)
		},
	})
}

func mustLoadConfig() *config.Config {
	cfg, err := config.Load()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err)
		os.Exit(1)
	}
	// Apply --profile flag override if provided.
	if profileFlag != "" {
		cfg.SetActiveProfile(profileFlag)
	}
	return cfg
}

func mustClient() (*api.Client, *config.Config) {
	cfg := mustLoadConfig()
	profileName := cfg.ActiveProfileName()
	profile := cfg.ActiveProfileData()
	if profile.API == "" || (profile.Token == "" && profile.APIKey == "") {
		fmt.Fprintln(os.Stderr, "Not authenticated. Run: investibook init --api-host <url> --api-key <key>")
		os.Exit(1)
	}
	client := api.New(profile.API, profile.Token)
	if profile.Token != "" {
		if _, err := client.Me(); err == nil {
			return client, cfg
		} else if !isUnauthorized(err) || profile.APIKey == "" {
			fmt.Fprintf(os.Stderr, "Authentication failed: %v\n", err)
			if isUnauthorized(err) {
				fmt.Fprintln(os.Stderr, "Run: investibook init --api-host <url> --api-key <key>")
			}
			os.Exit(1)
		}
	}

	refreshed, err := api.New(profile.API, "").LoginWithAPIKey(profile.APIKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to refresh session token: %v\n", err)
		fmt.Fprintln(os.Stderr, "Run: investibook init --api-host <url> --api-key <key>")
		os.Exit(1)
	}
	profile.Token = refreshed.Token
	profile.Email = refreshed.Email
	cfg.UpsertProfile(profileName, profile)
	if err := config.Save(cfg); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to save refreshed session token: %v\n", err)
		os.Exit(1)
	}
	return api.New(profile.API, profile.Token), cfg
}

func isUnauthorized(err error) bool {
	return err != nil && strings.Contains(err.Error(), " returned 401:")
}

func mustWorkspace(cfg *config.Config) string {
	ws := cfg.ResolveWorkspace(workspaceFlag)
	if ws == "" {
		fmt.Fprintln(os.Stderr, "No workspace selected. Use --workspace <id> or run: investibook workspace use <id>")
		os.Exit(1)
	}
	return ws
}
