package cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/spf13/cobra"
)

var connectOpts struct {
	token     string
	serverURL string
}

var connectCmd = &cobra.Command{
	Use:    "connect [agentteam://connect?token=...&origin=...]",
	Hidden: true,
	Short:  "Connect this machine to a AgentTeam server via browser token",
	Long: `Connect this machine to a AgentTeam server using a one-time token
from the web UI.

Can be invoked with --token and --server-url flags, or by passing the full
agentteam:// URL as a positional argument.`,
	SilenceUsage: true,
	RunE:         runConnect,
}

func init() {
	connectCmd.Flags().StringVar(&connectOpts.token, "token", "", "one-time connect token from the web UI")
	connectCmd.Flags().StringVar(&connectOpts.serverURL, "server-url", "", "AgentTeam server URL")
	rootCmd.AddCommand(connectCmd)
}

func runConnect(_ *cobra.Command, args []string) error {
	token := strings.TrimSpace(connectOpts.token)
	serverURL := strings.TrimSpace(connectOpts.serverURL)

	if len(args) > 0 {
		parsedToken, parsedServerURL, err := parseConnectURL(args[0])
		if err != nil {
			return fmt.Errorf("parse connect URL: %w", err)
		}
		token = parsedToken
		serverURL = parsedServerURL
	}

	if token == "" {
		return fmt.Errorf("token is required: use --token or pass a agentteam://connect URL")
	}
	if serverURL == "" {
		return fmt.Errorf("server URL is required: use --server-url or pass a agentteam://connect URL with origin")
	}

	result, err := exchangeMachineConnectToken(serverURL, token)
	if err != nil {
		return fmt.Errorf("exchange machine connect token: %w", err)
	}

	op, err := getOperator()
	if err != nil {
		return err
	}
	saveResult, err := upsertAgentTeamAuth(op.Servers(), *result)
	if err != nil {
		return err
	}

	printAgentTeamAuthResult(saveResult)

	status := daemonStatus()
	if status.Running && status.Healthy {
		return nil
	}
	return runDaemonForeground()
}

func parseConnectURL(rawURL string) (token string, serverURL string, err error) {
	parsedURL, err := url.Parse(strings.TrimSpace(rawURL))
	if err != nil {
		return "", "", fmt.Errorf("invalid URL: %w", err)
	}
	if parsedURL.Scheme != "agentteam" {
		return "", "", fmt.Errorf("expected agentteam:// URL, got %s://", parsedURL.Scheme)
	}
	host := strings.TrimSpace(parsedURL.Host)
	path := strings.Trim(strings.TrimSpace(parsedURL.Path), "/")
	if host != "connect" && !(host == "auth" && path == "login") {
		return "", "", fmt.Errorf("expected agentteam://auth/login or agentteam://connect URL")
	}
	token = strings.TrimSpace(parsedURL.Query().Get("token"))
	if token == "" {
		return "", "", fmt.Errorf("URL is missing token parameter")
	}
	serverURL = strings.TrimSpace(parsedURL.Query().Get("origin"))
	if serverURL == "" {
		return "", "", fmt.Errorf("URL is missing origin parameter")
	}
	return token, serverURL, nil
}

type agentteamAuthResolution struct {
	MachineID   string `json:"machineId"`
	APIKey      string `json:"apiKey"`
	ServerURL   string `json:"serverUrl"`
	WorkspaceID string `json:"workspaceId"`
}

func exchangeMachineConnectToken(serverURL string, token string) (*agentteamAuthResolution, error) {
	payload, err := json.Marshal(map[string]string{"token": token})
	if err != nil {
		return nil, fmt.Errorf("marshal request: %w", err)
	}

	endpoint := strings.TrimRight(serverURL, "/") + "/api/v1/machine-connect-exchanges"
	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Post(endpoint, "application/json", bytes.NewReader(payload))
	if err != nil {
		return nil, fmt.Errorf("request exchange endpoint: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read exchange response: %w", err)
	}
	if resp.StatusCode == http.StatusGone {
		return nil, fmt.Errorf("token expired or already used")
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server returned %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
	}

	var result agentteamAuthResolution
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("decode exchange response: %w", err)
	}
	return normalizeAgentTeamAuthResolution(serverURL, "", &result)
}
