package config

import (
	"fmt"
	"strings"
)

// Config is the complete set of inputs to the action, parsed from env.
type Config struct {
	WorkingDirectory string
	AppName          string
	Version          string
	WailsVersion     string
	ExtraBuildFlags  string

	DeveloperIDCertBase64   string
	DeveloperIDCertPassword string

	NotarizationMethod        string // "api-key" | "apple-id" | "auto"
	NotarizationAPIKeyBase64  string
	NotarizationAPIKeyID      string
	NotarizationAPIIssuerID   string
	NotarizationAppleID       string
	NotarizationApplePassword string
	NotarizationTeamID        string

	S3Bucket      string
	S3Key         string
	S3EndpointURL string
	S3Region      string
}

// Load reads the action's INPUT_* environment variables.
func Load(get func(string) string) *Config {
	c := &Config{
		WorkingDirectory:          getOr(get, "INPUT_WORKING_DIRECTORY", "."),
		AppName:                   get("INPUT_APP_NAME"),
		Version:                   get("INPUT_VERSION"),
		WailsVersion:              get("INPUT_WAILS_VERSION"),
		ExtraBuildFlags:           get("INPUT_EXTRA_BUILD_FLAGS"),
		DeveloperIDCertBase64:     get("INPUT_DEVELOPER_ID_CERT_BASE64"),
		DeveloperIDCertPassword:   get("INPUT_DEVELOPER_ID_CERT_PASSWORD"),
		NotarizationMethod:        getOr(get, "INPUT_NOTARIZATION_METHOD", "auto"),
		NotarizationAPIKeyBase64:  get("INPUT_NOTARIZATION_API_KEY_BASE64"),
		NotarizationAPIKeyID:      get("INPUT_NOTARIZATION_API_KEY_ID"),
		NotarizationAPIIssuerID:   get("INPUT_NOTARIZATION_API_ISSUER_ID"),
		NotarizationAppleID:       get("INPUT_NOTARIZATION_APPLE_ID"),
		NotarizationApplePassword: get("INPUT_NOTARIZATION_APPLE_PASSWORD"),
		NotarizationTeamID:        get("INPUT_NOTARIZATION_TEAM_ID"),
		S3Bucket:                  get("INPUT_S3_BUCKET"),
		S3Key:                     get("INPUT_S3_KEY"),
		S3EndpointURL:             get("INPUT_S3_ENDPOINT_URL"),
		S3Region:                  getOr(get, "INPUT_S3_REGION", "us-east-1"),
	}
	return c
}

func getOr(get func(string) string, key, def string) string {
	if v := get(key); v != "" {
		return v
	}
	return def
}

// Validate checks structural rules. It does NOT touch the filesystem.
func (c *Config) Validate() error {
	var missing []string
	if c.DeveloperIDCertBase64 == "" {
		missing = append(missing, "developer-id-cert-base64")
	}
	if c.DeveloperIDCertPassword == "" {
		missing = append(missing, "developer-id-cert-password")
	}
	if len(missing) > 0 {
		return fmt.Errorf("missing required input(s): %s", strings.Join(missing, ", "))
	}

	apiFields := []struct{ name, value string }{
		{"notarization-api-key-base64", c.NotarizationAPIKeyBase64},
		{"notarization-api-key-id", c.NotarizationAPIKeyID},
		{"notarization-api-issuer-id", c.NotarizationAPIIssuerID},
	}
	appleFields := []struct{ name, value string }{
		{"notarization-apple-id", c.NotarizationAppleID},
		{"notarization-apple-password", c.NotarizationApplePassword},
		{"notarization-team-id", c.NotarizationTeamID},
	}

	apiFilled, apiMissing := groupStatus(apiFields)
	appleFilled, appleMissing := groupStatus(appleFields)

	switch c.NotarizationMethod {
	case "api-key":
		if !apiFilled {
			return fmt.Errorf("notarization-method=api-key requires: %s (missing: %s)",
				joinNames(apiFields), strings.Join(apiMissing, ", "))
		}
	case "apple-id":
		if !appleFilled {
			return fmt.Errorf("notarization-method=apple-id requires: %s (missing: %s)",
				joinNames(appleFields), strings.Join(appleMissing, ", "))
		}
	case "auto", "":
		switch {
		case apiFilled && appleFilled:
			return fmt.Errorf("notarization credentials are ambiguous: both api-key and apple-id groups are populated; set notarization-method explicitly")
		case !apiFilled && !appleFilled:
			return fmt.Errorf("no notarization credentials supplied: populate either the api-key group (%s) or the apple-id group (%s)",
				joinNames(apiFields), joinNames(appleFields))
		}
	default:
		return fmt.Errorf("notarization-method must be one of api-key, apple-id, auto (got %q)", c.NotarizationMethod)
	}

	if c.S3Bucket != "" && c.S3Key == "" {
		return fmt.Errorf("s3-bucket is set but s3-key is empty")
	}
	return nil
}

// ResolvedNotarizationMethod returns "api-key" or "apple-id" once Validate has succeeded.
func (c *Config) ResolvedNotarizationMethod() string {
	if c.NotarizationMethod == "api-key" || c.NotarizationMethod == "apple-id" {
		return c.NotarizationMethod
	}
	if c.NotarizationAPIKeyID != "" {
		return "api-key"
	}
	return "apple-id"
}

func groupStatus(fields []struct{ name, value string }) (filled bool, missing []string) {
	any := false
	for _, f := range fields {
		if f.value != "" {
			any = true
		} else {
			missing = append(missing, f.name)
		}
	}
	return any && len(missing) == 0, missing
}

func joinNames(fields []struct{ name, value string }) string {
	names := make([]string, len(fields))
	for i, f := range fields {
		names[i] = f.name
	}
	return strings.Join(names, ", ")
}
