/*
 * SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
 * SPDX-License-Identifier: Apache-2.0
 */

package z

import (
	
	
	
	
	
	
	
	
	
	
)

// SuperFlagHelp makes it really easy to generate command line `--help` output for a SuperFlag. For
// example:
//
//	const flagDefaults = `enabled=true; path=some/path;`
//
//	var help string = z.NewSuperFlagHelp(flagDefaults).
//		Flag("enabled", "Turns on <something>.").
//		Flag("path", "The path to <something>.").
//		Flag("another", "Not present in defaults, but still included.").
//		String()
//
// The `help` string would then contain:
//
//	enabled=true; Turns on <something>.
//	path=some/path; The path to <something>.
//	another=; Not present in defaults, but still included.
//
// All flags are sorted alphabetically for consistent `--help` output. Flags with default values are
// placed at the top, and everything else goes under.
type SuperFlagHelp struct {
	head     string
	defaults *SuperFlag
	flags    map[string]string
}

func ( string) *SuperFlagHelp {
	return &SuperFlagHelp{
		defaults: NewSuperFlag(),
		flags:    make(map[string]string, 0),
	}
}

func ( *SuperFlagHelp) ( string) *SuperFlagHelp {
	.head = 
	return 
}

func ( *SuperFlagHelp) (,  string) *SuperFlagHelp {
	.flags[] = 
	return 
}

func ( *SuperFlagHelp) () string {
	 := make([]string, 0)
	 := make([]string, 0)
	for ,  := range .flags {
		,  := .defaults.m[]
		 := fmt.Sprintf("    %s=%s; %s\n", , , )
		if  {
			 = append(, )
		} else {
			 = append(, )
		}
	}
	sort.Strings()
	sort.Strings()
	 := strings.Join(, "")
	 := strings.Join(, "")
	if len(.defaults.m) == 0 && len() == 0 {
		// remove last newline
		 = [:len()-1]
	}
	// remove last newline
	if len(.defaults.m) == 0 && len() > 1 {
		 = [:len()-1]
	}
	return .head + "\n" +  + 
}

func parseFlag( string) (map[string]string, error) {
	 := make(map[string]string)
	for ,  := range strings.Split(, ";") {
		if strings.TrimSpace() == "" {
			continue
		}
		// For a non-empty separator, 0 < len(splits) ≤ 2.
		 := strings.SplitN(, "=", 2)
		 := strings.TrimSpace([0])
		if len() < 2 {
			return nil, fmt.Errorf("superflag: missing value for '%s' in flag: %s", , )
		}
		 = strings.ToLower()
		 = strings.ReplaceAll(, "_", "-")
		[] = strings.TrimSpace([1])
	}
	return , nil
}

type SuperFlag struct {
	m map[string]string
}

func ( string) *SuperFlag {
	,  := newSuperFlagImpl()
	if  != nil {
		log.Fatal()
	}
	return 
}

func newSuperFlagImpl( string) (*SuperFlag, error) {
	,  := parseFlag()
	if  != nil {
		return nil, 
	}
	return &SuperFlag{}, nil
}

func ( *SuperFlag) () string {
	if  == nil {
		return ""
	}
	 := make([]string, 0, len(.m))
	for ,  := range .m {
		 = append(, fmt.Sprintf("%s=%s", , ))
	}
	return strings.Join(, "; ")
}

func ( *SuperFlag) ( string) *SuperFlag {
	,  := .mergeAndCheckDefaultImpl()
	if  != nil {
		log.Fatal()
	}
	return 
}

func ( *SuperFlag) ( string) (*SuperFlag, error) {
	if  == nil {
		,  := parseFlag()
		if  != nil {
			return nil, 
		}
		return &SuperFlag{}, nil
	}

	,  := parseFlag()
	if  != nil {
		return nil, 
	}

	 := len(.m)
	for  := range  {
		if ,  := .m[];  {
			--
		}
	}
	if  != 0 {
		return nil, fmt.Errorf("superflag: found invalid options: %s.\nvalid options: %v", , )
	}
	for ,  := range  {
		if ,  := .m[]; ! {
			.m[] = 
		}
	}
	return , nil
}

func ( *SuperFlag) ( string) bool {
	 := .GetString()
	return  != ""
}

func ( *SuperFlag) ( string) time.Duration {
	 := .GetString()
	if  == "" {
		return time.Duration(0)
	}
	if strings.Contains(, "d") {
		 = strings.Replace(, "d", "", 1)
		,  := strconv.ParseInt(, 0, 64)
		if  != nil {
			return time.Duration(0)
		}
		return time.Hour * 24 * time.Duration()
	}
	,  := time.ParseDuration()
	if  != nil {
		return time.Duration(0)
	}
	return 
}

func ( *SuperFlag) ( string) bool {
	 := .GetString()
	if  == "" {
		return false
	}
	,  := strconv.ParseBool()
	if  != nil {
		 = errors.Join(,
			fmt.Errorf("Unable to parse %s as bool for key: %s. Options: %s\n", , , ))
		log.Fatalf("%+v", )
	}
	return 
}

func ( *SuperFlag) ( string) float64 {
	 := .GetString()
	if  == "" {
		return 0
	}
	,  := strconv.ParseFloat(, 64)
	if  != nil {
		 = errors.Join(,
			fmt.Errorf("Unable to parse %s as float64 for key: %s. Options: %s\n", , , ))
		log.Fatalf("%+v", )
	}
	return 
}

func ( *SuperFlag) ( string) int64 {
	 := .GetString()
	if  == "" {
		return 0
	}
	,  := strconv.ParseInt(, 0, 64)
	if  != nil {
		 = errors.Join(,
			fmt.Errorf("Unable to parse %s as int64 for key: %s. Options: %s\n", , , ))
		log.Fatalf("%+v", )
	}
	return 
}

func ( *SuperFlag) ( string) uint64 {
	 := .GetString()
	if  == "" {
		return 0
	}
	,  := strconv.ParseUint(, 0, 64)
	if  != nil {
		 = errors.Join(,
			fmt.Errorf("Unable to parse %s as uint64 for key: %s. Options: %s\n", , , ))
		log.Fatalf("%+v", )
	}
	return 
}

func ( *SuperFlag) ( string) uint32 {
	 := .GetString()
	if  == "" {
		return 0
	}
	,  := strconv.ParseUint(, 0, 32)
	if  != nil {
		 = errors.Join(,
			fmt.Errorf("Unable to parse %s as uint32 for key: %s. Options: %s\n", , , ))
		log.Fatalf("%+v", )
	}
	return uint32()
}

func ( *SuperFlag) ( string) string {
	if  == nil {
		return ""
	}
	return .m[]
}

func ( *SuperFlag) ( string) string {
	 := .GetString()
	,  := expandPath()
	if  != nil {
		log.Fatalf("Failed to get path: %+v", )
	}
	return 
}

// expandPath expands the paths containing ~ to /home/user. It also computes the absolute path
// from the relative paths. For example: ~/abc/../cef will be transformed to /home/user/cef.
func expandPath( string) (string, error) {
	if len() == 0 {
		return "", nil
	}
	if [0] == '~' && (len() == 1 || os.IsPathSeparator([1])) {
		,  := user.Current()
		if  != nil {
			return "", errors.Join(, errors.New("Failed to get the home directory of the user"))
		}
		 = filepath.Join(.HomeDir, [1:])
	}

	var  error
	,  = filepath.Abs()
	if  != nil {
		return "", errors.Join(, errors.New("Failed to generate absolute path"))
	}
	return , nil
}