package cmd

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

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

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

// RoleCommand returns the role command
func RoleCommand() *cli.Command {
	return &cli.Command{
		Name:  "role",
		Usage: "Manage roles",
		Description: `Manage ARP roles. Roles group permissions for access control.

Use this command to create, list, update, and delete roles.`,
		Commands: []*cli.Command{
			{
				Name:    "list",
				Aliases: []string{"ls"},
				Usage:   "List all roles",
				Flags: []cli.Flag{
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: roleList,
			},
			{
				Name:  "get",
				Usage: "Get a role by ID",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Role ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: roleGet,
			},
			{
				Name:   "create",
				Usage:  "Create a new role",
				Action: roleCreate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "name",
						Aliases: []string{"n"},
						Usage:   "Role name",
					},
					&cli.StringFlag{
						Name:    "description",
						Aliases: []string{"d"},
						Usage:   "Role description",
					},
					&cli.StringSliceFlag{
						Name:    "permissions",
						Aliases: []string{"p"},
						Usage:   "Permission IDs",
					},
				},
			},
			{
				Name:   "update",
				Usage:  "Update a role",
				Action: roleUpdate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Role ID",
						Required: true,
					},
					&cli.StringFlag{
						Name:    "name",
						Aliases: []string{"n"},
						Usage:   "Role name",
					},
					&cli.StringFlag{
						Name:    "description",
						Aliases: []string{"d"},
						Usage:   "Role description",
					},
					&cli.StringSliceFlag{
						Name:    "permissions",
						Aliases: []string{"p"},
						Usage:   "Permission IDs",
					},
				},
			},
			{
				Name:   "delete",
				Usage:  "Delete a role",
				Action: roleDelete,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Role ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "yes",
						Aliases: []string{"y"},
						Usage:   "Skip confirmation",
					},
				},
			},
		},
	}
}

type RoleDetail struct {
	ID          string       `json:"id"`
	Name        string       `json:"name"`
	Description string       `json:"description"`
	Permissions []Permission `json:"permissions"`
}

type Permission struct {
	ID          string `json:"id"`
	Code        string `json:"code"`
	Description string `json:"description"`
}

func roleList(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	query := "query Roles { roles { id name description permissions { id code } } }"

	resp, err := c.Query(query, nil)
	if err != nil {
		return err
	}

	var result struct {
		Roles []RoleDetail `json:"roles"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if cmd.Bool("json") {
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", "  ")
		return enc.Encode(result.Roles)
	}

	if len(result.Roles) == 0 {
		fmt.Println("No roles found.")
		return nil
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.Header([]string{"ID", "Name", "Description", "Permissions"})
	

	for _, r := range result.Roles {
		perms := make([]string, len(r.Permissions))
		for i, p := range r.Permissions {
			perms[i] = p.Code
		}
		table.Append([]string{r.ID, r.Name, r.Description, strings.Join(perms, ", ")})
	}

	table.Render()
	return nil
}

func roleGet(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	id := cmd.String("id")
	query := "query Role($id: ID!) { role(id: $id) { id name description permissions { id code description } } }"

	resp, err := c.Query(query, map[string]interface{}{"id": id})
	if err != nil {
		return err
	}

	var result struct {
		Role *RoleDetail `json:"role"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.Role == nil {
		return fmt.Errorf("role not found")
	}

	if cmd.Bool("json") {
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", "  ")
		return enc.Encode(result.Role)
	}

	r := result.Role
	fmt.Printf("ID: %s\n", r.ID)
	fmt.Printf("Name: %s\n", r.Name)
	fmt.Printf("Description: %s\n", r.Description)
	fmt.Printf("Permissions:\n")
	for _, p := range r.Permissions {
		fmt.Printf("  - %s (%s): %s\n", p.Code, p.ID, p.Description)
	}

	return nil
}

func roleCreate(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	name := cmd.String("name")
	description := cmd.String("description")
	permissions := cmd.StringSlice("permissions")

	if name == "" {
		prompt := &survey.Input{Message: "Role name:"}
		if err := survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	if description == "" {
		prompt := &survey.Input{Message: "Description:"}
		if err := survey.AskOne(prompt, &description, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	if len(permissions) == 0 {
		var permissionsStr string
		prompt := &survey.Input{Message: "Permission IDs (comma-separated):"}
		if err := survey.AskOne(prompt, &permissionsStr, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
		for _, p := range strings.Split(permissionsStr, ",") {
			permissions = append(permissions, strings.TrimSpace(p))
		}
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	mutation := `mutation CreateRole($input: NewRole!) { createRole(input: $input) { id name description permissions { id code } } }`

	input := map[string]interface{}{
		"name":        name,
		"description": description,
		"permissions": permissions,
	}

	resp, err := c.Mutation(mutation, map[string]interface{}{"input": input})
	if err != nil {
		return err
	}

	var result struct {
		CreateRole *RoleDetail `json:"createRole"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.CreateRole == nil {
		return fmt.Errorf("failed to create role")
	}

	fmt.Printf("Role created successfully!\n")
	fmt.Printf("ID: %s\n", result.CreateRole.ID)
	fmt.Printf("Name: %s\n", result.CreateRole.Name)

	return nil
}

func roleUpdate(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	id := cmd.String("id")
	name := cmd.String("name")
	description := cmd.String("description")
	permissions := cmd.StringSlice("permissions")

	if name == "" && description == "" && len(permissions) == 0 {
		fmt.Println("No updates provided. Use flags to specify what to update.")
		return nil
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	input := make(map[string]interface{})
	if name != "" {
		input["name"] = name
	}
	if description != "" {
		input["description"] = description
	}
	if len(permissions) > 0 {
		input["permissions"] = permissions
	}

	mutation := `mutation UpdateRole($id: ID!, $input: UpdateRoleInput!) { updateRole(id: $id, input: $input) { id name description permissions { id code } } }`

	resp, err := c.Mutation(mutation, map[string]interface{}{"id": id, "input": input})
	if err != nil {
		return err
	}

	var result struct {
		UpdateRole *RoleDetail `json:"updateRole"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.UpdateRole == nil {
		return fmt.Errorf("role not found")
	}

	fmt.Printf("Role updated successfully!\n")
	fmt.Printf("ID: %s\n", result.UpdateRole.ID)
	fmt.Printf("Name: %s\n", result.UpdateRole.Name)

	return nil
}

func roleDelete(ctx context.Context, cmd *cli.Command) error {
	cfg, err := config.Load()
	if err != nil {
		return err
	}
	if err := RequireAuth(cfg); err != nil {
		return err
	}

	id := cmd.String("id")
	skipConfirm := cmd.Bool("yes")

	if !skipConfirm {
		confirm := false
		prompt := &survey.Confirm{
			Message: fmt.Sprintf("Are you sure you want to delete role %s?", id),
			Default: false,
		}
		if err := survey.AskOne(prompt, &confirm); err != nil {
			return err
		}
		if !confirm {
			fmt.Println("Deletion cancelled.")
			return nil
		}
	}

	c := client.New(cfg.ServerURL)
	c.SetToken(cfg.Token)

	mutation := `mutation DeleteRole($id: ID!) { deleteRole(id: $id) }`

	resp, err := c.Mutation(mutation, map[string]interface{}{"id": id})
	if err != nil {
		return err
	}

	var result struct {
		DeleteRole bool `json:"deleteRole"`
	}
	if err := json.Unmarshal(resp.Data, &result); err != nil {
		return err
	}

	if result.DeleteRole {
		fmt.Printf("Role %s deleted successfully.\n", id)
	} else {
		fmt.Printf("Failed to delete role %s.\n", id)
	}

	return nil
}
