package task

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/spf13/cobra"

	"github.com/tidbcloud/aisupport/apps/cli/ticket-manager/common"
	wsapi "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/ticket-manager/workspace"
	taskent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/task"
	sliceutil "github.com/tidbcloud/aisupport/packages/sdk-go/infra/utils/container/slice"
)

// NewCommand builds workspace task subcommands.
func NewCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "task",
		Short: "Manage tasks for a workspace",
	}

	cmd.AddCommand(newListCommand())
	cmd.AddCommand(newCreateCommand())
	cmd.AddCommand(newGetCommand())
	cmd.AddCommand(newUpdateCommand())
	cmd.AddCommand(newDeleteCommand())
	cmd.AddCommand(newCompleteCommand())

	return cmd
}

func newListCommand() *cobra.Command {
	var workspaceID uint64
	var parentTaskID uint64
	var priorities []string
	var creator string
	var group string
	var page uint32 = 1
	var pageSize uint32 = 100

	cmd := &cobra.Command{
		Use:   "list",
		Short: "List workspace tasks",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			query := url.Values{}
			query.Set("page", strconv.FormatUint(uint64(page), 10))
			query.Set("page_size", strconv.FormatUint(uint64(pageSize), 10))
			if parentTaskID != 0 {
				query.Set("parent_task_id", strconv.FormatUint(parentTaskID, 10))
			}
			if creator != "" {
				query.Set("creator", creator)
			}
			if group != "" {
				query.Set("group", group)
			}
			for _, priority := range priorities {
				query.Add("priority", priority)
			}

			var result wsapi.ListTasksResponse
			_, err := common.Client.R().
				SetContext(cmd.Context()).
				SetPathParam("workspace_id", strconv.FormatUint(workspaceID, 10)).
				SetResult(&result).
				SetQueryParamsFromValues(query).
				Get("/workspace/workspaces/{workspace_id}/tasks")
			if err != nil {
				return fmt.Errorf("list workspace %d tasks: %w", workspaceID, err)
			}

			return common.Printer.PrintList(sliceutil.TransformSlice(result.Tasks, func(value *taskent.Task) any {
				return value
			}))
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().Uint64Var(&parentTaskID, "parent-task-id", 0, "Filter by parent task ID")
	cmd.Flags().StringArrayVar(&priorities, "priority", nil, "Filter by task priority")
	cmd.Flags().StringVar(&creator, "creator", "", "Filter by task creator")
	cmd.Flags().StringVar(&group, "group", "", "Filter by task group")
	cmd.Flags().Uint32Var(&page, "page", page, "Page number")
	cmd.Flags().Uint32Var(&pageSize, "page-size", pageSize, "Page size")

	return cmd
}

func newCreateCommand() *cobra.Command {
	var req wsapi.CreateTaskRequest
	var workspaceID uint64
	var priority string
	var dueAt int64
	var startedAt int64
	var group string
	var rawMembers []string

	cmd := &cobra.Command{
		Use:   "create",
		Short: "Create a workspace task",
		Args:  cobra.NoArgs,
		PreRunE: func(cmd *cobra.Command, args []string) error {
			req.Priority = taskent.TaskPriority(priority)
			if cmd.Flags().Changed("due-at") {
				req.DueAt = &dueAt
			}
			if cmd.Flags().Changed("started-at") {
				req.StartedAt = &startedAt
			}
			if group != "" {
				req.Group = taskent.TaskGroup(group)
			}

			members, err := parseMembers(rawMembers)
			if err != nil {
				return err
			}
			req.Members = members

			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			var result wsapi.CreateTaskResponse
			_, err := common.Client.R().
				SetContext(cmd.Context()).
				SetPathParam("workspace_id", strconv.FormatUint(workspaceID, 10)).
				SetBody(&req).
				SetResult(&result).
				Post("/workspace/workspaces/{workspace_id}/tasks")
			if err != nil {
				return fmt.Errorf("create task for workspace %d: %w", workspaceID, err)
			}

			return common.Printer.PrintObject(result.Task)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().StringVar(&req.Title, "title", "", "Task title")
	cmd.Flags().StringVar(&req.Description, "description", "", "Task description")
	cmd.Flags().StringVar(&priority, "priority", "", "Task priority")
	cmd.Flags().StringVar(&req.Creator, "creator", "", "Task creator")
	cmd.Flags().Int64Var(&dueAt, "due-at", 0, "Task due time as Unix seconds")
	cmd.Flags().Int64Var(&startedAt, "started-at", 0, "Task start time as Unix seconds")
	cmd.Flags().Uint64Var(&req.ParentTaskID, "parent-task-id", 0, "Parent task ID")
	cmd.Flags().StringVar(&group, "group", "", "Task group")
	cmd.Flags().StringArrayVar(&rawMembers, "member", nil, "Task member in email:role format")
	_ = cmd.MarkFlagRequired("title")
	_ = cmd.MarkFlagRequired("description")
	_ = cmd.MarkFlagRequired("priority")
	_ = cmd.MarkFlagRequired("creator")

	return cmd
}

func newGetCommand() *cobra.Command {
	var workspaceID uint64

	cmd := &cobra.Command{
		Use:   "get <task-id>",
		Short: "Get a workspace task",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			taskID, err := parseUintArg("task id", args[0])
			if err != nil {
				return err
			}

			var result wsapi.GetTaskResponse
			_, err = common.Client.R().
				SetContext(cmd.Context()).
				SetPathParam("workspace_id", strconv.FormatUint(workspaceID, 10)).
				SetPathParam("task_id", strconv.FormatUint(taskID, 10)).
				SetResult(&result).
				Get("/workspace/workspaces/{workspace_id}/tasks/{task_id}")
			if err != nil {
				return fmt.Errorf("get workspace %d task %d: %w", workspaceID, taskID, err)
			}

			return common.Printer.PrintObject(result.Task)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)

	return cmd
}

func newUpdateCommand() *cobra.Command {
	var req wsapi.UpdateTaskRequest
	var workspaceID uint64
	var dueAt int64
	var startedAt int64
	var rawMembers []string

	cmd := &cobra.Command{
		Use:   "update <task-id>",
		Short: "Update a workspace task",
		Args:  cobra.ExactArgs(1),
		PreRunE: func(cmd *cobra.Command, args []string) error {
			req = wsapi.UpdateTaskRequest{}
			if cmd.Flags().Changed("title") {
				value, err := cmd.Flags().GetString("title")
				if err != nil {
					return err
				}
				req.Title = &value
			}
			if cmd.Flags().Changed("description") {
				value, err := cmd.Flags().GetString("description")
				if err != nil {
					return err
				}
				req.Description = &value
			}
			if cmd.Flags().Changed("priority") {
				value, err := cmd.Flags().GetString("priority")
				if err != nil {
					return err
				}
				priority := taskent.TaskPriority(value)
				req.Priority = &priority
			}
			if cmd.Flags().Changed("due-at") {
				req.DueAt = &dueAt
			}
			if cmd.Flags().Changed("creator") {
				value, err := cmd.Flags().GetString("creator")
				if err != nil {
					return err
				}
				req.Creator = &value
			}
			if cmd.Flags().Changed("started-at") {
				req.StartedAt = &startedAt
			}
			if cmd.Flags().Changed("group") {
				value, err := cmd.Flags().GetString("group")
				if err != nil {
					return err
				}
				group := taskent.TaskGroup(value)
				req.Group = &group
			}
			if cmd.Flags().Changed("member") {
				values, err := cmd.Flags().GetStringArray("member")
				if err != nil {
					return err
				}
				members, err := parseMembers(values)
				if err != nil {
					return err
				}
				req.Members = &members
			}
			if req.Title == nil && req.Description == nil && req.Priority == nil && req.DueAt == nil &&
				req.Creator == nil && req.StartedAt == nil && req.Group == nil && req.Members == nil {
				return fmt.Errorf("at least one update field is required")
			}

			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			taskID, err := parseUintArg("task id", args[0])
			if err != nil {
				return err
			}

			var result wsapi.UpdateTaskResponse
			_, err = common.Client.R().
				SetContext(cmd.Context()).
				SetPathParam("workspace_id", strconv.FormatUint(workspaceID, 10)).
				SetPathParam("task_id", strconv.FormatUint(taskID, 10)).
				SetBody(&req).
				SetResult(&result).
				Put("/workspace/workspaces/{workspace_id}/tasks/{task_id}")
			if err != nil {
				return fmt.Errorf("update workspace %d task %d: %w", workspaceID, taskID, err)
			}

			return common.Printer.PrintObject(result.Task)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().String("title", "", "Task title")
	cmd.Flags().String("description", "", "Task description")
	cmd.Flags().String("priority", "", "Task priority")
	cmd.Flags().Int64Var(&dueAt, "due-at", 0, "Task due time as Unix seconds")
	cmd.Flags().String("creator", "", "Task creator")
	cmd.Flags().Int64Var(&startedAt, "started-at", 0, "Task start time as Unix seconds")
	cmd.Flags().String("group", "", "Task group")
	cmd.Flags().StringArrayVar(&rawMembers, "member", nil, "Task member in email:role format")

	return cmd
}

func newDeleteCommand() *cobra.Command {
	var workspaceID uint64

	cmd := &cobra.Command{
		Use:   "delete <task-id>",
		Short: "Delete a workspace task",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			taskID, err := parseUintArg("task id", args[0])
			if err != nil {
				return err
			}

			_, err = common.Client.R().
				SetContext(cmd.Context()).
				SetPathParam("workspace_id", strconv.FormatUint(workspaceID, 10)).
				SetPathParam("task_id", strconv.FormatUint(taskID, 10)).
				Delete("/workspace/workspaces/{workspace_id}/tasks/{task_id}")
			if err != nil {
				return fmt.Errorf("delete workspace %d task %d: %w", workspaceID, taskID, err)
			}

			return nil
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)

	return cmd
}

func newCompleteCommand() *cobra.Command {
	var workspaceID uint64

	cmd := &cobra.Command{
		Use:   "complete <task-id>",
		Short: "Complete a workspace task",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			taskID, err := parseUintArg("task id", args[0])
			if err != nil {
				return err
			}

			var result wsapi.CompleteTaskResponse
			_, err = common.Client.R().
				SetContext(cmd.Context()).
				SetPathParam("workspace_id", strconv.FormatUint(workspaceID, 10)).
				SetPathParam("task_id", strconv.FormatUint(taskID, 10)).
				SetResult(&result).
				Post("/workspace/workspaces/{workspace_id}/tasks/{task_id}/complete")
			if err != nil {
				return fmt.Errorf("complete workspace %d task %d: %w", workspaceID, taskID, err)
			}

			return common.Printer.PrintObject(result.Task)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)

	return cmd
}

func addWorkspaceIDFlag(cmd *cobra.Command, workspaceID *uint64) {
	cmd.Flags().Uint64Var(workspaceID, "workspace-id", 0, "Workspace ID")
	_ = cmd.MarkFlagRequired("workspace-id")
}

func parseMembers(values []string) ([]taskent.TaskMember, error) {
	if len(values) == 0 {
		return nil, nil
	}

	members := make([]taskent.TaskMember, 0, len(values))
	for _, value := range values {
		email, roleValue, ok := strings.Cut(value, ":")
		if !ok || email == "" || roleValue == "" {
			return nil, fmt.Errorf("invalid member %q: expected email:role", value)
		}

		role := taskent.TaskMemberRole(roleValue)
		switch role {
		case taskent.TaskMemberRoleOperator, taskent.TaskMemberRoleBackup:
		default:
			return nil, fmt.Errorf("invalid member role %q: expected operator or backup", roleValue)
		}

		members = append(members, taskent.TaskMember{
			MemberEmail: email,
			Role:        role,
		})
	}

	return members, nil
}

func parseUintArg(name string, value string) (uint64, error) {
	id, err := strconv.ParseUint(value, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("invalid %s %q: %w", name, value, err)
	}
	return id, nil
}
