package collab

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

	"github.com/spf13/cobra"

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

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

	cmd.AddCommand(newListCommand())
	cmd.AddCommand(newBindLarkGroupCommand())
	cmd.AddCommand(newCreateLarkGroupCommand())
	cmd.AddCommand(newBindLarkMeetingCommand())
	cmd.AddCommand(newUnbindCommand())

	return cmd
}

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

	cmd := &cobra.Command{
		Use:   "list",
		Short: "List workspace collaboration resources",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			query := url.Values{}
			query.Set("workspace_id", strconv.FormatUint(workspaceID, 10))
			query.Set("page", strconv.FormatUint(uint64(page), 10))
			query.Set("page_size", strconv.FormatUint(uint64(pageSize), 10))
			query.Set("with_resource_detail", "true")
			for _, rawResourceID := range resourceIDs {
				resourceID, err := parseUintArg("resource id", rawResourceID)
				if err != nil {
					return err
				}
				query.Add("resource_id", strconv.FormatUint(resourceID, 10))
			}

			var result wsapi.ListCollaborationResourcesResponse
			_, err := common.Client.R().
				SetContext(cmd.Context()).
				SetResult(&result).
				SetQueryParamsFromValues(query).
				Get("/workspace/collaboration-resource")
			if err != nil {
				return fmt.Errorf("list workspace %d collaboration resources: %w", workspaceID, err)
			}

			return common.Printer.PrintList(sliceutil.TransformSlice(result.Resources, func(value *wsent.CollaborationResource) any {
				return value
			}))
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().StringArrayVar(&resourceIDs, "resource-id", nil, "Filter by collaboration resource ID")
	cmd.Flags().Uint32Var(&page, "page", page, "Page number")
	cmd.Flags().Uint32Var(&pageSize, "page-size", pageSize, "Page size")

	return cmd
}

func newBindLarkGroupCommand() *cobra.Command {
	var workspaceID uint64
	var groupID string
	setPrimaryIfEmpty := true

	cmd := &cobra.Command{
		Use:   "bind-lark-group",
		Short: "Bind an existing Lark group to a workspace",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			createLarkGroup := false
			req := wsapi.BindCollaborationResourceRequest{
				WorkspaceID: workspaceID,
				LarkGroup: &wsapi.BindLarkGroupRequest{
					CreateLarkGroup:   &createLarkGroup,
					LinkLarkGroupID:   groupID,
					SetPrimaryIfEmpty: &setPrimaryIfEmpty,
				},
			}
			value, err := bindCollaborationResource(cmd, workspaceID, req)
			if err != nil {
				return fmt.Errorf("bind Lark group to workspace %d: %w", workspaceID, err)
			}

			return common.Printer.PrintObject(value)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().StringVar(&groupID, "group-id", "", "Lark group ID")
	cmd.Flags().BoolVar(&setPrimaryIfEmpty, "set-primary-if-empty", setPrimaryIfEmpty, "Set the group as primary when the workspace has no primary group")
	_ = cmd.MarkFlagRequired("group-id")

	return cmd
}

func newCreateLarkGroupCommand() *cobra.Command {
	var workspaceID uint64
	setPrimaryIfEmpty := true

	cmd := &cobra.Command{
		Use:   "create-lark-group",
		Short: "Create a Lark group and bind it to a workspace",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			createLarkGroup := true
			req := wsapi.BindCollaborationResourceRequest{
				WorkspaceID: workspaceID,
				LarkGroup: &wsapi.BindLarkGroupRequest{
					CreateLarkGroup:   &createLarkGroup,
					SetPrimaryIfEmpty: &setPrimaryIfEmpty,
				},
			}
			value, err := bindCollaborationResource(cmd, workspaceID, req)
			if err != nil {
				return fmt.Errorf("create Lark group for workspace %d: %w", workspaceID, err)
			}

			return common.Printer.PrintObject(value)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().BoolVar(&setPrimaryIfEmpty, "set-primary-if-empty", setPrimaryIfEmpty, "Set the group as primary when the workspace has no primary group")

	return cmd
}

func newBindLarkMeetingCommand() *cobra.Command {
	var workspaceID uint64
	var meetingID string

	cmd := &cobra.Command{
		Use:   "bind-lark-meeting",
		Short: "Bind an existing Lark meeting to a workspace",
		Args:  cobra.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			req := wsapi.BindCollaborationResourceRequest{
				WorkspaceID: workspaceID,
				LarkMeeting: &collabapi.CreateResourceForLarkMeetingRequest{
					MeetingID: meetingID,
				},
			}
			value, err := bindCollaborationResource(cmd, workspaceID, req)
			if err != nil {
				return fmt.Errorf("bind Lark meeting to workspace %d: %w", workspaceID, err)
			}

			return common.Printer.PrintObject(value)
		},
	}

	addWorkspaceIDFlag(cmd, &workspaceID)
	cmd.Flags().StringVar(&meetingID, "meeting-id", "", "Lark meeting ID")
	_ = cmd.MarkFlagRequired("meeting-id")

	return cmd
}

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

	cmd := &cobra.Command{
		Use:   "unbind <binding-id>",
		Short: "Unbind a collaboration resource from a workspace",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			bindingID, err := parseUintArg("binding id", args[0])
			if err != nil {
				return err
			}

			_, err = common.Client.R().
				SetContext(cmd.Context()).
				SetBody(&wsapi.UnbindCollaborationResourceRequest{
					WorkspaceID: workspaceID,
					ID:          bindingID,
				}).
				Delete("/workspace/collaboration-resource")
			if err != nil {
				return fmt.Errorf("unbind collaboration resource %d from workspace %d: %w", bindingID, workspaceID, err)
			}

			return nil
		},
	}

	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 bindCollaborationResource(cmd *cobra.Command, workspaceID uint64, req wsapi.BindCollaborationResourceRequest) (*wsent.CollaborationResource, error) {
	var result wsapi.BindCollaborationResourceResponse
	_, err := common.Client.R().
		SetContext(cmd.Context()).
		SetBody(&req).
		SetResult(&result).
		Post("/workspace/collaboration-resource")
	if err != nil {
		return nil, err
	}
	if result.Resource == nil {
		return nil, fmt.Errorf("workspace %d collaboration resource is empty", workspaceID)
	}

	return result.Resource, 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
}
