package access

import (
	"github.com/jackc/pgx/v5"

	postgreslib "github.com/codex-k8s/kodex/libs/go/postgres"
	"github.com/codex-k8s/kodex/services/internal/access-manager/internal/domain/types/entity"
	"github.com/codex-k8s/kodex/services/internal/access-manager/internal/domain/types/query"
)

func organizationArgs(organization entity.Organization) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":                     organization.ID,
		"kind":                   string(organization.Kind),
		"slug":                   organization.Slug,
		"display_name":           organization.DisplayName,
		"image_asset_ref":        organization.ImageAssetRef,
		"status":                 string(organization.Status),
		"parent_organization_id": postgreslib.NullableUUID(organization.ParentOrganizationID),
		"version":                organization.Version,
		"created_at":             organization.CreatedAt,
		"updated_at":             organization.UpdatedAt,
	}
}

func userArgs(user entity.User) pgx.NamedArgs {
	args := postgreslib.AddBaseArgs(pgx.NamedArgs{
		"primary_email":    user.PrimaryEmail,
		"display_name":     user.DisplayName,
		"avatar_asset_ref": user.AvatarAssetRef,
		"locale":           user.Locale,
	}, user.ID, user.Version, user.CreatedAt, user.UpdatedAt)
	args["status"] = string(user.Status)
	return args
}

func userUpdateArgs(user entity.User, previousVersion int64) pgx.NamedArgs {
	args := userArgs(user)
	args["previous_version"] = previousVersion
	return args
}

func userIdentityArgs(identity entity.UserIdentity) pgx.NamedArgs {
	args := pgx.NamedArgs{
		"id":             identity.ID,
		"user_id":        identity.UserID,
		"provider":       string(identity.Provider),
		"subject":        identity.Subject,
		"email_at_login": identity.EmailAtLogin,
	}
	args["last_login_at"] = postgreslib.NullableTime(identity.LastLoginAt)
	return args
}

func userIdentityLookupArgs(provider string, subject string) pgx.NamedArgs {
	return pgx.NamedArgs{"provider": provider, "subject": subject}
}

func commandIdentityArgs(identity query.CommandIdentity) pgx.NamedArgs {
	return pgx.NamedArgs{
		"command_id":      postgreslib.NullableCommandID(identity.CommandID),
		"idempotency_key": postgreslib.IdempotencyLookupKey(identity.CommandID, identity.IdempotencyKey),
	}
}

func commandResultArgs(result entity.CommandResult) pgx.NamedArgs {
	return pgx.NamedArgs{
		"key":             result.Key,
		"command_id":      postgreslib.NullableCommandID(result.CommandID),
		"idempotency_key": result.IdempotencyKey,
		"operation":       result.Operation,
		"aggregate_type":  result.AggregateType,
		"aggregate_id":    result.AggregateID,
		"created_at":      result.CreatedAt,
	}
}

func allowlistEntryArgs(entry entity.AllowlistEntry) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":              entry.ID,
		"match_type":      string(entry.MatchType),
		"value":           entry.Value,
		"organization_id": postgreslib.NullableUUID(entry.OrganizationID),
		"default_status":  string(entry.DefaultStatus),
		"status":          string(entry.Status),
		"version":         entry.Version,
		"created_at":      entry.CreatedAt,
		"updated_at":      entry.UpdatedAt,
	}
}

func allowlistEntryUpdateArgs(entry entity.AllowlistEntry, previousVersion int64) pgx.NamedArgs {
	args := allowlistEntryArgs(entry)
	args["previous_version"] = previousVersion
	return args
}

func allowlistLookupArgs(matchType string, value string) pgx.NamedArgs {
	return pgx.NamedArgs{"match_type": matchType, "value": value}
}

func groupArgs(group entity.Group) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":              group.ID,
		"scope_type":      string(group.ScopeType),
		"scope_id":        postgreslib.NullableUUID(group.ScopeID),
		"slug":            group.Slug,
		"display_name":    group.DisplayName,
		"parent_group_id": postgreslib.NullableUUID(group.ParentGroupID),
		"image_asset_ref": group.ImageAssetRef,
		"status":          string(group.Status),
		"version":         group.Version,
		"created_at":      group.CreatedAt,
		"updated_at":      group.UpdatedAt,
	}
}

func membershipArgs(membership entity.Membership) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":           membership.ID,
		"subject_type": string(membership.SubjectType),
		"subject_id":   membership.SubjectID,
		"target_type":  string(membership.TargetType),
		"target_id":    membership.TargetID,
		"role_hint":    membership.RoleHint,
		"status":       string(membership.Status),
		"source":       string(membership.Source),
		"version":      membership.Version,
		"created_at":   membership.CreatedAt,
		"updated_at":   membership.UpdatedAt,
	}
}

func externalProviderArgs(provider entity.ExternalProvider) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":             provider.ID,
		"slug":           provider.Slug,
		"provider_kind":  string(provider.ProviderKind),
		"display_name":   provider.DisplayName,
		"icon_asset_ref": provider.IconAssetRef,
		"status":         string(provider.Status),
		"version":        provider.Version,
		"created_at":     provider.CreatedAt,
		"updated_at":     provider.UpdatedAt,
	}
}

func externalProviderUpdateArgs(provider entity.ExternalProvider, previousVersion int64) pgx.NamedArgs {
	args := externalProviderArgs(provider)
	args["previous_version"] = previousVersion
	return args
}

func externalAccountArgs(account entity.ExternalAccount) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":                    account.ID,
		"external_provider_id":  account.ExternalProviderID,
		"account_type":          string(account.AccountType),
		"display_name":          account.DisplayName,
		"image_asset_ref":       account.ImageAssetRef,
		"owner_scope_type":      string(account.OwnerScopeType),
		"owner_scope_id":        account.OwnerScopeID,
		"status":                string(account.Status),
		"secret_binding_ref_id": postgreslib.NullableUUID(account.SecretBindingRefID),
		"version":               account.Version,
		"created_at":            account.CreatedAt,
		"updated_at":            account.UpdatedAt,
	}
}

func externalAccountUpdateArgs(account entity.ExternalAccount, previousVersion int64) pgx.NamedArgs {
	args := externalAccountArgs(account)
	args["previous_version"] = previousVersion
	return args
}

func externalAccountBindingArgs(binding entity.ExternalAccountBinding) pgx.NamedArgs {
	return withBaseArgs(binding.Base, pgx.NamedArgs{
		"external_account_id": binding.ExternalAccountID,
		"usage_scope_type":    string(binding.UsageScopeType),
		"usage_scope_id":      binding.UsageScopeID,
		"allowed_action_keys": binding.AllowedActionKeys,
		"status":              string(binding.Status),
	})
}

func externalAccountBindingUpdateArgs(binding entity.ExternalAccountBinding, previousVersion int64) pgx.NamedArgs {
	args := externalAccountBindingArgs(binding)
	args["previous_version"] = previousVersion
	return args
}

func secretBindingRefArgs(secret entity.SecretBindingRef) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":                secret.ID,
		"store_type":        string(secret.StoreType),
		"store_ref":         secret.StoreRef,
		"value_fingerprint": secret.ValueFingerprint,
		"rotated_at":        postgreslib.NullableTime(secret.RotatedAt),
		"version":           secret.Version,
		"created_at":        secret.CreatedAt,
		"updated_at":        secret.UpdatedAt,
	}
}

func packageInstallationSecretRefsFilterArgs(filter query.PackageInstallationSecretRefsFilter) pgx.NamedArgs {
	return pgx.NamedArgs{
		"package_installation_id": filter.PackageInstallationID,
		"installation_scope_type": filter.InstallationScope.Type,
		"installation_scope_id":   filter.InstallationScope.ID,
		"logical_keys":            filter.LogicalKeys,
	}
}

func accessActionArgs(action entity.AccessAction) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":            action.ID,
		"key":           action.Key,
		"display_name":  action.DisplayName,
		"description":   action.Description,
		"resource_type": action.ResourceType,
		"status":        string(action.Status),
		"version":       action.Version,
		"created_at":    action.CreatedAt,
		"updated_at":    action.UpdatedAt,
	}
}

func accessRuleArgs(rule entity.AccessRule) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":            rule.ID,
		"effect":        string(rule.Effect),
		"subject_type":  string(rule.SubjectType),
		"subject_id":    rule.SubjectID,
		"action_key":    rule.ActionKey,
		"resource_type": rule.ResourceType,
		"resource_id":   rule.ResourceID,
		"scope_type":    rule.ScopeType,
		"scope_id":      rule.ScopeID,
		"priority":      rule.Priority,
		"status":        string(rule.Status),
		"version":       rule.Version,
		"created_at":    rule.CreatedAt,
		"updated_at":    rule.UpdatedAt,
	}
}

func accessDecisionAuditArgs(audit entity.AccessDecisionAudit, requestContext []byte, explanation []byte) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":              audit.ID,
		"subject_type":    audit.Subject.Type,
		"subject_id":      audit.Subject.ID,
		"action_key":      audit.ActionKey,
		"resource_type":   audit.Resource.Type,
		"resource_id":     audit.Resource.ID,
		"scope_type":      audit.Scope.Type,
		"scope_id":        audit.Scope.ID,
		"request_context": string(requestContext),
		"decision":        string(audit.Decision),
		"reason_code":     audit.ReasonCode,
		"policy_version":  audit.PolicyVersion,
		"explanation":     string(explanation),
		"created_at":      audit.CreatedAt,
	}
}

func outboxEventArgs(event entity.OutboxEvent) pgx.NamedArgs {
	return pgx.NamedArgs{
		"id":             event.ID,
		"event_type":     event.EventType,
		"schema_version": event.SchemaVersion,
		"aggregate_type": event.AggregateType,
		"aggregate_id":   event.AggregateID,
		"payload":        string(event.Payload),
		"occurred_at":    event.OccurredAt,
		"published_at":   postgreslib.NullableTime(event.PublishedAt),
	}
}

func withBaseArgs(base entity.Base, args pgx.NamedArgs) pgx.NamedArgs {
	return postgreslib.AddBaseArgs(args, base.ID, base.Version, base.CreatedAt, base.UpdatedAt)
}
