import type { ActionDefinition } from "../../core/types.ts";

import { s } from "../../core/json-schema.ts";
import { defineProviderAction } from "../../core/provider-definition.ts";

const service = "clerk";

export type ClerkActionName =
  | "list_users"
  | "count_users"
  | "get_user"
  | "create_user"
  | "update_user"
  | "update_user_metadata"
  | "delete_user"
  | "ban_user"
  | "unban_user"
  | "lock_user"
  | "unlock_user";

const clerkUserIdSchema = s.nonEmptyString("The Clerk user ID.");
const clerkEmailSchema = s.email("An email address attached to the Clerk user.");
const clerkPhoneNumberSchema = s.nonEmptyString("A phone number attached to the Clerk user.");
const clerkUsernameSchema = s.nonEmptyString("The username attached to the Clerk user.");
const clerkPasswordSchema = s.nonEmptyString("The user's password.");
const clerkMetadataSchema = s.looseObject("Arbitrary Clerk metadata stored on the user.");
const clerkTimestampSchema = s.nullableInteger("Unix timestamp in milliseconds as returned by Clerk.");
const clerkStringListSchema = s.array(
  "A list of string values used by Clerk filters.",
  s.nonEmptyString("A string filter value."),
);
const clerkIdentificationSchema = s.object(
  "A Clerk identification object such as an email address or phone number.",
  {
    id: s.string("The identification ID."),
    object: s.string("The identification object type."),
    email_address: s.nullableString("The email address value when present."),
    phone_number: s.nullableString("The phone number value when present."),
    reserved: s.boolean("Whether the identifier is reserved."),
    verification: s.unknown("The verification state returned by Clerk."),
    linked_to: s.array(
      "External accounts linked to this identification.",
      s.unknown("A linked identification reference returned by Clerk."),
    ),
    created_at: clerkTimestampSchema,
    updated_at: clerkTimestampSchema,
  },
  {
    optional: [
      "id",
      "object",
      "email_address",
      "phone_number",
      "reserved",
      "verification",
      "linked_to",
      "created_at",
      "updated_at",
    ],
    additionalProperties: true,
  },
);
const clerkExternalAccountSchema = s.object(
  "A Clerk external account attached to the user.",
  {
    id: s.string("The external account ID."),
    object: s.string("The external account object type."),
    provider: s.string("The external provider identifier."),
    identification_id: s.string("The related identification ID."),
    provider_user_id: s.string("The user ID reported by the external provider."),
    email_address: s.nullableString("The provider email address when present."),
    first_name: s.nullableString("The provider first name when present."),
    last_name: s.nullableString("The provider last name when present."),
    image_url: s.nullableString("The provider image URL when present."),
    username: s.nullableString("The provider username when present."),
    public_metadata: clerkMetadataSchema,
    verification: s.unknown("The external account verification state returned by Clerk."),
  },
  {
    optional: [
      "id",
      "object",
      "provider",
      "identification_id",
      "provider_user_id",
      "email_address",
      "first_name",
      "last_name",
      "image_url",
      "username",
      "public_metadata",
      "verification",
    ],
    additionalProperties: true,
  },
);
const clerkUserSchema = s.object(
  "A Clerk user object.",
  {
    id: clerkUserIdSchema,
    object: s.string("The Clerk object type."),
    username: s.nullableString("The user's username when present."),
    first_name: s.nullableString("The user's first name when present."),
    last_name: s.nullableString("The user's last name when present."),
    image_url: s.nullableString("The user's image URL when present."),
    has_image: s.boolean("Whether the user has an image."),
    primary_email_address_id: s.nullableString("The primary email address ID."),
    primary_phone_number_id: s.nullableString("The primary phone number ID."),
    primary_web3_wallet_id: s.nullableString("The primary Web3 wallet ID."),
    password_enabled: s.boolean("Whether password authentication is enabled."),
    two_factor_enabled: s.boolean("Whether two-factor authentication is enabled."),
    totp_enabled: s.boolean("Whether TOTP is enabled."),
    backup_code_enabled: s.boolean("Whether backup codes are enabled."),
    email_addresses: s.array("Email addresses attached to the user.", clerkIdentificationSchema),
    phone_numbers: s.array("Phone numbers attached to the user.", clerkIdentificationSchema),
    web3_wallets: s.array("Web3 wallets attached to the user.", clerkIdentificationSchema),
    external_accounts: s.array("External accounts attached to the user.", clerkExternalAccountSchema),
    public_metadata: clerkMetadataSchema,
    private_metadata: clerkMetadataSchema,
    unsafe_metadata: clerkMetadataSchema,
    banned: s.boolean("Whether the user is banned."),
    locked: s.boolean("Whether the user is locked."),
    lockout_expires_in_seconds: s.nullableInteger("Seconds until lockout expires when present."),
    verification_attempts_remaining: s.nullableInteger("Verification attempts remaining when present."),
    last_sign_in_at: clerkTimestampSchema,
    created_at: clerkTimestampSchema,
    updated_at: clerkTimestampSchema,
    delete_self_enabled: s.boolean("Whether the user can delete their own account."),
    create_organization_enabled: s.boolean("Whether the user can create organizations."),
    create_organizations_limit: s.nullableInteger("The organization creation limit when present."),
  },
  {
    optional: [
      "id",
      "object",
      "username",
      "first_name",
      "last_name",
      "image_url",
      "has_image",
      "primary_email_address_id",
      "primary_phone_number_id",
      "primary_web3_wallet_id",
      "password_enabled",
      "two_factor_enabled",
      "totp_enabled",
      "backup_code_enabled",
      "email_addresses",
      "phone_numbers",
      "web3_wallets",
      "external_accounts",
      "public_metadata",
      "private_metadata",
      "unsafe_metadata",
      "banned",
      "locked",
      "lockout_expires_in_seconds",
      "verification_attempts_remaining",
      "last_sign_in_at",
      "created_at",
      "updated_at",
      "delete_self_enabled",
      "create_organization_enabled",
      "create_organizations_limit",
    ],
    additionalProperties: true,
  },
);
const clerkDeletedObjectSchema = s.object(
  "A Clerk deleted object response.",
  {
    id: s.string("The deleted object ID."),
    object: s.string("The deleted object type."),
    deleted: s.boolean("Whether the object was deleted."),
  },
  { optional: ["object"] },
);
const userFilterFields = {
  email_address: clerkStringListSchema,
  phone_number: clerkStringListSchema,
  username: clerkStringListSchema,
  user_id: clerkStringListSchema,
  external_id: clerkStringListSchema,
  query: s.nonEmptyString("A search query for Clerk users."),
};
const createUserInputSchema = s.object(
  "Input for creating a Clerk user.",
  {
    email_address: s.array("Email addresses to add to the new user.", clerkEmailSchema),
    phone_number: s.array("Phone numbers to add to the new user.", clerkPhoneNumberSchema),
    web3_wallet: s.array("Web3 wallet addresses to add to the new user.", s.nonEmptyString("A Web3 wallet address.")),
    external_id: s.nonEmptyString("An external ID for the user."),
    first_name: s.nonEmptyString("The user's first name."),
    last_name: s.nonEmptyString("The user's last name."),
    username: clerkUsernameSchema,
    password: clerkPasswordSchema,
    password_digest: s.nonEmptyString("A password digest for the user."),
    password_hasher: s.nonEmptyString("The hashing algorithm used for password_digest."),
    skip_password_checks: s.boolean("Whether Clerk should skip password validation checks."),
    skip_password_requirement: s.boolean("Whether Clerk should allow creating the user without a password."),
    totp_secret: s.nonEmptyString("The TOTP secret to add to the user."),
    backup_codes: s.array("Backup codes to add to the user.", s.nonEmptyString("A backup code.")),
    public_metadata: clerkMetadataSchema,
    private_metadata: clerkMetadataSchema,
    unsafe_metadata: clerkMetadataSchema,
    created_at: s.nonEmptyString("The RFC3339 datetime to set as the user's creation time."),
  },
  {
    optional: [
      "email_address",
      "phone_number",
      "web3_wallet",
      "external_id",
      "first_name",
      "last_name",
      "username",
      "password",
      "password_digest",
      "password_hasher",
      "skip_password_checks",
      "skip_password_requirement",
      "totp_secret",
      "backup_codes",
      "public_metadata",
      "private_metadata",
      "unsafe_metadata",
      "created_at",
    ],
  },
);
const updateUserInputSchema = s.object(
  "Input for updating a Clerk user.",
  {
    user_id: clerkUserIdSchema,
    first_name: s.nonEmptyString("The user's first name."),
    last_name: s.nonEmptyString("The user's last name."),
    primary_email_address_id: s.nonEmptyString("The primary email address ID."),
    primary_phone_number_id: s.nonEmptyString("The primary phone number ID."),
    primary_web3_wallet_id: s.nonEmptyString("The primary Web3 wallet ID."),
    username: clerkUsernameSchema,
    password: clerkPasswordSchema,
    password_digest: s.nonEmptyString("A password digest for the user."),
    password_hasher: s.nonEmptyString("The hashing algorithm used for password_digest."),
    skip_password_checks: s.boolean("Whether Clerk should skip password validation checks."),
    skip_password_requirement: s.boolean("Whether Clerk should allow the user without a password."),
    sign_out_of_other_sessions: s.boolean("Whether Clerk should sign the user out of other sessions."),
    totp_secret: s.nonEmptyString("The TOTP secret to add to the user."),
    backup_codes: s.array("Backup codes to add to the user.", s.nonEmptyString("A backup code.")),
    public_metadata: clerkMetadataSchema,
    private_metadata: clerkMetadataSchema,
    unsafe_metadata: clerkMetadataSchema,
  },
  {
    optional: [
      "first_name",
      "last_name",
      "primary_email_address_id",
      "primary_phone_number_id",
      "primary_web3_wallet_id",
      "username",
      "password",
      "password_digest",
      "password_hasher",
      "skip_password_checks",
      "skip_password_requirement",
      "sign_out_of_other_sessions",
      "totp_secret",
      "backup_codes",
      "public_metadata",
      "private_metadata",
      "unsafe_metadata",
    ],
  },
);
const userIdInputSchema = s.actionInput({ user_id: clerkUserIdSchema }, ["user_id"], "Input for a Clerk user ID.");
const userOutputSchema = s.actionOutput({ user: clerkUserSchema }, "Output returned with a Clerk user.");

export const clerkActions: ActionDefinition[] = [
  defineProviderAction(service, {
    name: "list_users",
    description: "List Clerk users with optional filtering and pagination.",
    inputSchema: s.object(
      "Input for listing Clerk users.",
      {
        ...userFilterFields,
        order_by: s.nonEmptyString("The Clerk order field, such as -created_at."),
        limit: s.integer("Maximum number of users to return.", { minimum: 1, maximum: 500 }),
        offset: s.nonNegativeInteger("Number of users to skip before returning results."),
      },
      {
        optional: [
          "email_address",
          "phone_number",
          "username",
          "user_id",
          "external_id",
          "query",
          "order_by",
          "limit",
          "offset",
        ],
      },
    ),
    outputSchema: s.actionOutput(
      {
        users: s.array("Clerk users returned by the list request.", clerkUserSchema),
        total_count: s.integer("Total number of matching users reported by Clerk."),
      },
      "Output returned after listing Clerk users.",
    ),
  }),
  defineProviderAction(service, {
    name: "count_users",
    description: "Count Clerk users with optional filters.",
    inputSchema: s.object("Input for counting Clerk users.", userFilterFields, {
      optional: ["email_address", "phone_number", "username", "user_id", "external_id", "query"],
    }),
    outputSchema: s.actionOutput(
      { total_count: s.integer("Total number of matching users reported by Clerk.") },
      "Output returned after counting Clerk users.",
    ),
  }),
  defineProviderAction(service, {
    name: "get_user",
    description: "Retrieve a Clerk user by ID.",
    inputSchema: userIdInputSchema,
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "create_user",
    description: "Create a Clerk user.",
    inputSchema: createUserInputSchema,
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "update_user",
    description: "Update a Clerk user.",
    inputSchema: updateUserInputSchema,
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "update_user_metadata",
    description: "Deep merge metadata for a Clerk user.",
    inputSchema: s.object(
      "Input for updating Clerk user metadata.",
      {
        user_id: clerkUserIdSchema,
        public_metadata: clerkMetadataSchema,
        private_metadata: clerkMetadataSchema,
        unsafe_metadata: clerkMetadataSchema,
      },
      { optional: ["public_metadata", "private_metadata", "unsafe_metadata"] },
    ),
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "delete_user",
    description: "Delete a Clerk user.",
    inputSchema: userIdInputSchema,
    outputSchema: s.actionOutput(
      { deleted_object: clerkDeletedObjectSchema },
      "Output returned after deleting a Clerk user.",
    ),
  }),
  defineProviderAction(service, {
    name: "ban_user",
    description: "Ban a Clerk user.",
    inputSchema: userIdInputSchema,
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "unban_user",
    description: "Unban a Clerk user.",
    inputSchema: userIdInputSchema,
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "lock_user",
    description: "Lock a Clerk user.",
    inputSchema: userIdInputSchema,
    outputSchema: userOutputSchema,
  }),
  defineProviderAction(service, {
    name: "unlock_user",
    description: "Unlock a Clerk user.",
    inputSchema: userIdInputSchema,
    outputSchema: userOutputSchema,
  }),
];
