import {
  AUTO_PROVISIONED_INVITATION_STATUS,
  addNomicTaskPrefix,
  isModelSelectionComplete,
  RouteId,
} from "@shared";
import { and, eq, inArray, like } from "drizzle-orm";
import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
import { z } from "zod";
import config from "@/config";
import db, { schema } from "@/database";
import { callEmbedding } from "@/knowledge-base/embedding-clients";
import { resolveApiKeyFromChatApiKey } from "@/knowledge-base/kb-llm-client";
import logger from "@/logging";
import {
  AgentModel,
  InteractionModel,
  InvitationModel,
  KbDocumentModel,
  KnowledgeBaseConnectorModel,
  LlmProviderApiKeyModel,
  McpToolCallModel,
  MemberModel,
  ModelModel,
  OrganizationModel,
  ToolModel,
  UserModel,
  UserTokenModel,
} from "@/models";
import {
  ApiError,
  AppearanceSettingsSchema,
  CompleteOnboardingSchema,
  constructResponseSchema,
  SelectOrganizationSchema,
  UpdateAgentSettingsSchema,
  UpdateAppearanceSettingsSchema,
  UpdateAuthSettingsSchema,
  UpdateConnectionSettingsSchema,
  UpdateKnowledgeSettingsSchema,
  UpdateLlmSettingsSchema,
  UpdatePresetEntityDefaultLabelSchema,
  UpdatePresetEntityDefaultValidationRegexSchema,
  UpdatePresetEntityNameSchema,
  UpdateSecuritySettingsSchema,
} from "@/types";

const organizationRoutes: FastifyPluginAsyncZod = async (fastify) => {
  fastify.get(
    "/api/organization",
    {
      schema: {
        operationId: RouteId.GetOrganization,
        description: "Get organization details",
        tags: ["Organization"],
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId }, reply) => {
      const organization = await OrganizationModel.getById(organizationId);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/appearance-settings",
    {
      schema: {
        operationId: RouteId.UpdateAppearanceSettings,
        description: "Update appearance settings",
        tags: ["Organization"],
        body: UpdateAppearanceSettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const currentOrganization =
        await OrganizationModel.getById(organizationId);
      if (!currentOrganization) {
        throw new ApiError(404, "Organization not found");
      }

      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      if (
        config.enterpriseFeatures.fullWhiteLabeling &&
        (body.appName !== undefined || body.iconLogo !== undefined)
      ) {
        // appName renames the built-in tool/server names, and iconLogo updates
        // the built-in catalog metadata shown across the UI.
        const appNameChanged =
          currentOrganization.appName !== organization.appName;
        const iconChanged =
          currentOrganization.iconLogo !== organization.iconLogo;

        if (appNameChanged || iconChanged) {
          await ToolModel.syncArchestraBuiltInCatalog({
            organization: organization,
          });
        }
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/security-settings",
    {
      schema: {
        operationId: RouteId.UpdateSecuritySettings,
        description:
          "Update security settings (global tool policy, chat file uploads)",
        tags: ["Organization"],
        body: UpdateSecuritySettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/llm-settings",
    {
      schema: {
        operationId: RouteId.UpdateLlmSettings,
        description:
          "Update LLM settings (TOON compression, compression scope, default user limit)",
        tags: ["Organization"],
        body: UpdateLlmSettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const normalizedBody =
        body.defaultUserLimitValue === null
          ? {
              ...body,
              defaultUserLimitModel: null,
              defaultUserLimitCleanupInterval: null,
            }
          : {
              ...body,
              ...(body.defaultUserLimitModel?.length === 0
                ? { defaultUserLimitModel: null }
                : {}),
            };

      const organization = await OrganizationModel.patch(
        organizationId,
        normalizedBody,
      );

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/agent-settings",
    {
      schema: {
        operationId: RouteId.UpdateAgentSettings,
        description: "Update agent settings (default model, default agent)",
        tags: ["Organization"],
        body: UpdateAgentSettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      if (body.defaultLlmApiKeyId) {
        const apiKey = await LlmProviderApiKeyModel.findById(
          body.defaultLlmApiKeyId,
        );
        if (!apiKey || apiKey.organizationId !== organizationId) {
          throw new ApiError(404, "API key not found");
        }
      }

      // The default model and its API key are a pair: persist both or neither.
      // Validate the merged result only when this update touches either field.
      if (
        body.defaultModelId !== undefined ||
        body.defaultLlmApiKeyId !== undefined
      ) {
        const currentOrg = await OrganizationModel.getById(organizationId);
        const mergedModelId =
          body.defaultModelId !== undefined
            ? body.defaultModelId
            : (currentOrg?.defaultModelId ?? null);
        const mergedApiKeyId =
          body.defaultLlmApiKeyId !== undefined
            ? body.defaultLlmApiKeyId
            : (currentOrg?.defaultLlmApiKeyId ?? null);
        if (
          !isModelSelectionComplete({
            modelId: mergedModelId,
            apiKeyId: mergedApiKeyId,
          })
        ) {
          throw new ApiError(
            400,
            "The default model and API key must be set together",
          );
        }
      }

      if (body.defaultAgentId) {
        const agent = await AgentModel.findById(body.defaultAgentId);
        if (!agent || agent.organizationId !== organizationId) {
          throw new ApiError(404, "Agent not found");
        }
      }

      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/connection-settings",
    {
      schema: {
        operationId: RouteId.UpdateConnectionSettings,
        description:
          "Update /connection admin settings (default gateway/proxy, hidden clients/providers)",
        tags: ["Organization"],
        body: UpdateConnectionSettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      if (body.connectionDefaultMcpGatewayId) {
        const agent = await AgentModel.findById(
          body.connectionDefaultMcpGatewayId,
        );
        if (!agent || agent.organizationId !== organizationId) {
          throw new ApiError(404, "MCP gateway not found");
        }
        if (
          agent.agentType !== "mcp_gateway" &&
          agent.agentType !== "profile"
        ) {
          throw new ApiError(400, "Agent is not an MCP gateway");
        }
      }

      if (body.connectionDefaultLlmProxyId) {
        const agent = await AgentModel.findById(
          body.connectionDefaultLlmProxyId,
        );
        if (!agent || agent.organizationId !== organizationId) {
          throw new ApiError(404, "LLM proxy not found");
        }
        if (agent.agentType !== "llm_proxy" && agent.agentType !== "profile") {
          throw new ApiError(400, "Agent is not an LLM proxy");
        }
      }

      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/preset-entity-name",
    {
      schema: {
        operationId: RouteId.UpdatePresetEntityName,
        description:
          "Configure the org-wide display label for catalog presets (the per-item child-configuration entity). Both singular and plural must be set together, or both null to reset.",
        tags: ["Organization"],
        body: UpdatePresetEntityNameSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/preset-entity-default-label",
    {
      schema: {
        operationId: RouteId.UpdatePresetEntityDefaultLabel,
        description:
          "Configure the org-wide display label for the implicit default preset row (parent catalog item). Pass null to reset to the built-in 'Default' label.",
        tags: ["Organization"],
        body: UpdatePresetEntityDefaultLabelSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/preset-entity-default-validation-regex",
    {
      schema: {
        operationId: RouteId.UpdatePresetEntityDefaultValidationRegex,
        description:
          "Set the validation regex applied to default-scoped field values when installing an MCP server (mirrors mcp_preset_entries.validation_regex for the implicit default row). Stored without delimiters or flags. Pass null to disable.",
        tags: ["Organization"],
        body: UpdatePresetEntityDefaultValidationRegexSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/auth-settings",
    {
      schema: {
        operationId: RouteId.UpdateAuthSettings,
        description: "Update organization Auth settings",
        tags: ["Organization"],
        body: UpdateAuthSettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.patch(
    "/api/organization/knowledge-settings",
    {
      schema: {
        operationId: RouteId.UpdateKnowledgeSettings,
        description: "Update knowledge settings (embedding model)",
        tags: ["Organization"],
        body: UpdateKnowledgeSettingsSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const currentOrg = await OrganizationModel.getById(organizationId);

      // Embedding model is locked once both key and model have been saved
      const isEmbeddingConfigLocked =
        !!currentOrg?.embeddingChatApiKeyId && !!currentOrg?.embeddingModel;

      if (body.embeddingModel) {
        if (
          isEmbeddingConfigLocked &&
          body.embeddingModel !== currentOrg.embeddingModel
        ) {
          throw new ApiError(
            400,
            "Embedding model cannot be changed once configured. Changing models requires re-embedding all documents.",
          );
        }
      }

      if (
        isEmbeddingConfigLocked &&
        body.embeddingChatApiKeyId !== undefined &&
        body.embeddingChatApiKeyId !== currentOrg.embeddingChatApiKeyId
      ) {
        throw new ApiError(
          400,
          "Embedding API key cannot be changed once configured. Drop the embedding configuration before selecting a different key.",
        );
      }

      // Validate embedding API key exists
      if (body.embeddingChatApiKeyId) {
        const chatApiKey = await LlmProviderApiKeyModel.findById(
          body.embeddingChatApiKeyId,
        );
        if (!chatApiKey) {
          throw new ApiError(404, "Embedding API key not found");
        }
      }

      const shouldValidateEmbeddingSelection =
        body.embeddingChatApiKeyId !== undefined ||
        body.embeddingModel !== undefined;
      const effectiveEmbeddingKeyId =
        body.embeddingChatApiKeyId ?? currentOrg?.embeddingChatApiKeyId ?? null;
      const effectiveEmbeddingModel =
        body.embeddingModel ?? currentOrg?.embeddingModel ?? null;

      if (
        shouldValidateEmbeddingSelection &&
        effectiveEmbeddingKeyId &&
        effectiveEmbeddingModel
      ) {
        const resolved = await resolveApiKeyFromChatApiKey(
          effectiveEmbeddingKeyId,
        );
        if (!resolved) {
          throw new ApiError(400, "Could not resolve embedding API key");
        }

        const model = await ModelModel.findByProviderAndModelId(
          resolved.provider,
          effectiveEmbeddingModel,
        );

        if (model?.embeddingDimensions === null || !model) {
          throw new ApiError(
            400,
            "Embedding model must be marked as an embedding model with configured dimensions in LLM Providers > Models.",
          );
        }
      }

      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.post(
    "/api/organization/knowledge-settings/drop-embedding",
    {
      schema: {
        operationId: RouteId.DropEmbeddingConfig,
        description:
          "Drop the embedding configuration, deleting all KB documents and resetting connector checkpoints",
        tags: ["Organization"],
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId }, reply) => {
      const currentOrg = await OrganizationModel.getById(organizationId);
      if (!currentOrg?.embeddingChatApiKeyId || !currentOrg?.embeddingModel) {
        throw new ApiError(
          400,
          "Embedding configuration is not locked — nothing to drop",
        );
      }

      // Delete all KB documents (chunks cascade via FK)
      await KbDocumentModel.deleteByOrganization(organizationId);

      // Reset connector checkpoints so next sync does a full re-ingest
      await KnowledgeBaseConnectorModel.resetCheckpointsByOrganization(
        organizationId,
      );

      // Clear embedding config
      const organization = await OrganizationModel.patch(organizationId, {
        embeddingModel: null,
        embeddingChatApiKeyId: null,
      });

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.post(
    "/api/organization/knowledge-settings/test-embedding",
    {
      schema: {
        operationId: RouteId.TestEmbeddingConnection,
        description: "Test the embedding connection by embedding a sample text",
        tags: ["Organization"],
        body: z.object({
          embeddingChatApiKeyId: z.string().uuid(),
          embeddingModel: z.string().min(1),
        }),
        response: constructResponseSchema(
          z.object({
            success: z.boolean(),
            error: z.string().optional(),
          }),
        ),
      },
    },
    async ({ body }, reply) => {
      // Validate API key exists
      const chatApiKey = await LlmProviderApiKeyModel.findById(
        body.embeddingChatApiKeyId,
      );
      if (!chatApiKey) {
        throw new ApiError(404, "API key not found");
      }

      // Resolve the actual secret
      const resolved = await resolveApiKeyFromChatApiKey(
        body.embeddingChatApiKeyId,
      );
      if (!resolved) {
        return reply.send({
          success: false,
          error: "Could not resolve API key secret",
        });
      }

      const model = await ModelModel.findByProviderAndModelId(
        resolved.provider,
        body.embeddingModel,
      );
      if (!model?.embeddingDimensions) {
        return reply.send({
          success: false,
          error:
            "Embedding model must be marked as an embedding model with configured dimensions in LLM Providers > Models.",
        });
      }

      try {
        const response = await callEmbedding({
          inputs: [
            addNomicTaskPrefix(
              body.embeddingModel,
              "hello world",
              "search_document",
            ),
          ],
          model: body.embeddingModel,
          apiKey: resolved.apiKey,
          baseUrl: resolved.baseUrl,
          dimensions: model.embeddingDimensions,
          provider: resolved.provider,
        });

        if (response.data.length > 0) {
          return reply.send({ success: true });
        }

        return reply.send({
          success: false,
          error: "No embedding data returned",
        });
      } catch (err) {
        const message = err instanceof Error ? err.message : "Unknown error";
        logger.error(
          { err },
          "[testEmbeddingConnection] embedding call failed",
        );
        return reply.send({ success: false, error: message });
      }
    },
  );

  fastify.post(
    "/api/organization/complete-onboarding",
    {
      schema: {
        operationId: RouteId.CompleteOnboarding,
        description: "Mark organization onboarding as complete",
        tags: ["Organization"],
        body: CompleteOnboardingSchema,
        response: constructResponseSchema(SelectOrganizationSchema),
      },
    },
    async ({ organizationId, body }, reply) => {
      const organization = await OrganizationModel.patch(organizationId, body);

      if (!organization) {
        throw new ApiError(404, "Organization not found");
      }

      return reply.send(organization);
    },
  );

  fastify.get(
    "/api/organization/onboarding-status",
    {
      schema: {
        operationId: RouteId.GetOnboardingStatus,
        description: "Check if organization onboarding is complete",
        tags: ["Organization"],
        response: constructResponseSchema(
          z.object({
            hasLlmProxyLogs: z.boolean(),
            hasMcpGatewayLogs: z.boolean(),
          }),
        ),
      },
    },
    async (_request, reply) => {
      // Check if onboarding is complete by checking if there are any logs
      const interactionCount = await InteractionModel.getCount();
      const mcpToolCallCount = await McpToolCallModel.getCount();

      return reply.send({
        hasLlmProxyLogs: interactionCount > 0,
        hasMcpGatewayLogs: mcpToolCallCount > 0,
      });
    },
  );

  /**
   * Get signup status for organization members.
   * Returns members that don't have an account record (auto-provisioned, haven't signed up),
   * along with the provider they were auto-provisioned from.
   */
  fastify.get(
    "/api/organization/members/signup-status",
    {
      schema: {
        operationId: RouteId.GetMemberSignupStatus,
        description:
          "Get which members have completed signup (have an account record)",
        tags: ["Organization"],
        response: constructResponseSchema(
          z.object({
            pendingSignupMembers: z.array(
              z.object({
                userId: z.string(),
                name: z.string().nullable(),
                email: z.string(),
                image: z.string().nullable(),
                role: z.string(),
                provider: z.string().nullable(),
                invitationId: z.string().nullable(),
              }),
            ),
          }),
        ),
      },
    },
    async ({ organizationId }, reply) => {
      // Get all member user IDs for this organization
      const members = await db
        .select({ userId: schema.membersTable.userId })
        .from(schema.membersTable)
        .where(eq(schema.membersTable.organizationId, organizationId));

      if (members.length === 0) {
        return reply.send({ pendingSignupMembers: [] });
      }

      const memberUserIds = members.map((m) => m.userId);

      // Find which of these users have an account record
      const usersWithAccounts = await db
        .select({ userId: schema.accountsTable.userId })
        .from(schema.accountsTable)
        .where(inArray(schema.accountsTable.userId, memberUserIds));

      const hasAccountSet = new Set(usersWithAccounts.map((a) => a.userId));
      const pendingUserIds = memberUserIds.filter(
        (id) => !hasAccountSet.has(id),
      );

      if (pendingUserIds.length === 0) {
        return reply.send({ pendingSignupMembers: [] });
      }

      // Look up auto-provisioned invitations to get provider and invitation ID
      const invitations = await db
        .select({
          id: schema.invitationsTable.id,
          email: schema.invitationsTable.email,
          status: schema.invitationsTable.status,
        })
        .from(schema.invitationsTable)
        .where(
          like(
            schema.invitationsTable.status,
            `${AUTO_PROVISIONED_INVITATION_STATUS}%`,
          ),
        );

      // Build email → { provider, invitationId } map
      const emailToInvitation = new Map<
        string,
        { provider: string | null; invitationId: string }
      >();
      for (const inv of invitations) {
        const parts = inv.status.split(":");
        emailToInvitation.set(inv.email, {
          provider: parts.length === 2 ? parts[1] : null,
          invitationId: inv.id,
        });
      }

      // Get emails for pending users
      const pendingUsers = await db
        .select({
          id: schema.usersTable.id,
          email: schema.usersTable.email,
          name: schema.usersTable.name,
          image: schema.usersTable.image,
          role: schema.membersTable.role,
        })
        .from(schema.membersTable)
        .innerJoin(
          schema.usersTable,
          eq(schema.membersTable.userId, schema.usersTable.id),
        )
        .where(
          and(
            eq(schema.membersTable.organizationId, organizationId),
            inArray(schema.usersTable.id, pendingUserIds),
          ),
        );

      const pendingSignupMembers = pendingUsers.map((u) => {
        const inv = emailToInvitation.get(u.email);
        return {
          userId: u.id,
          name: u.name,
          email: u.email,
          image: u.image,
          role: u.role,
          provider: inv?.provider ?? null,
          invitationId: inv?.invitationId ?? null,
        };
      });

      return reply.send({ pendingSignupMembers });
    },
  );

  /**
   * Delete an auto-provisioned member who hasn't completed signup.
   * Removes the member, invitation, user token, and user record.
   */
  fastify.delete(
    "/api/organization/members/:userId/pending-signup",
    {
      schema: {
        operationId: RouteId.DeletePendingSignupMember,
        description:
          "Delete an auto-provisioned member who hasn't completed signup",
        tags: ["Organization"],
        params: z.object({ userId: z.string() }),
        response: constructResponseSchema(z.object({ success: z.boolean() })),
      },
    },
    async ({ organizationId, params }, reply) => {
      const { userId } = params;

      // Verify user has no account (is actually pending signup)
      const [account] = await db
        .select({ userId: schema.accountsTable.userId })
        .from(schema.accountsTable)
        .where(eq(schema.accountsTable.userId, userId))
        .limit(1);

      if (account) {
        throw new ApiError(
          400,
          "Cannot delete a member who has already completed signup",
        );
      }

      // Get user email to find their invitation
      const user = await UserModel.getById(userId);
      if (!user) {
        throw new ApiError(404, "User not found");
      }

      // Delete invitation(s) with auto-provisioned status for this email
      const invitations = await db
        .select({ id: schema.invitationsTable.id })
        .from(schema.invitationsTable)
        .where(
          and(
            eq(schema.invitationsTable.email, user.email),
            like(
              schema.invitationsTable.status,
              `${AUTO_PROVISIONED_INVITATION_STATUS}%`,
            ),
          ),
        );

      for (const inv of invitations) {
        await InvitationModel.delete(inv.id);
      }

      // Delete personal tokens
      await UserTokenModel.deleteByUserAndOrg(userId, organizationId);

      // Delete member record
      await MemberModel.deleteByMemberOrUserId(userId, organizationId);

      // Delete user record (no other memberships since auto-provisioned)
      await UserModel.delete(userId);

      return reply.send({ success: true });
    },
  );

  fastify.get(
    "/api/organization/members",
    {
      schema: {
        operationId: RouteId.GetOrganizationMembers,
        description: "Get all members of the organization",
        tags: ["Organization"],
        response: constructResponseSchema(
          z.array(
            z.object({
              id: z.string(),
              name: z.string(),
              email: z.string(),
            }),
          ),
        ),
      },
    },
    async ({ organizationId }, reply) => {
      const members = await MemberModel.findAllByOrganization(organizationId);
      return reply.send(members);
    },
  );

  fastify.get(
    "/api/organization/members/:idOrEmail",
    {
      schema: {
        operationId: RouteId.GetOrganizationMember,
        description:
          "Get a member of the organization by user ID or email address",
        tags: ["Organization"],
        params: z.object({
          idOrEmail: z.string().min(1).describe("User ID or email address"),
        }),
        response: constructResponseSchema(
          z.object({
            id: z.string(),
            name: z.string(),
            email: z.string(),
            role: z.string(),
          }),
        ),
      },
    },
    async ({ organizationId, params: { idOrEmail } }, reply) => {
      const member = await MemberModel.findByIdOrEmail(
        idOrEmail,
        organizationId,
      );
      if (!member) {
        throw new ApiError(404, "Member not found");
      }
      return reply.send(member);
    },
  );

  fastify.get(
    "/api/organization/appearance-settings",
    {
      schema: {
        operationId: RouteId.GetAppearanceSettings,
        description: "Get organization appearance settings",
        tags: ["Organization"],
        response: constructResponseSchema(AppearanceSettingsSchema),
      },
    },
    async (_request, reply) => {
      return reply.send(await OrganizationModel.getAppearanceSettings());
    },
  );
};

export default organizationRoutes;
