//go:build runtime_api_integration

package runtimeapi_test

import (
	"context"
	"crypto/ed25519"
	"database/sql"
	"errors"
	"fmt"
	"net"
	"os"
	"strings"
	"sync/atomic"
	"testing"
	"time"

	"github.com/go-sql-driver/mysql"
	featureauth "github.com/nexuscrm/auth"
	"github.com/nexuscrm/backend/internal/runtimeapi"
	"github.com/nexuscrm/backend/internal/runtimeidentity"
	pkgauth "github.com/nexuscrm/kernel/pkg/auth"
	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/records"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1"
	identitysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	recordsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordsv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
)

const (
	identityTestOrg          = "org-identity-real"
	identityTestReplica      = "replica-identity-real"
	identityTestAudience     = "runtime-identity-real"
	identityTestInstallation = "installation-identity-real"
	identityTestWorkload     = "spiffe://identity.test/sfcrm"
	identityTestCallerHandle = "operator-handle-identity-real"
	identityTestPrincipal    = "user-identity-real"
	identityTestProfile      = "profile-identity"
	identityTestRole         = "role-identity"
	identityTestSession      = "session-identity-real"
	identityTestConnectedApp = "connected-app-identity-real"
)

// This is the F1-I walking slice: a real bearer is validated through
// AuthService and TiDB, exchanged over generated gRPC, signed by the one
// Runtime issuer, then accepted by the existing SecurityAdmission before the
// Records adapter can run.
func TestIdentityExchangeTokenIsAcceptedByExistingRuntimeAdmission(t *testing.T) {
	if os.Getenv("TIDB_HOST") == "" {
		t.Skip("runtime_api_integration requires the declared TiDB environment")
	}
	db, cleanup := openIdentityScratchDB(t)
	defer cleanup()
	createIdentityAuthorityTables(t, db)
	now := time.Now().UTC().Truncate(time.Second)
	roleID := identityTestRole
	bearer, _, err := pkgauth.GenerateTokenForSession(pkgauth.UserSession{
		ID: identityTestPrincipal, ProfileId: identityTestProfile, RoleId: &roleID,
		Scope:          recordsemantic.CapabilityName,
		ConnectedAppId: identityTestConnectedApp, GrantType: "authorization_code",
	}, identityTestSession, now, time.Hour)
	if err != nil {
		t.Fatal(err)
	}
	seedIdentityAuthorities(t, db, bearer, now)

	securityRepository := persistence.NewRuntimeSecurityRepository(db)
	if err := securityRepository.ActivateProjection(context.Background(), 0, persistence.RuntimeSecurityProjection{
		OrgID: identityTestOrg, RuntimeAudience: identityTestAudience, SecurityEpoch: 1,
		Bindings: []persistence.RuntimeSecurityBinding{{
			OrgID: identityTestOrg, AppInstallationID: identityTestInstallation,
			CallerWorkloadID: identityTestWorkload, Capabilities: []string{recordsemantic.CapabilityName}, Active: true,
		}},
	}); err != nil {
		t.Fatal(err)
	}
	if err := securityRepository.IssueLease(context.Background(), identityTestOrg, identityTestReplica, 1, now.Add(time.Minute)); err != nil {
		t.Fatal(err)
	}

	authService := featureauth.NewAuthService(persistence.NewUserRepository(db), persistence.NewIndependentIdentitySessionRepository(db), nil)
	binding := func(_ context.Context, caller runtimegrpc.VerifiedCaller) (runtimeapi.RuntimeCallerBinding, bool, error) {
		return runtimeapi.RuntimeCallerBinding{CallerWorkloadID: identityTestWorkload}, caller == identityTestCallerHandle, nil
	}
	securityState, err := runtimeidentity.NewSecurityState(context.Background(), securityRepository, identityTestReplica, time.Now)
	if err != nil {
		t.Fatal(err)
	}
	privateKey := ed25519.NewKeyFromSeed(make([]byte, ed25519.SeedSize))
	keys := runtimeidentity.SigningKeySnapshot{Active: runtimeidentity.ActiveSigningKey{SigningKeyID: "identity-real-current", PrivateKey: privateKey}}
	verifier, err := keys.Verifier(now, []string{recordsemantic.CapabilityName})
	if err != nil {
		t.Fatal(err)
	}
	securityAdmission, err := runtimeapi.NewSecurityAdmission(runtimeapi.SecurityAdmissionConfig{
		Verifier: verifier, Bindings: binding, Sessions: runtimeidentity.SessionAuthority(authService), State: securityState,
	})
	if err != nil {
		t.Fatal(err)
	}
	var signingCalls atomic.Int64
	issuer, err := runtimeidentity.NewIssuer(runtimeidentity.IssuerConfig{
		Bindings: binding, PublicSessions: runtimeidentity.PublicSessionAuthorityFrom(authService),
		SessionCapabilities: func(_ context.Context, state runtimeapi.RuntimeSessionState) ([]string, error) {
			return strings.Fields(state.User.Scope), nil
		},
		Security: runtimeidentity.IssuanceSecuritySourceFrom(securityRepository, identityTestReplica, time.Now),
		SigningKeys: func(context.Context, time.Time) (runtimeidentity.SigningKeySnapshot, error) {
			signingCalls.Add(1)
			return keys, nil
		},
		Now: time.Now,
	})
	if err != nil {
		t.Fatal(err)
	}

	reader := &identityReader{}
	process, err := runtimeapi.Assemble(runtimeapi.ProcessConfig{
		Version: "f1-i-integration", AdminAddress: "127.0.0.1:0", GRPCAddress: "127.0.0.1:0",
		AdmissionLimit: runtimeapi.DefaultAdmissionLimit, ServingEnabled: true,
		WorkloadAuthority: func(context.Context, string) (runtimegrpc.VerifiedCaller, error) {
			return identityTestCallerHandle, nil
		},
		SecurityAdmission: securityAdmission, IdentityExchange: issuer.Exchange, IdentityAttemptCap: time.Second,
		Receipts: runtimeapi.ReceiptSourceFunc(runtimeapi.BootstrapReceipts),
	}, reader)
	if err != nil {
		t.Fatal(err)
	}
	if err := process.Start(); err != nil {
		t.Fatal(err)
	}
	defer func() {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		if err := process.Stop(ctx); err != nil {
			t.Error(err)
		}
	}()

	connection, err := grpc.NewClient("passthrough:///"+process.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		t.Fatal(err)
	}
	defer connection.Close()
	identityClient, err := runtimegrpc.NewIdentityExchangeClient(runtimev1.NewIdentityContextServiceClient(connection).Exchange)
	if err != nil {
		t.Fatal(err)
	}
	exchangeCtx, exchangeCancel := context.WithTimeout(context.Background(), time.Second)
	defer exchangeCancel()
	delegation, err := identityClient.Exchange(exchangeCtx, bearer, identitysemantic.Request{
		AppInstallationID: identityTestInstallation, RequestedCapabilities: []string{recordsemantic.CapabilityName},
	})
	if err != nil {
		t.Fatal(err)
	}
	claims, err := verifier.Verify(delegation.OpaqueDelegation, time.Now(), delegationv1.ExpectedBindings{
		OrgID: identityTestOrg, CallerWorkloadID: identityTestWorkload,
		AppInstallationID: identityTestInstallation, RuntimeAudience: identityTestAudience,
		RequiredCapability: recordsemantic.CapabilityName,
	})
	if err != nil {
		t.Fatal(err)
	}
	if claims.PrincipalID != identityTestPrincipal || claims.SessionID != identityTestSession ||
		claims.AuthEpoch != 1 || claims.SecurityEpoch != 1 {
		t.Fatalf("issued claims = %#v", claims)
	}

	recordsClient, err := runtimegrpc.NewSelectedRootClient(runtimev1.NewRecordsServiceClient(connection).ReadSelectedRoot)
	if err != nil {
		t.Fatal(err)
	}
	readCtx, readCancel := context.WithTimeout(context.Background(), time.Second)
	defer readCancel()
	readCtx = metadata.AppendToOutgoingContext(readCtx, envelope.RuntimeDelegationMetadataKey, delegation.OpaqueDelegation)
	response, err := recordsClient.ReadSelectedRoot(readCtx, recordsemantic.Request{
		ObjectID: "Issue", RecordID: "ISS-F1-I", SelectedRootFields: []string{"Subject"},
	})
	if err != nil {
		t.Fatal(err)
	}
	if response.RecordID != "ISS-F1-I" || reader.calls.Load() != 1 || reader.userID != identityTestPrincipal {
		t.Fatalf("response=%#v calls=%d user=%q", response, reader.calls.Load(), reader.userID)
	}
	if signingCalls.Load() != 1 {
		t.Fatalf("signing calls after valid exchange = %d", signingCalls.Load())
	}

	// AuthEpoch is never caller-authored. Advancing the durable owner makes the
	// first delegation unusable, and a subsequent exchange must bind the new
	// epoch rather than minting another stale token from the still-current
	// public bearer.
	if _, err := db.Exec(`UPDATE _System_Session SET AuthEpoch = 2 WHERE Id = ?`, identityTestSession); err != nil {
		t.Fatal(err)
	}
	staleCtx, staleCancel := context.WithTimeout(context.Background(), time.Second)
	staleCtx = metadata.AppendToOutgoingContext(staleCtx, envelope.RuntimeDelegationMetadataKey, delegation.OpaqueDelegation)
	_, staleErr := recordsClient.ReadSelectedRoot(staleCtx, recordsemantic.Request{
		ObjectID: "Issue", RecordID: "ISS-F1-I", SelectedRootFields: []string{"Subject"},
	})
	staleCancel()
	var staleFailure *recordsemantic.Failure
	if !errors.As(staleErr, &staleFailure) || staleFailure.Category != recordsemantic.FailureUnauthenticated || reader.calls.Load() != 1 {
		t.Fatalf("old AuthEpoch failure=%#v err=%v records=%d", staleFailure, staleErr, reader.calls.Load())
	}
	freshCtx, freshCancel := context.WithTimeout(context.Background(), time.Second)
	freshDelegation, err := identityClient.Exchange(freshCtx, bearer, identitysemantic.Request{
		AppInstallationID: identityTestInstallation, RequestedCapabilities: []string{recordsemantic.CapabilityName},
	})
	freshCancel()
	if err != nil {
		t.Fatal(err)
	}
	freshClaims, err := verifier.Verify(freshDelegation.OpaqueDelegation, time.Now(), delegationv1.ExpectedBindings{
		OrgID: identityTestOrg, CallerWorkloadID: identityTestWorkload,
		AppInstallationID: identityTestInstallation, RuntimeAudience: identityTestAudience,
		RequiredCapability: recordsemantic.CapabilityName,
	})
	if err != nil || freshClaims.AuthEpoch != 2 || signingCalls.Load() != 2 {
		t.Fatalf("fresh AuthEpoch claims=%#v signing=%d err=%v", freshClaims, signingCalls.Load(), err)
	}

	expectFailure := func(name, candidateBearer string, request identitysemantic.Request, want identitysemantic.FailureCategory) {
		t.Helper()
		before := signingCalls.Load()
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		_, callErr := identityClient.Exchange(ctx, candidateBearer, request)
		var failure *identitysemantic.Failure
		if !errors.As(callErr, &failure) || failure.Category != want {
			t.Fatalf("%s: failure=%#v err=%v, want %s", name, failure, callErr, want)
		}
		if signingCalls.Load() != before {
			t.Fatalf("%s reached signing provider", name)
		}
		if strings.Contains(callErr.Error(), candidateBearer) {
			t.Fatalf("%s disclosed bearer in error: %v", name, callErr)
		}
	}
	validRequest := identitysemantic.Request{AppInstallationID: identityTestInstallation, RequestedCapabilities: []string{recordsemantic.CapabilityName}}
	expectFailure("malformed bearer", "not-a-jwt", validRequest, identitysemantic.FailureUnauthenticated)
	expectFailure("cross-org installation", bearer, identitysemantic.Request{AppInstallationID: "installation-other-org", RequestedCapabilities: []string{recordsemantic.CapabilityName}}, identitysemantic.FailureForbidden)
	expectFailure("ungranted capability", bearer, identitysemantic.Request{AppInstallationID: identityTestInstallation, RequestedCapabilities: []string{"runtime.records.other.v1"}}, identitysemantic.FailureForbidden)

	if _, err := db.Exec(`UPDATE _System_Session SET IsRevoked = 1 WHERE Id = ?`, identityTestSession); err != nil {
		t.Fatal(err)
	}
	expectFailure("revoked session", bearer, validRequest, identitysemantic.FailureUnauthenticated)
	if _, err := db.Exec(`UPDATE _System_Session SET IsRevoked = 0 WHERE Id = ?`, identityTestSession); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(`UPDATE User SET IsActive = 0 WHERE Id = ?`, identityTestPrincipal); err != nil {
		t.Fatal(err)
	}
	expectFailure("inactive user", bearer, validRequest, identitysemantic.FailureUnauthenticated)
	if _, err := db.Exec(`UPDATE User SET IsActive = 1 WHERE Id = ?`, identityTestPrincipal); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(`UPDATE _System_ConnectedApp SET IsActive = 0 WHERE Id = ?`, identityTestConnectedApp); err != nil {
		t.Fatal(err)
	}
	expectFailure("disabled connected app", bearer, validRequest, identitysemantic.FailureUnauthenticated)
	if _, err := db.Exec(`UPDATE _System_ConnectedApp SET IsActive = 1 WHERE Id = ?`, identityTestConnectedApp); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(`UPDATE _System_RuntimeSecurityLease SET SecurityEpoch = 2 WHERE RuntimeReplicaId = ?`, identityTestReplica); err != nil {
		t.Fatal(err)
	}
	expectFailure("stale security epoch", bearer, validRequest, identitysemantic.FailureStaleState)
	if _, err := db.Exec(`UPDATE _System_RuntimeSecurityLease SET SecurityEpoch = 1, ExpiresAt = ? WHERE RuntimeReplicaId = ?`, time.Unix(1, 0), identityTestReplica); err != nil {
		t.Fatal(err)
	}
	expectFailure("expired security lease", bearer, validRequest, identitysemantic.FailureStaleState)
}

type identityReader struct {
	calls  atomic.Int64
	userID string
}

func (r *identityReader) ReadSelectedRecord(ctx context.Context, _, recordID string, _ []string, user *models.UserSession) (*records.SelectedRootRecord, error) {
	if bearer, ok := runtimegrpc.EndUserBearer(ctx); ok || bearer != "" {
		return nil, fmt.Errorf("raw bearer reached records")
	}
	if values, _ := metadata.FromIncomingContext(ctx); len(values.Get(runtimegrpc.EndUserAuthorizationMetadataKey)) != 0 {
		return nil, fmt.Errorf("authorization metadata reached records")
	}
	r.calls.Add(1)
	if user != nil {
		r.userID = user.ID
	}
	value := "issued delegation accepted"
	return &records.SelectedRootRecord{RecordID: recordID, Fields: []records.SelectedRootField{{Name: "Subject", Kind: records.SelectedRootString, String: &value}}}, nil
}

func (*identityReader) EvaluateRecordAccess(context.Context, *string, []string, *models.UserSession) (map[string]contract.RecordAccess, error) {
	return map[string]contract.RecordAccess{}, nil
}

func openIdentityScratchDB(t *testing.T) (*sql.DB, func()) {
	t.Helper()
	config := mysql.NewConfig()
	config.Net = "tcp"
	config.Addr = net.JoinHostPort(os.Getenv("TIDB_HOST"), identityEnv("TIDB_PORT", "4000"))
	config.User = identityEnv("TIDB_USER", "root")
	config.Passwd = os.Getenv("TIDB_PASSWORD")
	config.ParseTime = true
	admin, err := sql.Open("mysql", config.FormatDSN())
	if err != nil {
		t.Fatal(err)
	}
	name := fmt.Sprintf("nexuscrm_f1_i_%d", time.Now().UnixNano())
	if _, err := admin.Exec("CREATE DATABASE `" + name + "`"); err != nil {
		admin.Close()
		t.Fatal(err)
	}
	config.DBName = name
	db, err := sql.Open("mysql", config.FormatDSN())
	if err != nil {
		t.Fatal(err)
	}
	if err := db.Ping(); err != nil {
		t.Fatal(err)
	}
	return db, func() {
		_ = db.Close()
		_, _ = admin.Exec("DROP DATABASE IF EXISTS `" + name + "`")
		_ = admin.Close()
	}
}

func createIdentityAuthorityTables(t *testing.T, db *sql.DB) {
	t.Helper()
	for _, statement := range []string{
		`CREATE TABLE User (Id VARCHAR(255) PRIMARY KEY, Username VARCHAR(255) NOT NULL, Email VARCHAR(255) NOT NULL, ProfileId VARCHAR(255) NOT NULL, FirstName VARCHAR(255) NULL, LastName VARCHAR(255) NULL, IsActive BOOL NOT NULL, IsDeleted BOOL NOT NULL DEFAULT 0, FailedLoginAttempts INT NOT NULL DEFAULT 0, LockedUntil DATETIME NULL, UserRoleId VARCHAR(255) NULL)`,
		`CREATE TABLE _System_Session (Id VARCHAR(255) PRIMARY KEY, UserId VARCHAR(255) NOT NULL, Token VARBINARY(3000) NOT NULL, CurrentBearerToken VARBINARY(3000) NULL, ExpiresAt DATETIME NOT NULL, IpAddress VARCHAR(255), UserAgent VARCHAR(255), IsRevoked BOOL NOT NULL, AuthEpoch DECIMAL(20,0) NOT NULL, ControlProofHash CHAR(64) NULL, LastActivity DATETIME NOT NULL, Scope TEXT NULL, ConnectedAppId VARCHAR(255) NULL, GrantType VARCHAR(50) NULL, CreatedDate DATETIME NOT NULL, LastModifiedDate DATETIME NOT NULL)`,
		`CREATE TABLE _System_ConnectedApp (Id VARCHAR(255) PRIMARY KEY, IsActive BOOL NOT NULL)`,
		`CREATE TABLE _System_ConnectedAppDeclaration (ConnectedAppId VARCHAR(255), InstalledPackName VARCHAR(255), IsDeleted BOOL NOT NULL DEFAULT 0)`,
		`CREATE TABLE _System_OAuthDynamicClientRegistration (ConnectedAppId VARCHAR(255), ScopeOwnerPack VARCHAR(255), IsDeleted BOOL NOT NULL DEFAULT 0)`,
		`CREATE TABLE _System_PermissionStateVersion (SingletonKey TINYINT PRIMARY KEY, Lineage BINARY(16) NOT NULL, Revision DECIMAL(20,0) NOT NULL, EnforcementMode VARCHAR(16) NOT NULL, CreatedDate DATETIME(6) NOT NULL, LastModifiedDate DATETIME(6) NOT NULL)`,
		`CREATE TABLE _System_RuntimeSecurityState (OrgId VARCHAR(255) PRIMARY KEY, RuntimeAudience VARCHAR(255) NOT NULL, SecurityEpoch DECIMAL(20,0) NOT NULL, CreatedDate DATETIME(6) NOT NULL, LastModifiedDate DATETIME(6) NOT NULL)`,
		`CREATE TABLE _System_RuntimeSecurityBinding (Id VARCHAR(255) PRIMARY KEY, OrgId VARCHAR(255) NOT NULL, AppInstallationId VARCHAR(255) NOT NULL, CallerWorkloadId VARCHAR(1024) NOT NULL, CallerWorkloadDigest CHAR(64) NOT NULL, Capabilities JSON NOT NULL, IsActive BOOL NOT NULL, CreatedDate DATETIME(6) NOT NULL, LastModifiedDate DATETIME(6) NOT NULL, INDEX idx_installation (OrgId, AppInstallationId, CallerWorkloadDigest))`,
		`CREATE TABLE _System_RuntimeSecurityLease (RuntimeReplicaId VARCHAR(255) PRIMARY KEY, OrgId VARCHAR(255) NOT NULL, SecurityEpoch DECIMAL(20,0) NOT NULL, ExpiresAt DATETIME(6) NOT NULL, CreatedDate DATETIME(6) NOT NULL, LastModifiedDate DATETIME(6) NOT NULL)`,
		`CREATE TABLE _System_RuntimeDelegationRevocation (Id VARCHAR(255) PRIMARY KEY, OrgId VARCHAR(255) NOT NULL, DelegationId VARCHAR(255) NOT NULL, ExpiresAt DATETIME(6) NOT NULL, CreatedDate DATETIME(6) NOT NULL, LastModifiedDate DATETIME(6) NOT NULL, UNIQUE KEY ux_org_delegation (OrgId, DelegationId))`,
	} {
		if _, err := db.Exec(statement); err != nil {
			t.Fatal(err)
		}
	}
}

func seedIdentityAuthorities(t *testing.T, db *sql.DB, bearer string, now time.Time) {
	t.Helper()
	if _, err := db.Exec(`INSERT INTO User (Id, Username, Email, ProfileId, FirstName, LastName, IsActive, FailedLoginAttempts, UserRoleId) VALUES (?, 'identity-user', 'identity@example.test', ?, 'Identity', 'User', 1, 0, ?)`, identityTestPrincipal, identityTestProfile, identityTestRole); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(`INSERT INTO _System_ConnectedApp (Id, IsActive) VALUES (?, 1)`, identityTestConnectedApp); err != nil {
		t.Fatal(err)
	}
	// The independently serving Runtime has no L1 product hooks. This source
	// attribution must therefore be validated as durable Connected App state,
	// while AppInstallation/workload/capability authority remains on the
	// separate Runtime security path exercised by this test.
	if _, err := db.Exec(`INSERT INTO _System_ConnectedAppDeclaration (ConnectedAppId, InstalledPackName, IsDeleted) VALUES (?, 'neutral-app-pack', 0)`, identityTestConnectedApp); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(`INSERT INTO _System_Session (Id, UserId, Token, ExpiresAt, IpAddress, UserAgent, IsRevoked, AuthEpoch, LastActivity, Scope, ConnectedAppId, GrantType, CreatedDate, LastModifiedDate) VALUES (?, ?, ?, ?, '127.0.0.1', 'f1-i', 0, 1, ?, ?, ?, 'authorization_code', ?, ?)`, identityTestSession, identityTestPrincipal, bearer, now.Add(time.Hour), now, recordsemantic.CapabilityName, identityTestConnectedApp, now, now); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(
		`INSERT INTO _System_PermissionStateVersion (SingletonKey, Lineage, Revision, EnforcementMode, CreatedDate, LastModifiedDate) VALUES (1, ?, 1, 'dark', ?, ?)`,
		[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
		now,
		now,
	); err != nil {
		t.Fatal(err)
	}
}

func identityEnv(key, fallback string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return fallback
}
