/*
 * 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 filecmd

import (
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"sigs.k8s.io/yaml"
)

func (util *FileUtil) MakeFileCmds() *cobra.Command {
	var subCmd = &cobra.Command{
		Use:     "files",
		Short:   "File operations",
		Aliases: []string{"file"},
	}
	const defaultTimeout uint64 = 10

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

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

	// PutFile
	var putFileCmd = &cobra.Command{
		Use:     "put filepath [user:pass@]hostname[:port]/collection/name",
		Args:    cobra.ExactArgs(2),
		Aliases: []string{"push"},
		Short:   "Put file to storage",
		Run:     util.PutFile,
	}
	subCmd.AddCommand(putFileCmd)

	// GetFile
	var getFileCmd = &cobra.Command{
		Use:     "get [user:pass@]hostname[:port]/collection/name filepath",
		Aliases: []string{"pull"},
		Args:    cobra.ExactArgs(2),
		Short:   "Get file from storage",
		Run:     util.GetFile,
	}
	subCmd.AddCommand(getFileCmd)

	// FileInfo
	var fileInfoCmd = &cobra.Command{
		Use:   "info [user:pass@]hostname[:port]/collection/name",
		Args:  cobra.ExactArgs(1),
		Short: "Show file information",
		Run:   util.FileInfo,
	}
	subCmd.AddCommand(fileInfoCmd)

	// DeleteFile
	var deleteFileCmd = &cobra.Command{
		Use:     "delete [user:pass@]hostname[:port]/collection/name",
		Args:    cobra.ExactArgs(1),
		Aliases: []string{"remove"},
		Short:   "Delete file in storage",
		Run:     util.DeleteFile,
	}
	subCmd.AddCommand(deleteFileCmd)

	// ListFiles
	var listFilesCmd = &cobra.Command{
		Use:   "list [user:pass@]hostname[:port]/catalog",
		Args:  cobra.ExactArgs(1),
		Short: "List files in storage",
		Run:   util.ListFiles,
	}
	listFilesCmd.Flags().BoolVarP(&util.listFilesParams.Detail, "detail", "D", false, "Show detail file information")
	listFilesCmd.Flags().BoolVarP(&util.listFilesParams.Prefix, "prefix", "P", true, "Use path as collection path prefix")
	listFilesCmd.Flags().BoolVarP(&util.listFilesParams.Regexp, "regex", "R", false, "Use path as collection path prefix")
	subCmd.AddCommand(listFilesCmd)

	// ImportFiles
	var importFilesCmd = &cobra.Command{
		Use:   "import directory [user:pass@]hostname[:port]/collection",
		Args:  cobra.ExactArgs(2),
		Short: "Send file tree to storage as is",
		Run:   util.ImportFiles,
	}
	subCmd.AddCommand(importFilesCmd)

	// ExportFiles
	var exportFilesCmd = &cobra.Command{
		Use:   "export directory [user:pass@]hostname[:port]/collection dir",
		Args:  cobra.ExactArgs(2),
		Short: "Download file tree to storage as is",
		Run:   util.ExportFiles,
	}
	exportFilesCmd.Flags().BoolVarP(&util.exportFilesParams.Detail, "detail", "D", false, "Show detail file information")
	exportFilesCmd.Flags().BoolVarP(&util.exportFilesParams.Prefix, "prefix", "P", true, "Use path as collection path prefix")
	exportFilesCmd.Flags().BoolVarP(&util.exportFilesParams.Regexp, "regex", "R", false, "Use path as collection path prefix")
	subCmd.AddCommand(exportFilesCmd)

	return subCmd
}

func (util *FileUtil) MakeCollectionCmds() *cobra.Command {
	var subCmd = &cobra.Command{
		Use:     "collections",
		Short:   "Colletion operations",
		Aliases: []string{"col", "cols", "dir", "dirs"},
	}
	const defaultTimeout uint64 = 10

	subCmd.PersistentFlags().StringVarP(&util.commonFileParams.Username, "user", "u", "", "Username")
	subCmd.PersistentFlags().StringVarP(&util.commonFileParams.Password, "pass", "p", "", "Password")
	subCmd.PersistentFlags().Uint64VarP(&util.commonFileParams.Timeout, "timeout", "t", defaultTimeout, "Operation timeout")
	subCmd.MarkPersistentFlagRequired("host")
	subCmd.MarkFlagsRequiredTogether("user", "pass")

	// ListCollections
	var listCollectionsCmd = &cobra.Command{
		Use:   "list [user:pass@]hostname[:port]/catalog",
		Args:  cobra.ExactArgs(1),
		Short: "List collections in storage",
		Run:   util.ListCollections,
	}
	listCollectionsCmd.Flags().BoolVarP(&util.listCollectionsParams.Prefix, "prefix", "P", true, "Use path as collection path prefix")
	subCmd.AddCommand(listCollectionsCmd)

	// DeleteCollection
	var deleteCollectionCmd = &cobra.Command{
		Use:   "delete [user:pass@]hostname[:port]/catalog",
		Args:  cobra.ExactArgs(1),
		Short: "Delete all files in collection",
		Run:   util.DeleteCollection,
	}
	deleteCollectionCmd.Flags().BoolVarP(&util.deleteCollectionParams.Detail, "detail", "D", false, "Show detail file information")
	deleteCollectionCmd.Flags().BoolVarP(&util.deleteCollectionParams.Prefix, "prefix", "P", false, "Use path as collection path prefix")
	deleteCollectionCmd.Flags().BoolVarP(&util.deleteCollectionParams.Regexp, "regex", "R", false, "Use path as collection path prefix")
	deleteCollectionCmd.Flags().BoolVarP(&util.deleteCollectionParams.DryRun, "dryrun", "Y", false, "Simulate process, don't delete files")

	subCmd.AddCommand(deleteCollectionCmd)

	return subCmd
}

type FileUtil struct {
	fileInfoParams         FileInfoParams
	putFileParams          PutFileParams
	getFileParams          GetFileParams
	deleteFileParams       DeleteFileParams
	listFilesParams        ListFilesParams
	importFilesParams      ImportFilesParams
	exportFilesParams      ExportFilesParams
	deleteCollectionParams DeleteCollectionParams
	listCollectionsParams  ListCollectionsParams
	commonFileParams       CommonFileParams
}

type CommonFileParams struct {
	Username      string
	Password      string
	Timeout       uint64
	SkipTLSVerify bool
}

func printResponse(res any, err error) {
	type Response struct {
		Error   bool   `json:"error"             json:"error"`
		Message string `json:"message,omitempty" json:"message,omitempty"`
		Result  any    `json:"result,omitempty"  json:"result,omitempty"`
	}
	resp := Response{}
	if err != nil {
		resp.Error = true
		resp.Message = err.Error()
	} else {
		resp.Result = res
	}
	respBytes, _ := yaml.Marshal(resp)
	fmt.Printf("---\n%s\n", string(respBytes))
}
