import { getAccessToken } from "./auth-state";
import { State } from "./types/proto/api/v1/common_pb";
import { Activity_Level, Activity_Type, type Activity } from "./types/proto/api/v1/activity_service_pb";
import { type Attachment } from "./types/proto/api/v1/attachment_service_pb";
import type { GetCurrentUserResponse, RefreshTokenResponse, SignInResponse } from "./types/proto/api/v1/auth_service_pb";
import { IdentityProvider_Type, type IdentityProvider } from "./types/proto/api/v1/idp_service_pb";
import {
  type InstanceProfile,
  type InstanceSetting,
  type InstanceSetting_GeneralSetting,
  type InstanceSetting_MemoRelatedSetting,
  type InstanceSetting_StorageSetting,
  InstanceSetting_StorageSetting_StorageType,
} from "./types/proto/api/v1/instance_service_pb";
import { type Memo, type Reaction, Visibility } from "./types/proto/api/v1/memo_service_pb";
import { type Shortcut } from "./types/proto/api/v1/shortcut_service_pb";
import {
  type CreatePersonalAccessTokenResponse,
  type PersonalAccessToken,
  type User,
  type UserNotification,
  User_Role,
  type UserSetting,
  type UserSetting_GeneralSetting,
  type UserSetting_WebhooksSetting,
  type UserStats,
  type UserWebhook,
} from "./types/proto/api/v1/user_service_pb";
import { redirectOnAuthFailure } from "./utils/auth-redirect";
import { getClerkSessionToken } from "./utils/clerk";

// Call backend directly (bypass Vite proxy).
// Override via VITE_API_BASE_URL, e.g. "http://localhost:5000"
const API_ORIGIN = (import.meta.env.VITE_API_BASE_URL as string | undefined) || "http://localhost:5000";
const API_BASE = `${API_ORIGIN}/api/v1`;

type ApiMemo = {
  id: number;
  content: string;
  visibility?: string | null;
  tags?: string[];
  creator_id?: number | string;
  create_time?: string;
  update_time?: string;
  display_time?: string;
};

type ApiAttachment = {
  id: number;
  filename: string;
  url: string;
};

type ApiShortcut = {
  id: number;
  name: string;
  filter: string;
};

type ApiActivity = {
  id: number;
  type?: string | null;
  description?: string | null;
};

type ApiIdentityProvider = {
  id: number;
  name: string;
  type?: string | null;
};

type ApiInstanceInfo = {
  mode?: string | null;
  version?: string | null;
};

type ApiUser = {
  id: number;
  email?: string | null;
  nickname?: string | null;
};

type RequestOptions = {
  method?: string;
  body?: unknown;
  headers?: HeadersInit;
};

const parseBody = async (response: Response): Promise<unknown> => {
  const contentType = response.headers.get("content-type") || "";
  if (contentType.includes("application/json")) {
    try {
      return await response.json();
    } catch {
      return null;
    }
  }

  const text = await response.text();
  if (!text) {
    return null;
  }

  try {
    return JSON.parse(text) as unknown;
  } catch {
    return text;
  }
};

const fetchJson = async <T>(path: string, options: RequestOptions = {}): Promise<T> => {
  // Prefer Clerk token if available; fallback to legacy token store.
  const clerkToken = await getClerkSessionToken();
  const token = clerkToken || getAccessToken();
  const headers = new Headers(options.headers);
  if (token) {
    headers.set("Authorization", `Bearer ${token}`);
  }

  let body = options.body;
  const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
  if (body !== undefined && body !== null && !isFormData && typeof body !== "string") {
    headers.set("Content-Type", "application/json");
    body = JSON.stringify(body);
  }

  const response = await fetch(`${API_BASE}${path}`, {
    method: options.method ?? (body ? "POST" : "GET"),
    headers,
    body: body as BodyInit | null | undefined,
    credentials: "include",
  });

  if (!response.ok) {
    if (response.status === 401 || response.status === 403) {
      redirectOnAuthFailure();
    }
    let message = `${response.status} ${response.statusText}`;
    const data = await parseBody(response);
    if (data && typeof data === "object" && "detail" in data) {
      const detail = (data as { detail?: string }).detail;
      if (detail) {
        message = detail;
      }
    } else if (typeof data === "string" && data.trim()) {
      message = data;
    }
    throw new Error(message);
  }

  if (response.status === 204) {
    return undefined as T;
  }

  const data = await parseBody(response);
  return data as T;
};

const parseResourceId = (name: string): string => {
  const parts = name.split("/");
  return parts[parts.length - 1] || name;
};

const parseNumericId = (name: string): number => {
  const raw = parseResourceId(name);
  const parsed = Number.parseInt(raw, 10);
  return Number.isFinite(parsed) ? parsed : 1;
};

const buildSnippet = (content: string): string => {
  const trimmed = content.trim();
  if (!trimmed) {
    return "";
  }
  return trimmed.length > 140 ? trimmed.slice(0, 140) : trimmed;
};

const visibilityFromApi = (value?: string | null): Visibility => {
  const normalized = (value || "").toUpperCase();
  if (normalized === "PUBLIC") {
    return Visibility.PUBLIC;
  }
  if (normalized === "PROTECTED") {
    return Visibility.PROTECTED;
  }
  return Visibility.PRIVATE;
};

const visibilityToApi = (value?: Visibility): string => {
  switch (value) {
    case Visibility.PUBLIC:
      return "PUBLIC";
    case Visibility.PROTECTED:
      return "PROTECTED";
    default:
      return "PRIVATE";
  }
};

const parseTimestamp = (dateStr?: string) => {
  if (!dateStr) return undefined;
  // Handle "YYYY-MM-DD HH:MM:SS" SQLite string format
  // Append 'Z' to treat as UTC if no timezone info, or assume local?
  // Backend stores DATETIME('now') which is UTC.
  const date = new Date(dateStr.endsWith("Z") ? dateStr : `${dateStr}Z`);
  const seconds = BigInt(Math.floor(date.getTime() / 1000));
  return { seconds, nanos: 0 } as any;
};

const memoFromApi = (raw: ApiMemo): Memo => {
  const content = raw.content || "";
  console.log("DEBUG: memoFromApi input:", raw);
  const result = {
    name: `memos/${raw.id}`,
    state: State.NORMAL,
    creator: `users/${raw.creator_id ?? 1}`,
    content,
    visibility: visibilityFromApi(raw.visibility),
    tags: Array.isArray(raw.tags) ? raw.tags : [],
    pinned: false,
    attachments: [],
    relations: [],
    reactions: [],
    property: {
      hasLink: false,
      hasTaskList: false,
      hasCode: false,
      hasIncompleteTasks: false,
    },
    snippet: buildSnippet(content),
    createTime: parseTimestamp(raw.create_time),
    updateTime: parseTimestamp(raw.update_time),
    displayTime: parseTimestamp(raw.display_time),
  };
  console.log("DEBUG: memoFromApi output:", result);
  return result;
};

const userFromApi = (raw: ApiUser): User => {
  const email = raw.email || `user${raw.id}@example.com`;
  const username = email.includes("@") ? email.split("@")[0] : `user${raw.id}`;
  return {
    name: `users/${raw.id}`,
    role: User_Role.USER,
    username,
    email,
    displayName: raw.nickname || "",
    avatarUrl: "",
    description: "",
    password: "",
    state: State.NORMAL,
  };
};

const attachmentFromApi = (raw: ApiAttachment): Attachment => {
  return {
    name: `attachments/${raw.id}`,
    filename: raw.filename || "",
    content: new Uint8Array(),
    externalLink: raw.url || "",
    type: "",
    size: BigInt(0),
  };
};

const shortcutFromApi = (raw: ApiShortcut): Shortcut => {
  return {
    name: `users/1/shortcuts/${raw.id}`,
    title: raw.name || "",
    filter: raw.filter || "",
  };
};

const activityFromApi = (raw: ApiActivity): Activity => {
  return {
    name: `activities/${raw.id}`,
    creator: "users/1",
    type: Activity_Type.TYPE_UNSPECIFIED,
    level: Activity_Level.INFO,
  };
};

const identityProviderFromApi = (raw: ApiIdentityProvider): IdentityProvider => {
  const type = raw.type && raw.type.toLowerCase().includes("oauth") ? IdentityProvider_Type.OAUTH2 : IdentityProvider_Type.TYPE_UNSPECIFIED;
  return {
    name: `identity-providers/${raw.id}`,
    type,
    title: raw.name || "",
    identifierFilter: "",
  };
};

const defaultInstanceGeneralSetting: InstanceSetting_GeneralSetting = {
  disallowUserRegistration: false,
  disallowPasswordAuth: false,
  additionalScript: "",
  additionalStyle: "",
  customProfile: {
    title: "",
    description: "",
    logoUrl: "",
  },
  weekStartDayOffset: 0,
  disallowChangeUsername: false,
  disallowChangeNickname: false,
};

const defaultInstanceMemoSetting: InstanceSetting_MemoRelatedSetting = {
  disallowPublicVisibility: false,
  displayWithUpdateTime: false,
  contentLengthLimit: 0,
  enableDoubleClickEdit: true,
  reactions: [],
};

const defaultInstanceStorageSetting: InstanceSetting_StorageSetting = {
  storageType: InstanceSetting_StorageSetting_StorageType.LOCAL,
  filepathTemplate: "",
  uploadSizeLimitMb: BigInt(50),
};

const buildInstanceSetting = (name: string): InstanceSetting => {
  const key = parseResourceId(name);
  if (key === "GENERAL") {
    return { name, value: { case: "generalSetting", value: defaultInstanceGeneralSetting } };
  }
  if (key === "MEMO_RELATED") {
    return { name, value: { case: "memoRelatedSetting", value: defaultInstanceMemoSetting } };
  }
  if (key === "STORAGE") {
    return { name, value: { case: "storageSetting", value: defaultInstanceStorageSetting } };
  }
  return { name, value: { case: "generalSetting", value: defaultInstanceGeneralSetting } };
};

const defaultUserGeneralSetting: UserSetting_GeneralSetting = {
  locale: "en",
  memoVisibility: "PRIVATE",
  theme: "",
};

const defaultUserWebhooksSetting: UserSetting_WebhooksSetting = {
  webhooks: [],
};

const buildUserSettings = (parent: string): UserSetting[] => {
  return [
    {
      name: `${parent}/settings/GENERAL`,
      value: { case: "generalSetting", value: defaultUserGeneralSetting },
    },
    {
      name: `${parent}/settings/WEBHOOKS`,
      value: { case: "webhooksSetting", value: defaultUserWebhooksSetting },
    },
  ];
};

const extractFilterValue = (filter?: string, pattern?: RegExp): string | undefined => {
  if (!filter || !pattern) {
    return undefined;
  }
  const match = filter.match(pattern);
  return match?.[1];
};

const applyMemoFilter = (memos: Memo[], filter?: string): Memo[] => {
  if (!filter) {
    return memos;
  }

  let filtered = memos;
  const contentValue = extractFilterValue(filter, /content\.contains\("([^"]+)"\)/);
  if (contentValue) {
    const needle = contentValue.toLowerCase();
    filtered = filtered.filter((memo) => memo.content.toLowerCase().includes(needle));
  }

  const creatorValue = extractFilterValue(filter, /creator_id\s*==\s*(\d+)/);
  if (creatorValue) {
    filtered = filtered.filter((memo) => memo.creator.endsWith(`/${creatorValue}`));
  }

  return filtered;
};

const extractTagFilter = (filter?: string): string | undefined => {
  return extractFilterValue(filter, /tag\s*==\s*"([^"]+)"/) ?? extractFilterValue(filter, /tag\s*in\s*\["([^"]+)"\]/);
};

// Core service clients
export const instanceServiceClient = {
  async getInstanceProfile(_request?: unknown): Promise<InstanceProfile> {
    void _request;
    const data = await fetchJson<ApiInstanceInfo>("/instance", { method: "GET" });
    const instanceUrl = API_ORIGIN;
    return {
      owner: "users/1",
      version: typeof data?.version === "string" ? data.version : "",
      mode: typeof data?.mode === "string" ? data.mode : "dev",
      instanceUrl,
    };
  },
  async getInstanceSetting(request: { name: string }): Promise<InstanceSetting> {
    return buildInstanceSetting(request.name);
  },
  async updateInstanceSetting(request: { setting?: InstanceSetting }): Promise<InstanceSetting> {
    if (request.setting) {
      return request.setting;
    }
    return buildInstanceSetting("instance/settings/GENERAL");
  },
};

export const authServiceClient = {
  async getCurrentUser(_request?: unknown): Promise<GetCurrentUserResponse> {
    void _request;
    const data = await fetchJson<GetCurrentUserResponse>("/auth/me", { method: "GET" });
    return data;
  },
  async signIn(request: unknown): Promise<SignInResponse> {
    const body = typeof request === "object" && request ? request : {};
    const data = await fetchJson<{ accessToken?: string }>("/auth/signin", { method: "POST", body });
    return {
      accessToken: data.accessToken || "",
    };
  },
  async signOut(_request?: unknown): Promise<Record<string, never>> {
    void _request;
    return {};
  },
  async refreshToken(_request?: unknown): Promise<RefreshTokenResponse> {
    void _request;
    return { accessToken: "" };
  },
};

export const userServiceClient = {
  async listUsers(_request?: unknown): Promise<{ users: User[]; nextPageToken: string; totalSize: number }> {
    void _request;
    const data = await fetchJson<ApiUser[]>("/users", { method: "GET" });
    const users = Array.isArray(data) ? data.map(userFromApi) : [];
    return { users, nextPageToken: "", totalSize: users.length };
  },
  async getUser(request: { name: string }): Promise<User> {
    const userId = parseNumericId(request.name);
    const data = await fetchJson<ApiUser>(`/users/${userId}`, { method: "GET" });
    return userFromApi(data);
  },
  async createUser(request: { user?: User }): Promise<User> {
    const user = request.user;
    if (!user) {
      throw new Error("Missing user payload");
    }
    const email = user.email || (user.username ? `${user.username}@example.com` : undefined);
    const payload = {
      email,
      password: user.password || "",
    };
    const data = await fetchJson<ApiUser>("/users", { method: "POST", body: payload });
    return userFromApi(data);
  },
  async updateUser(request: { user?: User }): Promise<User> {
    const user = request.user;
    if (!user?.name) {
      throw new Error("Missing user name");
    }
    const userId = parseNumericId(user.name);
    const payload = {
      email: user.email,
      nickname: user.displayName,
    };
    const data = await fetchJson<ApiUser>(`/users/${userId}`, { method: "PATCH", body: payload });
    return userFromApi(data);
  },
  async deleteUser(_request?: { name: string }): Promise<void> {
    void _request;
    return;
  },
  async getUserStats(request: { name: string }): Promise<UserStats> {
    return {
      name: request.name,
      memoDisplayTimestamps: [],
      memoTypeStats: {
        linkCount: 0,
        codeCount: 0,
        todoCount: 0,
        undoCount: 0,
      },
      tagCount: {},
      pinnedMemos: [],
      totalMemoCount: 0,
    };
  },
  async listAllUserStats(_request?: unknown): Promise<{ stats: UserStats[] }> {
    void _request;
    return { stats: [] };
  },
  async listUserSettings(request: { parent: string }): Promise<{ settings: UserSetting[]; nextPageToken: string; totalSize: number }> {
    const settings = buildUserSettings(request.parent);
    return { settings, nextPageToken: "", totalSize: settings.length };
  },
  async updateUserSetting(request: { setting?: UserSetting }): Promise<UserSetting> {
    if (request.setting) {
      return request.setting;
    }
    return buildUserSettings("users/1")[0];
  },
  async listUserNotifications(_request: { parent: string }): Promise<{ notifications: UserNotification[]; nextPageToken: string }> {
    void _request;
    return { notifications: [], nextPageToken: "" };
  },
  async updateUserNotification(request: { notification?: UserNotification }): Promise<UserNotification> {
    if (request.notification) {
      return request.notification;
    }
    return {
      name: "users/1/notifications/1",
      sender: "users/1",
      status: 1,
      type: 0,
    };
  },
  async deleteUserNotification(_request: { name: string }): Promise<void> {
    void _request;
    return;
  },
  async listPersonalAccessTokens(_request: { parent: string }): Promise<{
    personalAccessTokens: PersonalAccessToken[];
    nextPageToken: string;
    totalSize: number;
  }> {
    void _request;
    return { personalAccessTokens: [], nextPageToken: "", totalSize: 0 };
  },
  async createPersonalAccessToken(request: {
    parent?: string;
    description?: string;
    expiresInDays?: number;
  }): Promise<CreatePersonalAccessTokenResponse> {
    const parent = request.parent || "users/1";
    const tokenId = Date.now().toString();
    const personalAccessToken: PersonalAccessToken = {
      name: `${parent}/personalAccessTokens/${tokenId}`,
      description: request.description || "",
    };
    return {
      personalAccessToken,
      token: "stub-token",
    };
  },
  async deletePersonalAccessToken(_request: { name: string }): Promise<void> {
    void _request;
    return;
  },
  async listUserWebhooks(_request: { parent: string }): Promise<{ webhooks: UserWebhook[] }> {
    void _request;
    return { webhooks: [] };
  },
  async createUserWebhook(request: { parent?: string; webhook?: UserWebhook }): Promise<UserWebhook> {
    const parent = request.parent || "users/1";
    const webhook = request.webhook;
    if (webhook?.name) {
      return webhook;
    }
    return {
      name: `${parent}/webhooks/${Date.now()}`,
      url: webhook?.url || "",
      displayName: webhook?.displayName || "",
    };
  },
  async updateUserWebhook(request: { webhook?: UserWebhook }): Promise<UserWebhook> {
    if (request.webhook) {
      return request.webhook;
    }
    return {
      name: "users/1/webhooks/1",
      url: "",
      displayName: "",
    };
  },
  async deleteUserWebhook(_request: { name: string }): Promise<void> {
    void _request;
    return;
  },
};

export const memoServiceClient = {
  async listMemos(request: { filter?: string } = {}): Promise<{ memos: Memo[]; nextPageToken: string }> {
    const tag = extractTagFilter(request.filter);
    const query = tag ? `?tag=${encodeURIComponent(tag)}` : "";
    const data = await fetchJson<ApiMemo[]>(`/memos${query}`, { method: "GET" });
    const memos = Array.isArray(data) ? data.map(memoFromApi) : [];
    return { memos: applyMemoFilter(memos, request.filter), nextPageToken: "" };
  },
  async getMemo(request: { name: string }): Promise<Memo> {
    const memoId = parseNumericId(request.name);
    const data = await fetchJson<ApiMemo>(`/memos/${memoId}`, { method: "GET" });
    return memoFromApi(data);
  },
  async createMemo(request: { memo?: Memo }): Promise<Memo> {
    const memo = request.memo;
    if (!memo) {
      throw new Error("Missing memo payload");
    }
    const payload = {
      content: memo.content,
      visibility: visibilityToApi(memo.visibility),
      tags: memo.tags || [],
    };
    const data = await fetchJson<ApiMemo>("/memos", { method: "POST", body: payload });
    return memoFromApi(data);
  },
  async updateMemo(request: { memo?: Memo }): Promise<Memo> {
    const memo = request.memo;
    if (!memo?.name) {
      throw new Error("Missing memo name");
    }
    const memoId = parseNumericId(memo.name);
    const payload: { content?: string; visibility?: string; tags?: string[] } = {};
    if (memo.content !== undefined) {
      payload.content = memo.content;
    }
    if (memo.visibility !== undefined) {
      payload.visibility = visibilityToApi(memo.visibility);
    }
    if (memo.tags) {
      payload.tags = memo.tags;
    }
    const data = await fetchJson<ApiMemo>(`/memos/${memoId}`, { method: "PATCH", body: payload });
    return memoFromApi(data);
  },
  async deleteMemo(request: { name: string }): Promise<void> {
    const memoId = parseNumericId(request.name);
    await fetchJson(`/memos/${memoId}`, { method: "DELETE" });
  },
  async createMemoComment(request: { name: string; comment?: Memo }): Promise<Memo> {
    if (!request.comment) {
      throw new Error("Missing comment payload");
    }
    const created = await memoServiceClient.createMemo({ memo: request.comment });
    return { ...created, parent: request.name };
  },
  async listMemoComments(_request: { name: string }): Promise<{ memos: Memo[]; nextPageToken: string; totalSize: number }> {
    void _request;
    return { memos: [], nextPageToken: "", totalSize: 0 };
  },
  async upsertMemoReaction(request: { name: string; reaction?: Partial<Reaction> }): Promise<Reaction> {
    const reactionType = request.reaction?.reactionType || "";
    const contentId = request.reaction?.contentId || request.name;
    return {
      name: `${request.name}/reactions/1`,
      creator: "users/1",
      contentId,
      reactionType,
    };
  },
  async deleteMemoReaction(_request: { name: string }): Promise<void> {
    void _request;
    return;
  },
};

export const attachmentServiceClient = {
  async listAttachments(_request?: unknown): Promise<{ attachments: Attachment[]; nextPageToken: string; totalSize: number }> {
    void _request;
    const data = await fetchJson<ApiAttachment[]>("/attachments", { method: "GET" });
    const attachments = Array.isArray(data) ? data.map(attachmentFromApi) : [];
    return { attachments, nextPageToken: "", totalSize: attachments.length };
  },
  async createAttachment(request: { attachment?: Attachment }): Promise<Attachment> {
    const attachment = request.attachment;
    if (!attachment) {
      throw new Error("Missing attachment payload");
    }
    const content = attachment.content || new Uint8Array();
    const fileType = attachment.type || "application/octet-stream";
    const filename = attachment.filename || "upload.bin";
    const formData = new FormData();
    formData.append("file", new Blob([content], { type: fileType }), filename);
    const data = await fetchJson<ApiAttachment>("/attachments", { method: "POST", body: formData });
    const mapped = attachmentFromApi(data);
    return {
      ...mapped,
      type: attachment.type || "",
      size: attachment.size || BigInt(0),
    };
  },
  async deleteAttachment(_request: { name: string }): Promise<void> {
    void _request;
    return;
  },
};

export const shortcutServiceClient = {
  async listShortcuts(_request?: { parent?: string }): Promise<{ shortcuts: Shortcut[] }> {
    void _request;
    const data = await fetchJson<ApiShortcut[]>("/shortcuts", { method: "GET" });
    const shortcuts = Array.isArray(data) ? data.map(shortcutFromApi) : [];
    return { shortcuts };
  },
  async createShortcut(request: { shortcut?: Shortcut }): Promise<Shortcut> {
    const shortcut = request.shortcut;
    if (!shortcut) {
      throw new Error("Missing shortcut payload");
    }
    const payload = {
      name: shortcut.title || "",
      filter: shortcut.filter || "",
    };
    const data = await fetchJson<ApiShortcut>("/shortcuts", { method: "POST", body: payload });
    return shortcutFromApi(data);
  },
  async updateShortcut(request: { shortcut?: Shortcut }): Promise<Shortcut> {
    const shortcut = request.shortcut;
    if (!shortcut?.name) {
      throw new Error("Missing shortcut name");
    }
    const shortcutId = parseNumericId(shortcut.name);
    const payload = {
      name: shortcut.title || "",
      filter: shortcut.filter || "",
    };
    const data = await fetchJson<ApiShortcut>(`/shortcuts/${shortcutId}`, { method: "PATCH", body: payload });
    return shortcutFromApi(data);
  },
  async deleteShortcut(request: { name: string }): Promise<void> {
    const shortcutId = parseNumericId(request.name);
    await fetchJson(`/shortcuts/${shortcutId}`, { method: "DELETE" });
  },
};

export const activityServiceClient = {
  async listActivities(_request?: unknown): Promise<{ activities: Activity[]; nextPageToken: string }> {
    void _request;
    const data = await fetchJson<ApiActivity[]>("/activities", { method: "GET" });
    const activities = Array.isArray(data) ? data.map(activityFromApi) : [];
    return { activities, nextPageToken: "" };
  },
  async getActivity(request: { name: string }): Promise<Activity> {
    const activityId = parseNumericId(request.name);
    return activityFromApi({ id: activityId });
  },
};

export const identityProviderServiceClient = {
  async listIdentityProviders(_request?: unknown): Promise<{ identityProviders: IdentityProvider[] }> {
    void _request;
    const data = await fetchJson<ApiIdentityProvider[]>("/idp", { method: "GET" });
    const identityProviders = Array.isArray(data) ? data.map(identityProviderFromApi) : [];
    return { identityProviders };
  },
  async createIdentityProvider(request: { identityProvider?: IdentityProvider }): Promise<IdentityProvider> {
    const identityProvider = request.identityProvider;
    if (!identityProvider) {
      throw new Error("Missing identity provider payload");
    }
    const payload = {
      name: identityProvider.title || identityProvider.name || "identity-provider",
      type: identityProvider.type === IdentityProvider_Type.OAUTH2 ? "oauth2" : "unspecified",
    };
    const data = await fetchJson<ApiIdentityProvider>("/idp", { method: "POST", body: payload });
    return identityProviderFromApi(data);
  },
  async updateIdentityProvider(request: { identityProvider?: IdentityProvider }): Promise<IdentityProvider> {
    const identityProvider = request.identityProvider;
    if (!identityProvider?.name) {
      throw new Error("Missing identity provider name");
    }
    const idpId = parseNumericId(identityProvider.name);
    const payload = {
      name: identityProvider.title || identityProvider.name,
      config: identityProvider.config,
    };
    const data = await fetchJson<ApiIdentityProvider>(`/idp/${idpId}`, { method: "PATCH", body: payload });
    return identityProviderFromApi(data);
  },
  async deleteIdentityProvider(_request: { name: string }): Promise<void> {
    void _request;
    return;
  },
};
