package cmd

import (
	"context"
	"encoding/json"
	"fmt"

	"gogs.dmsc.dev/arp/arp_cli/client"
	"gogs.dmsc.dev/arp/arp_cli/config"

	"github.com/AlecAivazis/survey/v2"
	"github.com/urfave/cli/v3"
)

// LoginCommand returns the login command
func LoginCommand() *cli.Command {
	return &cli.Command{
		Name:  "login",
		Usage: "Authenticate with the ARP server",
		Description: `Login to an ARP server using email and password.

This command will store the authentication token in ~/.arp_cli/config.json
for use by subsequent commands.

Examples:
  # Login with URL and email specified
  arp_cli login --url http://localhost:8080/query --email user@example.com

  # Login interactively (will prompt for all fields)
  arp_cli login`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "url",
				Aliases: []string{"u"},
				Usage:   "ARP server URL",
				Sources: cli.EnvVars("ARP_URL"),
			},
			&cli.StringFlag{
				Name:    "email",
				Aliases: []string{"e"},
				Usage:   "User email address",
			},
			&cli.StringFlag{
				Name:    "password",
				Aliases: []string{"p"},
				Usage:   "User password (will prompt if not provided)",
			},
		},
		Action: doLogin,
	}
}

func doLogin(ctx context.Context, cmd *cli.Command) error {
	// Get URL
	serverURL := cmd.String("url")
	if serverURL == "" {
		cfg, err := config.Load()
		if err != nil {
			return fmt.Errorf("failed to load config: %w", err)
		}
		serverURL = cfg.ServerURL
	}

	// Prompt for URL if still empty
	if serverURL == "" {
		prompt := &survey.Input{
			Message: "Server URL",
			Default: "http://localhost:8080",
			Help:    "The ARP server URL (e.g., http://localhost:8080)",
		}
		if err := survey.AskOne(prompt, &serverURL, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	// Ensure URL has the /query endpoint
	serverURL = ensureQueryEndpoint(serverURL)

	// Get email
	email := cmd.String("email")
	if email == "" {
		prompt := &survey.Input{
			Message: "Email",
			Help:    "Your user account email address",
		}
		if err := survey.AskOne(prompt, &email, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	// Get password
	password := cmd.String("password")
	if password == "" {
		prompt := &survey.Password{
			Message: "Password",
			Help:    "Your user account password",
		}
		if err := survey.AskOne(prompt, &password, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	// Perform login
	c := client.New(serverURL)

	query := `
		mutation Login($email: String!, $password: String!) {
			login(email: $email, password: $password) {
				token
				user {
					id
					email
					roles {
						id
						name
					}
				}
			}
		}`

	variables := map[string]interface{}{
		"email":    email,
		"password": password,
	}

	resp, err := c.Mutation(query, variables)
	if err != nil {
		return fmt.Errorf("login failed: %w", err)
	}

	// Parse response
	var loginResp struct {
		Login struct {
			Token string `json:"token"`
			User  struct {
				ID    string `json:"id"`
				Email string `json:"email"`
				Roles []struct {
					ID   string `json:"id"`
					Name string `json:"name"`
				} `json:"roles"`
			} `json:"user"`
		} `json:"login"`
	}

	if err := json.Unmarshal(resp.Data, &loginResp); err != nil {
		return fmt.Errorf("failed to parse response: %w", err)
	}

	// Save config
	cfg, err := config.Load()
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}
	cfg.ServerURL = serverURL
	cfg.Token = loginResp.Login.Token
	cfg.UserEmail = loginResp.Login.User.Email

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

	fmt.Printf("Logged in as %s\n", loginResp.Login.User.Email)
	if len(loginResp.Login.User.Roles) > 0 {
		fmt.Print("Roles: ")
		for i, role := range loginResp.Login.User.Roles {
			if i > 0 {
				fmt.Print(", ")
			}
			fmt.Print(role.Name)
		}
		fmt.Println()
	}

	return nil
}

// ensureQueryEndpoint ensures the URL ends with /query
func ensureQueryEndpoint(url string) string {
	// Remove trailing slash if present
	for len(url) > 0 && url[len(url)-1] == '/' {
		url = url[:len(url)-1]
	}

	// Add /query if not already present
	if len(url) < 6 || url[len(url)-6:] != "/query" {
		url = url + "/query"
	}

	return url
}
