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"
)

// ServiceCommand returns the service command
func ServiceCommand() *cli.Command {
	return &cli.Command{
		Name:  "service",
		Usage: "Manage services",
		Description: `Manage ARP services. Services are entities that agents coordinate work around.

Services can have participants (users) and contain tasks. Use this command to
create, list, update, and delete services.`,
		Commands: []*cli.Command{
			{
				Name:    "list",
				Aliases: []string{"ls"},
				Usage:   "List all services",
				Flags: []cli.Flag{
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: serviceList,
			},
			{
				Name:  "get",
				Usage: "Get a service by ID",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Service ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "json",
						Aliases: []string{"j"},
						Usage:   "Output as JSON",
					},
				},
				Action: serviceGet,
			},
			{
				Name:   "create",
				Usage:  "Create a new service",
				Action: serviceCreate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "name",
						Aliases: []string{"n"},
						Usage:   "Service name",
					},
					&cli.StringFlag{
						Name:    "description",
						Aliases: []string{"d"},
						Usage:   "Service description",
					},
					&cli.StringFlag{
						Name:    "created-by",
						Aliases: []string{"c"},
						Usage:   "Creator user ID",
					},
					&cli.StringSliceFlag{
						Name:    "participants",
						Aliases: []string{"p"},
						Usage:   "Participant user IDs",
					},
				},
			},
			{
				Name:   "update",
				Usage:  "Update a service",
				Action: serviceUpdate,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Service ID",
						Required: true,
					},
					&cli.StringFlag{
						Name:    "name",
						Aliases: []string{"n"},
						Usage:   "Service name",
					},
					&cli.StringFlag{
						Name:    "description",
						Aliases: []string{"d"},
						Usage:   "Service description",
					},
					&cli.StringSliceFlag{
						Name:    "participants",
						Aliases: []string{"p"},
						Usage:   "Participant user IDs",
					},
				},
			},
			{
				Name:   "delete",
				Usage:  "Delete a service",
				Action: serviceDelete,
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "id",
						Aliases:  []string{"i"},
						Usage:    "Service ID",
						Required: true,
					},
					&cli.BoolFlag{
						Name:    "yes",
						Aliases: []string{"y"},
						Usage:   "Skip confirmation",
					},
				},
			},
		},
	}
}

type Service struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	CreatedByID string `json:"createdById"`
	CreatedBy   struct {
		ID    string `json:"id"`
		Email string `json:"email"`
	} `json:"createdBy"`
	Participants []struct {
		ID    string `json:"id"`
		Email string `json:"email"`
	} `json:"participants"`
	CreatedAt string `json:"createdAt"`
	UpdatedAt string `json:"updatedAt"`
}

func serviceList(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 Services { services { id name description createdBy { id email } participants { id email } createdAt updatedAt } }"

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

	var result struct {
		Services []Service `json:"services"`
	}
	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.Services)
	}

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

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

	for _, s := range result.Services {
		participants := fmt.Sprintf("%d", len(s.Participants))
		table.Append([]string{s.ID, s.Name, s.Description, s.CreatedBy.Email, participants, s.CreatedAt})
	}

	table.Render()
	return nil
}

func serviceGet(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 Service($id: ID!) { service(id: $id) { id name description createdBy { id email } participants { id email } createdAt updatedAt } }"

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

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

	if result.Service == nil {
		return fmt.Errorf("service not found")
	}

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

	s := result.Service
	fmt.Printf("ID: %s\n", s.ID)
	fmt.Printf("Name: %s\n", s.Name)
	fmt.Printf("Description: %s\n", s.Description)
	fmt.Printf("Created By: %s\n", s.CreatedBy.Email)
	fmt.Printf("Participants:\n")
	for _, p := range s.Participants {
		fmt.Printf("  - %s (%s)\n", p.Email, p.ID)
	}
	fmt.Printf("Created At: %s\n", s.CreatedAt)
	fmt.Printf("Updated At: %s\n", s.UpdatedAt)

	return nil
}

func serviceCreate(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")
	createdBy := cmd.String("created-by")
	participants := cmd.StringSlice("participants")

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

	if description == "" {
		prompt := &survey.Input{Message: "Description (optional):"}
		survey.AskOne(prompt, &description)
	}

	if createdBy == "" {
		prompt := &survey.Input{Message: "Creator user ID:"}
		if err := survey.AskOne(prompt, &createdBy, survey.WithValidator(survey.Required)); err != nil {
			return err
		}
	}

	if len(participants) == 0 {
		var participantsStr string
		prompt := &survey.Input{Message: "Participant user IDs (comma-separated, optional):"}
		if err := survey.AskOne(prompt, &participantsStr); err != nil {
			return err
		}
		if participantsStr != "" {
			for _, p := range strings.Split(participantsStr, ",") {
				participants = append(participants, strings.TrimSpace(p))
			}
		}
	}

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

	mutation := `mutation CreateService($input: NewService!) { createService(input: $input) { id name description createdBy { id email } participants { id email } createdAt updatedAt } }`

	input := map[string]interface{}{
		"name":         name,
		"description":  description,
		"createdById":  createdBy,
		"participants": participants,
	}

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

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

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

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

	return nil
}

func serviceUpdate(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")
	participants := cmd.StringSlice("participants")

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

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

	// Build the input dynamically
	input := make(map[string]interface{})
	if name != "" {
		input["name"] = name
	}
	if description != "" {
		input["description"] = description
	}
	if len(participants) > 0 {
		input["participants"] = participants
	}

	mutation := `mutation UpdateService($id: ID!, $input: UpdateServiceInput!) { updateService(id: $id, input: $input) { id name description createdBy { id email } participants { id email } createdAt updatedAt } }`

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

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

	if result.UpdateService == nil {
		return fmt.Errorf("service not found")
	}

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

	return nil
}

func serviceDelete(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 service %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 DeleteService($id: ID!) { deleteService(id: $id) }`

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

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

	if result.DeleteService {
		fmt.Printf("Service %s deleted successfully.\n", id)
	} else {
		fmt.Printf("Failed to delete service %s.\n", id)
	}

	return nil
}
