// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

// Package internal contains miscellaneous functions and types
// that are internal to the syspolicy packages.
package internal

import (
	"bytes"

	"github.com/go-json-experiment/json/jsontext"
	"github.com/sagernet/tailscale/types/lazy"
	"github.com/sagernet/tailscale/version"
)

// Init facilitates deferred invocation of initializers.
var Init lazy.DeferredInit

// OSForTesting is the operating system override used for testing.
// It follows the same naming convention as [version.OS].
var OSForTesting lazy.SyncValue[string]

// OS is like [version.OS], but supports a test hook.
func OS() string {
	return OSForTesting.Get(version.OS)
}

// TB is a subset of testing.TB that we use to set up test helpers.
// It's defined here to avoid pulling in the testing package.
type TB interface {
	Helper()
	Cleanup(func())
	Logf(format string, args ...any)
	Error(args ...any)
	Errorf(format string, args ...any)
	Fatal(args ...any)
	Fatalf(format string, args ...any)
}

// EqualJSONForTest compares the JSON in j1 and j2 for semantic equality.
// It returns "", "", true if j1 and j2 are equal. Otherwise, it returns
// indented versions of j1 and j2 and false.
func EqualJSONForTest(tb TB, j1, j2 jsontext.Value) (s1, s2 string, equal bool) {
	tb.Helper()
	j1 = j1.Clone()
	j2 = j2.Clone()
	// Canonicalize JSON values for comparison.
	if err := j1.Canonicalize(); err != nil {
		tb.Error(err)
	}
	if err := j2.Canonicalize(); err != nil {
		tb.Error(err)
	}
	// Check and return true if the two values are structurally equal.
	if bytes.Equal(j1, j2) {
		return "", "", true
	}
	// Otherwise, format the values for display and return false.
	if err := j1.Indent("", "\t"); err != nil {
		tb.Fatal(err)
	}
	if err := j2.Indent("", "\t"); err != nil {
		tb.Fatal(err)
	}
	return j1.String(), j2.String(), false
}
