package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	dialectsfcrm "github.com/nexuscrm/dialect-sf-crm"
)

type inputPaths []string

func (values *inputPaths) String() string { return fmt.Sprint([]string(*values)) }
func (values *inputPaths) Set(value string) error {
	if value == "" {
		return errors.New("Approval publication input path is empty")
	}
	*values = append(*values, value)
	return nil
}

type metadataInputSpec struct {
	objectID       string
	definitionName string
	path           string
}

type metadataInputSpecs []metadataInputSpec

func (values *metadataInputSpecs) String() string { return fmt.Sprint([]metadataInputSpec(*values)) }
func (values *metadataInputSpecs) Set(value string) error {
	identityAndPath := strings.SplitN(value, "=", 2)
	if len(identityAndPath) != 2 {
		return errors.New("Approval metadata input must use Object/Definition=path")
	}
	identity := strings.SplitN(identityAndPath[0], "/", 2)
	if len(identity) != 2 || identity[0] == "" || identity[1] == "" || identityAndPath[1] == "" {
		return errors.New("Approval metadata input must use Object/Definition=path")
	}
	*values = append(*values, metadataInputSpec{
		objectID: identity[0], definitionName: identity[1], path: identityAndPath[1],
	})
	return nil
}

func main() {
	if err := run(os.Args[1:]); err != nil {
		_, _ = fmt.Fprintf(os.Stderr, "approval-publication: %v\n", err)
		os.Exit(1)
	}
}

func run(args []string) error {
	flags := flag.NewFlagSet("approval-publication", flag.ContinueOnError)
	metadataSourceDigest := flags.String("metadata-source-digest", "", "exact immutable L2 metadata tree digest")
	output := flags.String("output", "", "output path for the canonical neutral publication bundle")
	var inputs inputPaths
	var metadataInputs metadataInputSpecs
	flags.Var(&inputs, "input", "stopped-install Approval source snapshot; repeatable")
	flags.Var(&metadataInputs, "metadata-input", "complete Approval metadata source as Object/Definition=path; repeatable")
	if err := flags.Parse(args); err != nil {
		return err
	}
	if flags.NArg() != 0 || *metadataSourceDigest == "" || *output == "" || len(inputs) == 0 {
		return errors.New("-metadata-source-digest, -output, and at least one -input are required")
	}
	rawSeeds := make([][]byte, 0, len(inputs))
	for _, path := range inputs {
		raw, err := os.ReadFile(path)
		if err != nil {
			return fmt.Errorf("read Approval source snapshot %q: %w", path, err)
		}
		rawSeeds = append(rawSeeds, raw)
	}
	metadataSources := make([]dialectsfcrm.ApprovalPublicationMetadataSource, 0, len(metadataInputs))
	for _, input := range metadataInputs {
		raw, err := os.ReadFile(input.path)
		if err != nil {
			return fmt.Errorf("read Approval metadata source %q: %w", input.path, err)
		}
		metadataSources = append(metadataSources, dialectsfcrm.ApprovalPublicationMetadataSource{
			ObjectID: input.objectID, DefinitionName: input.definitionName, Raw: raw,
		})
	}
	bundle, digest, err := dialectsfcrm.CompileApprovalPublication(*metadataSourceDigest, rawSeeds, metadataSources)
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Dir(*output), 0o755); err != nil {
		return err
	}
	temporary, err := os.CreateTemp(filepath.Dir(*output), ".approval-publication-*")
	if err != nil {
		return err
	}
	temporaryPath := temporary.Name()
	defer os.Remove(temporaryPath)
	// The bundle digest and Catalog's fail-closed decoder cover the exact bytes.
	// Do not add presentation whitespace to this immutable contract artifact.
	if _, err := temporary.Write(bundle); err != nil {
		_ = temporary.Close()
		return err
	}
	if err := temporary.Chmod(0o600); err != nil {
		_ = temporary.Close()
		return err
	}
	if err := temporary.Close(); err != nil {
		return err
	}
	if err := os.Rename(temporaryPath, *output); err != nil {
		return err
	}
	_, _ = fmt.Fprintln(os.Stdout, digest)
	return nil
}
