package rest

import (
	"context"
	"fmt"

	"github.com/nexuscrm/backend/internal/application/services"
	featuremetadata "github.com/nexuscrm/backend/internal/features/metadata"
	appErrors "github.com/nexuscrm/backend/pkg/errors"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/rs/zerolog/log"
)

func provisionCreatedSchemaPermissions(ctx context.Context, svc *services.ServiceManager, schema *models.ObjectMetadata) error {
	if svc == nil || svc.Permissions == nil || schema == nil {
		return nil
	}

	if err := svc.Permissions.SeedPermissionsForObject(ctx, schema.APIName); err != nil {
		log.Error().Err(err).Str("object", schema.APIName).
			Msg("schema created but permission provisioning failed; rolling schema back")
		if rollbackErr := svc.Metadata.DeleteSchema(ctx, schema.APIName); rollbackErr != nil {
			log.Error().Err(rollbackErr).Str("object", schema.APIName).
				Msg("schema permission rollback failed; object remains inconsistent")
		}
		return appErrors.NewInternalError("failed to provision permissions for new object", err)
	}

	svc.Permissions.InvalidateCache()
	return nil
}

func provisionCreatedFieldPermissions(ctx context.Context, svc *services.ServiceManager, objectAPIName string, fieldNames []string) error {
	if svc == nil || svc.Permissions == nil || len(fieldNames) == 0 {
		return nil
	}

	if err := svc.Permissions.SeedFieldPermissionsForFields(ctx, objectAPIName, fieldNames); err != nil {
		return err
	}

	svc.Permissions.InvalidateCache()
	return nil
}

func fieldNames(fields []*models.FieldMetadata) []string {
	result := make([]string, 0, len(fields))
	for _, field := range fields {
		if field == nil || field.APIName == "" {
			continue
		}
		result = append(result, field.APIName)
	}
	return result
}

func rollbackFieldNames(fields []*models.FieldMetadata) []string {
	result := make([]string, 0, len(fields)*2)
	seen := make(map[string]bool, len(fields)*2)

	for _, field := range fields {
		if field == nil || field.APIName == "" {
			continue
		}
		if !seen[field.APIName] {
			seen[field.APIName] = true
			result = append(result, field.APIName)
		}
		if field.Type == constants.FieldTypeReference && len(field.ReferenceTo) > 1 {
			typeField := featuremetadata.GetPolymorphicTypeColumnName(field.APIName)
			if !seen[typeField] {
				seen[typeField] = true
				result = append(result, typeField)
			}
		}
	}

	return result
}

func hasActiveFormulaFields(fields []*models.FieldMetadata) bool {
	for _, field := range fields {
		if field == nil {
			continue
		}
		if field.Type == constants.FieldTypeFormula && field.FormulaMode != nil && *field.FormulaMode == "active" {
			return true
		}
	}
	return false
}

func rollbackCreatedFields(ctx context.Context, svc *services.ServiceManager, objectAPIName string, fields []*models.FieldMetadata) {
	if svc == nil || svc.Metadata == nil {
		return
	}

	rollbackNames := rollbackFieldNames(fields)
	for _, fieldName := range rollbackNames {
		if rollbackErr := svc.Metadata.DeleteField(ctx, objectAPIName, fieldName); rollbackErr != nil {
			log.Error().Err(rollbackErr).Str("object", objectAPIName).Str("field", fieldName).
				Msg("field rollback failed; field remains inconsistent")
		}
	}

	if svc.Permissions != nil {
		if err := svc.Permissions.DeleteFieldPermissionsForFields(ctx, objectAPIName, rollbackNames); err != nil {
			log.Error().Err(err).Str("object", objectAPIName).
				Msg("field rollback failed to cleanup field permissions")
		} else {
			svc.Permissions.InvalidateCache()
		}
	}

	if hasActiveFormulaFields(fields) && svc.FormulaViewSvc != nil {
		if err := svc.FormulaViewSvc.RebuildView(ctx, objectAPIName); err != nil {
			log.Error().Err(err).Str("object", objectAPIName).
				Msg("field rollback failed to repair formula view")
		}
	}
}

func rebuildActiveFormulaView(ctx context.Context, svc *services.ServiceManager, objectAPIName string, fields []*models.FieldMetadata) error {
	if svc == nil || svc.FormulaViewSvc == nil {
		return nil
	}

	if !hasActiveFormulaFields(fields) {
		return nil
	}

	if err := svc.FormulaViewSvc.RebuildView(ctx, objectAPIName); err != nil {
		return fmt.Errorf("rebuild formula view for %s: %w", objectAPIName, err)
	}

	return nil
}

func finalizeCreatedFields(ctx context.Context, svc *services.ServiceManager, objectAPIName string, fields []*models.FieldMetadata) error {
	if err := rebuildActiveFormulaView(ctx, svc, objectAPIName, fields); err != nil {
		rollbackCreatedFields(ctx, svc, objectAPIName, fields)
		return appErrors.NewInternalError("failed to rebuild formula view for new field", err)
	}

	if err := provisionCreatedFieldPermissions(ctx, svc, objectAPIName, fieldNames(fields)); err != nil {
		rollbackCreatedFields(ctx, svc, objectAPIName, fields)
		return appErrors.NewInternalError("failed to provision permissions for new field", err)
	}

	return nil
}
