package persistence

import (
	"bytes"
	"context"
	"errors"
	"sync"
	"sync/atomic"
	"testing"

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

func TestIntegration_CreatePermissionStateVersionSingleton(t *testing.T) {
	db, cleanup := openSchemaDDLTiDB(t)
	defer cleanup()

	definitions, err := schemacatalog.ManagedTableDefinitions()
	if err != nil {
		t.Fatal(err)
	}
	repository := NewSchemaRepository(db, nil)
	found := false
	for _, definition := range definitions {
		if definition.TableName != PermissionStateVersionTable {
			continue
		}
		found = true
		if err := repository.CreatePhysicalTable(context.Background(), definition); err != nil {
			t.Fatalf("CreatePhysicalTable(%s): %v", definition.TableName, err)
		}
	}
	if !found {
		t.Fatalf("%s not found in managed table definitions", PermissionStateVersionTable)
	}

	var lineageClaims atomic.Uint32
	stateRepository, err := newPermissionStateRepository(db, func() (permissionstatev1.Lineage, error) {
		value := append([]byte(nil), permissionStateLineage...)
		value[len(value)-1] = byte(lineageClaims.Add(1))
		return permissionstatev1.LineageFromBytes(value)
	})
	if err != nil {
		t.Fatal(err)
	}

	const contenders = 8
	states := make([]PermissionState, contenders)
	errs := make([]error, contenders)
	start := make(chan struct{})
	var wait sync.WaitGroup
	for index := range states {
		wait.Add(1)
		go func() {
			defer wait.Done()
			<-start
			states[index], errs[index] = stateRepository.EnsureInitialized(context.Background())
		}()
	}
	close(start)
	wait.Wait()
	for index, err := range errs {
		if err != nil {
			t.Fatalf("contender %d EnsureInitialized(): %v", index, err)
		}
	}
	winner := states[0].Version.Lineage().Bytes()
	for index, state := range states {
		if err := state.Validate(); err != nil || state.Version.Revision() != 1 ||
			state.EnforcementMode != PermissionStateDarkMode ||
			!bytes.Equal(state.Version.Lineage().Bytes(), winner) {
			t.Fatalf("contender %d state = %+v, validation %v, want one shared revision-one dark winner", index, state, err)
		}
	}
	assertCount(t, db, "SELECT COUNT(*) FROM `_System_PermissionStateVersion`", 1, "concurrent initialization must create exactly one singleton")

	ready, err := stateRepository.CheckReady(context.Background())
	if err != nil {
		t.Fatalf("CheckReady(): %v", err)
	}
	assertPermissionState(t, ready, winner, 1, PermissionStateDarkMode)

	noRewriteRepository, err := newPermissionStateRepository(db, func() (permissionstatev1.Lineage, error) {
		return permissionstatev1.Lineage{}, errors.New("existing singleton must not claim another lineage")
	})
	if err != nil {
		t.Fatal(err)
	}
	second, err := noRewriteRepository.EnsureInitialized(context.Background())
	if err != nil {
		t.Fatalf("idempotent EnsureInitialized(): %v", err)
	}
	assertPermissionState(t, second, winner, 1, PermissionStateDarkMode)
}
