package records

import (
	"context"
	"fmt"

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
)

const profilePermissionCarrierLifecycleCapability = "platform.profile-permission-carrier"

func (ps *PersistenceService) registerProfileLifecycleHooks() {
	if ps == nil || ps.permissions == nil {
		return
	}
	if err := ps.RegisterRecordLifecycleHook(contract.RecordLifecycleRegistration{
		Capability:                     profilePermissionCarrierLifecycleCapability,
		Phase:                          recordLifecycleAfterCreateInTx,
		RequiresPessimisticTransaction: true,
		Hook:                           ps.profileAfterCreateInTx,
	}); err != nil {
		panic(err)
	}
	if err := ps.RegisterRecordLifecycleHook(contract.RecordLifecycleRegistration{
		Capability:                     profilePermissionCarrierLifecycleCapability,
		Phase:                          recordLifecycleAfterUpdateInTx,
		RequiresPessimisticTransaction: true,
		Hook:                           ps.profileAfterUpdateInTx,
	}); err != nil {
		panic(err)
	}
	if err := ps.RegisterRecordLifecycleHook(contract.RecordLifecycleRegistration{
		Capability:                     profilePermissionCarrierLifecycleCapability,
		Phase:                          recordLifecycleAfterDeleteInTx,
		RequiresPessimisticTransaction: true,
		Hook:                           ps.profileAfterDeleteInTx,
	}); err != nil {
		panic(err)
	}
}

func (ps *PersistenceService) profileAfterCreateInTx(ctx context.Context, event contract.RecordLifecycleEvent) error {
	if ps == nil || ps.permissions == nil {
		return nil
	}
	profileID := event.RecordID
	if profileID == "" {
		profileID, _ = event.Record[constants.FieldID].(string)
	}
	if profileID == "" {
		return appErrors.NewInternalError("missing Profile Id after insert", fmt.Errorf("insert result did not include %s", constants.FieldID))
	}

	name, _ := event.Record[constants.FieldName].(string)
	permSetID, err := ps.permissions.EnsureProfilePermissionSet(ctx, profileID, name)
	if err != nil {
		return err
	}

	var schemas []*models.ObjectMetadata
	if ps.metadata != nil {
		schemas = ps.metadata.GetSchemas(ctx)
	}
	if err := ps.permissions.SeedPermissionsForProfile(ctx, profileID, permSetID, schemas); err != nil {
		return err
	}
	if err := ps.permissions.EnsureNavigationVisibilityForProfile(ctx, profileID); err != nil {
		return err
	}
	ps.invalidateProfilePermissionCacheAfterCommit(ctx)
	return nil
}

func (ps *PersistenceService) profileAfterUpdateInTx(ctx context.Context, event contract.RecordLifecycleEvent) error {
	if ps == nil || ps.permissions == nil {
		return nil
	}
	profileID := event.RecordID
	if profileID == "" {
		profileID, _ = event.Record[constants.FieldID].(string)
	}
	if profileID == "" {
		return appErrors.NewInternalError("missing Profile Id after update", fmt.Errorf("update result did not include %s", constants.FieldID))
	}
	if isGuardFieldTruthy(event.OldRecord[constants.FieldIsDeleted]) && !isGuardFieldTruthy(event.Record[constants.FieldIsDeleted]) {
		// Restore the same canonical carrier and its existing grants. Do not
		// reseed permissions: restoration must not replace administrator data.
		name, _ := event.Record[constants.FieldName].(string)
		if _, err := ps.permissions.EnsureProfilePermissionSet(ctx, profileID, name); err != nil {
			return err
		}
		ps.invalidateProfilePermissionCacheAfterCommit(ctx)
	}
	return ps.permissions.EnsureNavigationVisibilityForProfile(ctx, profileID)
}

func (ps *PersistenceService) profileAfterDeleteInTx(ctx context.Context, event contract.RecordLifecycleEvent) error {
	if ps == nil || ps.permissions == nil {
		return nil
	}
	profileID := event.RecordID
	if profileID == "" {
		profileID, _ = event.OldRecord[constants.FieldID].(string)
	}
	if profileID == "" {
		return appErrors.NewInternalError("missing Profile Id after delete", fmt.Errorf("delete event did not include %s", constants.FieldID))
	}
	if err := ps.permissions.CleanupProfilePermissionSet(ctx, profileID); err != nil {
		return err
	}
	ps.invalidateProfilePermissionCacheAfterCommit(ctx)
	return nil
}

func (ps *PersistenceService) invalidateProfilePermissionCacheAfterCommit(ctx context.Context) {
	if ps == nil || ps.permissions == nil {
		return
	}
	invalidate := ps.permissions.InvalidateCache
	if ps.txManager != nil && ps.txManager.IsTransactionActive(ctx) {
		ports.RegisterAfterCommitHook(ctx, invalidate)
		return
	}
	invalidate()
}
