package permissions

import (
	"context"
	"database/sql"
	"errors"
	"strings"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// systemProfileOwnedPermissionSetImmutableMessage is the admin-surface
// rejection message for grant mutations on a system-profile-owned permission
// set. It states the same invariant as the sobject-surface guard: both
// surfaces reject writes to system-managed permission resources.
const systemProfileOwnedPermissionSetImmutableMessage = "System-profile-owned permission resources are immutable."

// IsSystemProfileOwnedPermissionSet reports whether the PermissionSet with the
// given ID is owned by a system profile — IsOwnedByProfile=true and the owning
// profile has IsSystem=true. It is the admin-surface counterpart of the
// records-package metadata-driven owner-resolution guard: the permission-admin
// grant surface (AdminService.SaveSystemGrants / SaveRecordTypeGrants /
// SaveCustomPermissionGrants) uses it to reject mutations of system-managed
// resources before any repository write.
//
// The sobject and admin surfaces currently resolve system-profile ownership
// through their own read paths, because the permissions package cannot import
// the records package to reuse its helper (the records package already imports
// permissions). They consolidate onto a single shared L0 owner-resolution
// primitive in Step C of #2249.
func (ps *PermissionService) IsSystemProfileOwnedPermissionSet(ctx context.Context, permSetID string) (bool, error) {
	permSetID = strings.TrimSpace(permSetID)
	if permSetID == "" {
		return false, nil
	}
	permSet, err := ps.repo.GetRecordByID(ctx, constants.TablePermissionSet, permSetID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return false, nil
		}
		return false, err
	}
	if !sobjectFieldTruthy(permSet, constants.FieldPermissionSet_IsOwnedByProfile) {
		return false, nil
	}
	profileID := strings.TrimSpace(sobjectStringField(permSet, constants.FieldPermissionSet_ProfileID))
	if profileID == "" {
		return false, nil
	}
	profile, err := ps.repo.GetRecordByID(ctx, constants.TableProfile, profileID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return false, nil
		}
		return false, err
	}
	return sobjectFieldTruthy(profile, constants.FieldProfile_IsSystem), nil
}

// sobjectFieldTruthy reports whether a boolean-ish SObject field is truthy. The
// persistence layer stores guard booleans across bool/int/string encodings, so
// this mirrors the records-package isGuardFieldTruthy helper.
func sobjectFieldTruthy(record models.SObject, field string) bool {
	if record == nil {
		return false
	}
	switch v := record[field].(type) {
	case nil:
		return false
	case bool:
		return v
	case int:
		return v != 0
	case int32:
		return v != 0
	case int64:
		return v != 0
	case string:
		return v == "1" || strings.EqualFold(v, "true")
	default:
		return false
	}
}

func sobjectStringField(record models.SObject, field string) string {
	if record == nil {
		return ""
	}
	if s, ok := record[field].(string); ok {
		return s
	}
	return ""
}
