/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 *
 * This work is published and licensed under a Creative Commons
 * Attribution-NonCommercial-NoDerivatives 4.0 International License.
 *
 * Distribution of this work is permitted, but commercial use and
 * modifications are strictly prohibited.
 */
package main

import (
	"context"
	"regexp"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"mstore/pkg/client"
	"mstore/pkg/descr"
)

func (util *GrantUtil) CreateGrantCmds() *cobra.Command {
	var subCmd = &cobra.Command{
		Use:     "grants",
		Short:   "Grant operations",
		Aliases: []string{"grant"},
	}
	const defaultTimeout uint64 = 10

	subCmd.PersistentFlags().StringVarP(&util.commonGrantParams.Username, "user", "u", "", "Username")
	subCmd.PersistentFlags().StringVarP(&util.commonGrantParams.Password, "pass", "p", "", "Password")
	subCmd.PersistentFlags().Uint64VarP(&util.commonGrantParams.Timeout, "timeout", "t", defaultTimeout, "Operation timeout")
	subCmd.PersistentFlags().BoolVarP(&util.commonGrantParams.SkipTLSVerify, "skipVerify", "S", true, "Skip server certificate verify")

	vi := viper.New()
	vi.SetEnvPrefix("mstore")
	vi.BindEnv("user")
	vi.BindEnv("pass")
	util.commonGrantParams.Username = vi.GetString("user")
	util.commonGrantParams.Password = vi.GetString("pass")

	// CreateGrant
	var createGrantCmd = &cobra.Command{
		Use:   "create [user:pass@]hostname[:port] username|accountId rigth pattern",
		Short: "Create grant",
		Args:  cobra.ExactArgs(4),
		Run:   util.CreateGrant,
	}
	subCmd.AddCommand(createGrantCmd)

	// GetGrant
	var getGrantCmd = &cobra.Command{
		Use:   "get [user:pass@]hostname[:port] grantId",
		Short: "Get detail grant info",
		Args:  cobra.ExactArgs(2),
		Run:   util.GetGrant,
	}
	subCmd.AddCommand(getGrantCmd)

	// UpdateGrant
	var updateGrantCmd = &cobra.Command{
		Use:   "update [user:pass@]hostname[:port] gruntId newPattern",
		Short: "Update grant parameters",
		Args:  cobra.ExactArgs(3),
		Run:   util.UpdateGrant,
	}
	subCmd.AddCommand(updateGrantCmd)

	// DeleteGrant
	var deleteGrantCmd = &cobra.Command{
		Use:   "delete [user:pass@]hostname[:port] gruntId ",
		Short: "Delete grant",
		Args:  cobra.ExactArgs(2),

		Run: util.DeleteGrant,
	}
	subCmd.AddCommand(deleteGrantCmd)

	// ListGrants
	var listGrantsCmd = &cobra.Command{
		Use:   "list [user:pass@]hostname[:port] accountId|username",
		Short: "list user grants",
		Args:  cobra.ExactArgs(2),
		Run:   util.ListGrants,
	}
	listGrantsCmd.Flags().BoolVarP(&util.listGrantsParams.Detail, "detail", "d", false, "Show detail information")

	subCmd.AddCommand(listGrantsCmd)

	return subCmd
}

type GrantUtil struct {
	createGrantParams CreateGrantParams
	updateGrantParams UpdateGrantParams
	getGrantParams    GetGrantParams
	deleteGrantParams DeleteGrantParams
	listGrantsParams  ListGrantsParams
	commonGrantParams CommonGrantParams
}

type CommonGrantParams struct {
	Username      string
	Password      string
	Hostname      string
	Timeout       uint64
	SkipTLSVerify bool
}

// CreateGrant
type CreateGrantParams struct {
	AccountID string
	Right     string
	Pattern   string
}
type CreateGrantResult struct {
	GrantID string `yaml:"grantId"`
}

func (util *GrantUtil) CreateGrant(cmd *cobra.Command, args []string) {
	util.commonGrantParams.Hostname = args[0]
	util.createGrantParams.AccountID = args[1]
	util.createGrantParams.Right = args[2]
	util.createGrantParams.Pattern = args[3]
	res, err := util.createGrant(&util.commonGrantParams, &util.createGrantParams)
	printResponse(res, err)
}

func (util *GrantUtil) createGrant(common *CommonGrantParams, params *CreateGrantParams) (*CreateGrantResult, error) {
	var err error
	res := &CreateGrantResult{}
	hostname, err := packUserinfo(common.Hostname, common.Username, common.Password)
	if err != nil {
		return res, err
	}
	timeout := time.Duration(common.Timeout) * time.Second
	ctx, _ := context.WithTimeout(context.Background(), timeout)
	re := regexp.MustCompile(uuidRegex)
	id := strings.ToLower(params.AccountID)
	var operRes string
	if re.MatchString(id) {
		operRes, err = client.NewClient(common.SkipTLSVerify).CreateGrantByAccountID(ctx, hostname, id, params.Right, params.Pattern)
	} else {
		operRes, err = client.NewClient(common.SkipTLSVerify).CreateGrantByUsername(ctx, hostname, params.AccountID, params.Right, params.Pattern)
	}
	if err != nil {
		return res, err
	}
	res.GrantID = operRes
	return res, err
}

// UpdateGrant
type UpdateGrantParams struct {
	GrantID string
	Pattern string
}
type UpdateGrantResult struct{}

func (util *GrantUtil) UpdateGrant(cmd *cobra.Command, args []string) {
	util.commonGrantParams.Hostname = args[0]
	util.updateGrantParams.GrantID = args[1]
	util.updateGrantParams.Pattern = args[2]
	res, err := util.updateGrant(&util.commonGrantParams, &util.updateGrantParams)
	printResponse(res, err)
}

func (util *GrantUtil) updateGrant(common *CommonGrantParams, params *UpdateGrantParams) (*UpdateGrantResult, error) {
	var err error
	res := &UpdateGrantResult{}
	hostname, err := packUserinfo(common.Hostname, common.Username, common.Password)
	if err != nil {
		return res, err
	}
	timeout := time.Duration(common.Timeout) * time.Second
	ctx, _ := context.WithTimeout(context.Background(), timeout)
	id := strings.ToLower(params.GrantID)
	err = client.NewClient(common.SkipTLSVerify).UpdateGrant(ctx, hostname, id, params.Pattern)
	if err != nil {
		return res, err
	}
	return res, err
}

// GetGrant
type GetGrantParams struct {
	GrantID string
}

type GetGrantResult struct {
	Grant *descr.Grant `yaml:"grant,omitempty"`
}

func (util *GrantUtil) GetGrant(cmd *cobra.Command, args []string) {
	util.commonGrantParams.Hostname = args[0]
	util.getGrantParams.GrantID = args[1]

	res, err := util.getGrant(&util.commonGrantParams, &util.getGrantParams)
	printResponse(res, err)
}

func (util *GrantUtil) getGrant(common *CommonGrantParams, params *GetGrantParams) (*GetGrantResult, error) {
	var err error
	res := &GetGrantResult{}
	hostname, err := packUserinfo(common.Hostname, common.Username, common.Password)
	if err != nil {
		return res, err
	}
	timeout := time.Duration(common.Timeout) * time.Second
	ctx, _ := context.WithTimeout(context.Background(), timeout)
	opRes := &descr.Grant{}

	id := strings.ToLower(params.GrantID)
	opRes, err = client.NewClient(common.SkipTLSVerify).GetGrant(ctx, hostname, id)
	if err != nil {
		return res, err
	}
	res.Grant = opRes
	return res, err
}

// DeleteGrant
type DeleteGrantParams struct {
	GrantID string
}

type DeleteGrantResult struct{}

func (util *GrantUtil) DeleteGrant(cmd *cobra.Command, args []string) {
	util.commonGrantParams.Hostname = args[0]
	util.deleteGrantParams.GrantID = args[1]
	res, err := util.deleteGrant(&util.commonGrantParams, &util.deleteGrantParams)
	printResponse(res, err)
}
func (util *GrantUtil) deleteGrant(common *CommonGrantParams, params *DeleteGrantParams) (*DeleteGrantResult, error) {
	var err error
	res := &DeleteGrantResult{}
	hostname, err := packUserinfo(common.Hostname, common.Username, common.Password)
	if err != nil {
		return res, err
	}
	timeout := time.Duration(common.Timeout) * time.Second
	ctx, _ := context.WithTimeout(context.Background(), timeout)
	id := strings.ToLower(params.GrantID)
	err = client.NewClient(common.SkipTLSVerify).DeleteGrant(ctx, hostname, id)
	if err != nil {
		return res, err
	}
	return res, err
}

// ListGrants
type ListGrantsParams struct {
	Detail    bool
	AccountID string
}

type ListGrantsResult struct {
	Grants []descr.Grant     `yaml:"grants,omitempty"`
	Rights map[string]string `yaml:"rights,omitempty"`
}

func (util *GrantUtil) ListGrants(cmd *cobra.Command, args []string) {
	util.commonGrantParams.Hostname = args[0]
	util.listGrantsParams.AccountID = args[1]
	res, err := util.listGrants(&util.commonGrantParams, &util.listGrantsParams)
	printResponse(res, err)
}
func (util *GrantUtil) listGrants(common *CommonGrantParams, params *ListGrantsParams) (*ListGrantsResult, error) {
	var err error
	res := &ListGrantsResult{}
	hostname, err := packUserinfo(common.Hostname, common.Username, common.Password)
	if err != nil {
		return res, err
	}
	timeout := time.Duration(common.Timeout) * time.Second
	ctx, _ := context.WithTimeout(context.Background(), timeout)
	grants := make([]descr.Grant, 0)
	re := regexp.MustCompile(uuidRegex)
	id := strings.ToLower(params.AccountID)
	if re.MatchString(id) {
		grants, err = client.NewClient(common.SkipTLSVerify).ListGrantsByAccountID(ctx, hostname, id)
	} else {
		grants, err = client.NewClient(common.SkipTLSVerify).ListGrantsByUsername(ctx, hostname, params.AccountID)
	}
	if err != nil {
		return res, err
	}
	if params.Detail {
		res.Grants = grants
	} else {
		res.Rights = make(map[string]string, 0)
		for _, item := range grants {
			res.Rights[item.ID] = item.Right
		}
	}
	return res, err
}
