//go:build runtime_api_integration

package runtimeapi

import (
	"bytes"
	"context"
	"encoding/json"
	"log"
	"os"
	"strings"
	"testing"

	"github.com/nexuscrm/backend/internal/runtimeassembly"
	database "github.com/nexuscrm/db"
	"github.com/nexuscrm/kernel/pkg/schemacatalog"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/pagelayoutresolutionv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// Exercise the actual Runtime service, permission owner and database resolver.
// Authentication/Catalog admission use the existing trusted-context fixture;
// role ancestry and Layout selection are not stubs or a Catalog fallback.
func TestPageLayoutServiceRoleRootsOnTiDB(t *testing.T) {
	if os.Getenv("TIDB_HOST") == "" {
		t.Fatal("TIDB_HOST is required for Runtime integration tests")
	}
	for _, root := range []struct {
		name   string
		parent any
	}{{"null", nil}, {"empty", ""}} {
		t.Run(root.name, func(t *testing.T) {
			db, cleanup := openRuntimeAPIScratchDB(t)
			defer cleanup()
			ctx := t.Context()
			wanted := map[string]bool{"UserRole": true, "_System_RuntimeSecurityState": true, "_System_Object": true, "_System_Field": true, "_System_PageLayout": true, "_System_LayoutAssignment": true}
			definitions, err := schemacatalog.ManagedTableDefinitions()
			if err != nil {
				t.Fatal(err)
			}
			schema := persistence.NewSchemaRepository(db, nil)
			for _, definition := range definitions {
				if wanted[definition.TableName] {
					if err := schema.CreatePhysicalTable(ctx, definition); err != nil {
						t.Fatal(err)
					}
					delete(wanted, definition.TableName)
				}
			}
			if len(wanted) != 0 {
				t.Fatalf("missing canonical schema: %v", wanted)
			}
			exec := func(statement string, args ...any) {
				t.Helper()
				if _, err := db.ExecContext(ctx, statement, args...); err != nil {
					t.Fatal(err)
				}
			}
			// Imported/older rows may have empty roots or dangling parents. The fixture
			// uses a dedicated connection; runtime reads retain normal FK enforcement.
			seed, err := db.Conn(ctx)
			if err != nil {
				t.Fatal(err)
			}
			defer seed.Close()
			seedRole := func(statement string, args ...any) {
				t.Helper()
				if _, err := seed.ExecContext(ctx, "SET FOREIGN_KEY_CHECKS=0"); err != nil {
					t.Fatal(err)
				}
				if _, err := seed.ExecContext(ctx, statement, args...); err != nil {
					t.Fatal(err)
				}
				if _, err := seed.ExecContext(ctx, "SET FOREIGN_KEY_CHECKS=1"); err != nil {
					t.Fatal(err)
				}
			}
			seedRole(`INSERT INTO UserRole (Id,Name,ParentRoleId,IsDeleted) VALUES ('role-far','Far',?,0),('role-near','Near','role-far',0),('role-child','Child','role-near',0)`, root.parent)
			exec(`INSERT INTO _System_RuntimeSecurityState (OrgId,RuntimeAudience,SecurityEpoch) VALUES (?,'runtime',3)`, testOrgID)
			exec(`INSERT INTO _System_Object (Id,ApiName,TableType,Label,SharingModel) VALUES ('object-work-item','WorkItem','standard_object','Work item','Private')`)
			for _, kind := range []string{"default", "near", "far"} {
				layout := models.PageLayout{ID: "current-" + kind, ObjectAPIName: "WorkItem", LayoutName: kind, Type: "Detail", Source: "admin_created", IsCustomized: true}
				raw, err := json.Marshal(layout)
				if err != nil {
					t.Fatal(err)
				}
				exec(`INSERT INTO _System_PageLayout (Id,ObjectApiName,LayoutName,Config,Source,IsCustomized) VALUES (?,'WorkItem',?,?,'admin_created',1)`, layout.ID, kind, raw)
			}
			exec(`INSERT INTO _System_LayoutAssignment (Id,ObjectApiName,LayoutId,Specificity,RouteSource) VALUES ('default','WorkItem','current-default',0,'admin_authored')`)
			runtime, err := runtimeassembly.NewPageLayoutRuntime(database.NewConnectionFromDB(db), permissions.NewPermissionService(persistence.NewPermissionRepository(db), nil, nil))
			if err != nil {
				t.Fatal(err)
			}
			service, err := NewPageLayoutResolutionServer(runtime.Engine, runtime.Engine)
			if err != nil {
				t.Fatal(err)
			}
			// Catalog intentionally has no role assignments: current Runtime rows own
			// these assignments, and even default-only objects must accept legal roots.
			view := pageLayoutResolutionServerTestView(t, false)
			call := func(role string) (string, error) {
				callCtx := pageLayoutResolutionServerTestContextWithRole(t, view, &role)
				callCtx = context.WithValue(callCtx, constants.ContextKeyRequestID, "role-root-regression")
				request := semantic.Request{ObjectID: "WorkItem"}
				wireRequest, err := mapping.ToWireResolvePageLayoutRequest(callCtx, request)
				if err != nil {
					t.Fatal(err)
				}
				wireResponse, err := service.ResolvePageLayout(callCtx, wireRequest)
				if err != nil {
					return "", err
				}
				response, err := mapping.FromWireResolvePageLayoutResponse(request, wireResponse)
				if err != nil {
					t.Fatal(err)
				}
				if len(response.Selections) != 1 {
					t.Fatalf("selections=%v", response.Selections)
				}
				return response.Selections[0].LayoutID, nil
			}
			check := func(role, want string) {
				t.Helper()
				got, err := call(role)
				if err != nil || got != want {
					t.Fatalf("role=%s layout=%s want=%s err=%v", role, got, want, err)
				}
			}
			check("role-child", "current-default")
			check("role-far", "current-default")
			for _, role := range []string{"far", "near"} {
				exec(`INSERT INTO _System_LayoutAssignment (Id,ObjectApiName,LayoutId,RoleId,Specificity,RouteSource) VALUES (?,'WorkItem',?,?,1,'admin_authored')`, role, "current-"+role, "role-"+role)
			}
			check("role-child", "current-near")
			check("role-far", "current-far")
			for _, invalid := range []struct{ name, reason, sql string }{
				{"missing parent", "missing_parent", `UPDATE UserRole SET ParentRoleId='private-missing-parent' WHERE Id='role-far'`},
				{"whitespace parent", "missing_parent", `UPDATE UserRole SET ParentRoleId=' ' WHERE Id='role-far'`},
				{"cycle", "cycle", `UPDATE UserRole SET ParentRoleId='role-child' WHERE Id='role-far'`},
				{"deleted parent", "deleted_role", `UPDATE UserRole SET IsDeleted=1 WHERE Id='role-near'`},
				{"stale epoch", "role_or_epoch_unavailable", `UPDATE _System_RuntimeSecurityState SET SecurityEpoch=4`},
			} {
				t.Run(invalid.name, func(t *testing.T) {
					seedRole(`UPDATE UserRole SET ParentRoleId=?,IsDeleted=0 WHERE Id='role-far'`, root.parent)
					exec(`UPDATE UserRole SET IsDeleted=0 WHERE Id='role-near'`)
					exec(`UPDATE _System_RuntimeSecurityState SET SecurityEpoch=3`)
					seedRole(invalid.sql)
					var logs bytes.Buffer
					previous := log.Writer()
					log.SetOutput(&logs)
					defer log.SetOutput(previous)
					_, err := call("role-child")
					if status.Code(err) != codes.Unavailable {
						t.Fatalf("invalid hierarchy returned %v", err)
					}
					if strings.Contains(err.Error(), invalid.reason) || strings.Contains(err.Error(), "private-missing-parent") {
						t.Fatalf("external diagnostic leak: %v", err)
					}
					if !strings.Contains(logs.String(), "reason="+invalid.reason) || !strings.Contains(logs.String(), `request_id="role-root-regression"`) {
						t.Fatalf("missing safe correlated diagnosis: %s", logs.String())
					}
					for _, secret := range []string{"private-missing-parent", "role-child", testOrgID} {
						if strings.Contains(logs.String(), secret) {
							t.Fatalf("identity leaked in log: %s", logs.String())
						}
					}
				})
			}
		})
	}
}
