package cmd

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"syscall"

	"forgejo.zerova.net/public/fj/internal/api"
	"forgejo.zerova.net/public/fj/internal/config"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"golang.org/x/term"
)

var authCmd = &cobra.Command{
	Use:   "auth",
	Short: "Authenticate fj with a Forgejo instance",
	Long:  "Manage authentication state for Forgejo instances.",
}

var authLoginCmd = &cobra.Command{
	Use:   "login",
	Short: "Authenticate with a Forgejo instance",
	Long:  "Authenticate with a Forgejo instance using a personal access token.",
	RunE:  runAuthLogin,
}

var authStatusCmd = &cobra.Command{
	Use:   "status",
	Short: "View authentication status",
	Long:  "Display the authentication status for configured Forgejo instances.",
	RunE:  runAuthStatus,
}

var authLogoutCmd = &cobra.Command{
	Use:   "logout",
	Short: "Remove authentication for a Forgejo instance",
	Long:  "Remove authentication for a configured Forgejo instance.",
	RunE:  runAuthLogout,
}

var authTokenCmd = &cobra.Command{
	Use:   "token",
	Short: "Print the stored authentication token",
	Long:  "Print the stored authentication token for a configured Forgejo instance.",
	RunE:  runAuthToken,
}

func init() {
	rootCmd.AddCommand(authCmd)
	authCmd.AddCommand(authLoginCmd)
	authCmd.AddCommand(authStatusCmd)
	authCmd.AddCommand(authLogoutCmd)
	authCmd.AddCommand(authTokenCmd)

	authLoginCmd.Flags().String("hostname", "", "Forgejo instance hostname (e.g., codeberg.org)")
	authLoginCmd.Flags().StringP("token", "t", "", "Personal access token")
	authLogoutCmd.Flags().String("hostname", "", "Forgejo instance hostname (e.g., codeberg.org)")
	authTokenCmd.Flags().String("hostname", "", "Forgejo instance hostname (e.g., codeberg.org)")
}

func runAuthLogin(cmd *cobra.Command, args []string) error {
	hostname, _ := cmd.Flags().GetString("hostname")
	token, _ := cmd.Flags().GetString("token")

	reader := bufio.NewReader(os.Stdin)

	if hostname == "" {
		fmt.Fprint(ios.ErrOut, "Forgejo instance hostname (default: codeberg.org): ")
		input, _ := reader.ReadString('\n')
		hostname = strings.TrimSpace(input)
		if hostname == "" {
			hostname = "codeberg.org"
		}
	}

	if token == "" {
		fmt.Fprint(ios.ErrOut, "Personal access token: ")
		tokenBytes, err := term.ReadPassword(int(syscall.Stdin))
		if err != nil {
			return fmt.Errorf("failed to read token: %w", err)
		}
		fmt.Fprintln(ios.ErrOut)
		token = strings.TrimSpace(string(tokenBytes))
	}

	if token == "" {
		return fmt.Errorf("token is required")
	}

	client, err := api.NewClient(hostname, token)
	if err != nil {
		return fmt.Errorf("failed to create client: %w", err)
	}

	ios.StartSpinner("Authenticating...")
	user, _, err := client.GetMyUserInfo()
	ios.StopSpinner()
	if err != nil {
		return fmt.Errorf("authentication failed: %w", err)
	}

	cfg, err := config.Load()
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}

	cfg.SetHost(hostname, config.HostConfig{
		Hostname:    hostname,
		Token:       token,
		User:        user.UserName,
		GitProtocol: "https",
	})

	if err := cfg.Save(); err != nil {
		return fmt.Errorf("failed to save config: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Authenticated as %s on %s\n", cs.SuccessIcon(), user.UserName, hostname)

	return nil
}

func runAuthStatus(cmd *cobra.Command, args []string) error {
	cfg, err := config.Load()
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}

	if len(cfg.Hosts) == 0 {
		fmt.Fprintln(ios.Out, "Not authenticated with any Forgejo instances")
		fmt.Fprintln(ios.Out, "Run 'fj auth login' to authenticate")
		return nil
	}

	fmt.Fprintln(ios.Out, "Authenticated instances:")
	for hostname, host := range cfg.Hosts {
		cs := ios.ColorScheme()
		fmt.Fprintf(ios.Out, "  %s %s (user: %s)\n", cs.SuccessIcon(), hostname, host.User)
	}

	return nil
}

func runAuthLogout(cmd *cobra.Command, args []string) error {
	cfg, err := config.Load()
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}

	hostname, _ := cmd.Flags().GetString("hostname")
	resolved, err := resolveAuthHostname(cfg, hostname)
	if err != nil {
		return err
	}

	delete(cfg.Hosts, resolved)
	if err := cfg.Save(); err != nil {
		return fmt.Errorf("failed to save config: %w", err)
	}

	cs := ios.ColorScheme()
	fmt.Fprintf(ios.Out, "%s Logged out from %s\n", cs.SuccessIcon(), resolved)
	return nil
}

func runAuthToken(cmd *cobra.Command, args []string) error {
	cfg, err := config.Load()
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}

	hostname, _ := cmd.Flags().GetString("hostname")
	resolved, err := resolveAuthHostname(cfg, hostname)
	if err != nil {
		return err
	}

	fmt.Fprintln(ios.Out, cfg.Hosts[resolved].Token)
	return nil
}

func resolveAuthHostname(cfg *config.Config, hostname string) (string, error) {
	if hostname == "" {
		hostname = viper.GetString("hostname")
	}
	if hostname == "" {
		hostname = os.Getenv("FGJ_HOST")
	}
	if hostname == "" {
		hostname = getDetectedHost()
	}
	if hostname == "" && len(cfg.Hosts) == 1 {
		for host := range cfg.Hosts {
			hostname = host
		}
	}
	if hostname == "" {
		hostname = "codeberg.org"
	}

	if _, ok := cfg.Hosts[hostname]; !ok {
		return "", fmt.Errorf("no configuration found for host %s", hostname)
	}

	return hostname, nil
}
