package setupcmd

import (
	"bytes"
	"context"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"math"
	"strconv"

	"github.com/nexuscrm/persistence"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionstatev1"
)

const permissionStateActivationCommand = "activate-permission-state"

type permissionStateActivationArgs struct {
	expected permissionstatev1.Version
}

type permissionStateActivator interface {
	PromoteToEnforced(context.Context, permissionstatev1.Version) (persistence.PermissionState, error)
}

type permissionStateActivatorFactory func() (permissionStateActivator, error)

type permissionStateActivationOutput struct {
	Lineage         string `json:"lineage"`
	Revision        uint64 `json:"revision"`
	EnforcementMode string `json:"enforcementMode"`
}

func runPermissionStateActivationCommand(
	ctx context.Context,
	args []string,
	stdout io.Writer,
	stderr io.Writer,
	factory permissionStateActivatorFactory,
) int {
	parsed, err := parsePermissionStateActivationArgs(args, stderr)
	if err != nil {
		fmt.Fprintf(stderr, "%s: %v\n", permissionStateActivationCommand, err)
		return 2
	}
	if ctx == nil {
		fmt.Fprintf(stderr, "%s: context is required\n", permissionStateActivationCommand)
		return 1
	}
	if factory == nil {
		fmt.Fprintf(stderr, "%s: repository factory is required\n", permissionStateActivationCommand)
		return 1
	}
	activator, err := factory()
	if err != nil {
		fmt.Fprintf(stderr, "%s: open repository: %v\n", permissionStateActivationCommand, err)
		return 1
	}
	if activator == nil {
		fmt.Fprintf(stderr, "%s: open repository: activator is unavailable\n", permissionStateActivationCommand)
		return 1
	}
	state, err := activator.PromoteToEnforced(ctx, parsed.expected)
	if err != nil {
		fmt.Fprintf(stderr, "%s: activate permission state: %v\n", permissionStateActivationCommand, err)
		return 1
	}
	expectedSuccessor, err := permissionstatev1.NewVersion(
		parsed.expected.Lineage(),
		parsed.expected.Revision()+1,
	)
	if err != nil {
		fmt.Fprintf(stderr, "%s: construct exact successor: %v\n", permissionStateActivationCommand, err)
		return 1
	}
	comparison, compareErr := state.Version.Compare(expectedSuccessor)
	if validateErr := state.Validate(); validateErr != nil ||
		compareErr != nil ||
		comparison != 0 ||
		state.EnforcementMode != persistence.PermissionStateEnforcedMode {
		fmt.Fprintf(
			stderr,
			"%s: activation did not return the exact successor enforced state\n",
			permissionStateActivationCommand,
		)
		return 1
	}
	if err := json.NewEncoder(stdout).Encode(permissionStateActivationOutput{
		Lineage:         permissionStateLineageHex(state.Version.Lineage()),
		Revision:        state.Version.Revision(),
		EnforcementMode: string(state.EnforcementMode),
	}); err != nil {
		fmt.Fprintf(stderr, "%s: write result: %v\n", permissionStateActivationCommand, err)
		return 1
	}
	return 0
}

func parsePermissionStateActivationArgs(
	args []string,
	stderr io.Writer,
) (permissionStateActivationArgs, error) {
	fs := flag.NewFlagSet(permissionStateActivationCommand, flag.ContinueOnError)
	fs.SetOutput(stderr)
	lineageHex := fs.String(
		"expected-lineage",
		"",
		"exact 16-byte Permission State predecessor lineage as 32 hexadecimal characters",
	)
	revisionText := fs.String(
		"expected-revision",
		"",
		"exact positive decimal Permission State predecessor revision",
	)
	confirmed := fs.Bool(
		"confirm-maintenance-window",
		false,
		"confirm that all servers, workers, adapters, and external ingress are stopped",
	)
	if err := fs.Parse(args); err != nil {
		return permissionStateActivationArgs{}, err
	}
	if fs.NArg() != 0 {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"unexpected arguments: %v",
			fs.Args(),
		)
	}
	if !*confirmed {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"--confirm-maintenance-window is required after stopping all servers, workers, adapters, and external ingress",
		)
	}
	if len(*lineageHex) != permissionstatev1.LineageBytes*2 {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"--expected-lineage must contain exactly 32 hexadecimal characters",
		)
	}
	lineageBytes, err := hex.DecodeString(*lineageHex)
	if err != nil {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"--expected-lineage must contain exactly 32 hexadecimal characters: %w",
			err,
		)
	}
	if bytes.Equal(lineageBytes, make([]byte, permissionstatev1.LineageBytes)) {
		return permissionStateActivationArgs{}, fmt.Errorf("--expected-lineage must be nonzero")
	}
	lineage, err := permissionstatev1.LineageFromBytes(lineageBytes)
	if err != nil {
		return permissionStateActivationArgs{}, fmt.Errorf("--expected-lineage is invalid: %w", err)
	}
	revision, err := strconv.ParseUint(*revisionText, 10, 64)
	if err != nil || revision == 0 {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"--expected-revision must be a positive decimal integer",
		)
	}
	if revision == math.MaxUint64 {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"--expected-revision must have a uint64 successor",
		)
	}
	expected, err := permissionstatev1.NewVersion(lineage, revision)
	if err != nil {
		return permissionStateActivationArgs{}, fmt.Errorf(
			"construct expected Permission State version: %w",
			err,
		)
	}
	return permissionStateActivationArgs{expected: expected}, nil
}

func permissionStateLineageHex(lineage permissionstatev1.Lineage) string {
	return hex.EncodeToString(lineage.Bytes())
}
