import type { AxiosInstance, AxiosRequestConfig, Method } from "axios";

import { api } from "@/client";

import type { ChannelMessage } from "./types";

type HttpRoute = {
  method: Method;
  path: string;
  data?: unknown;
  params?: Record<string, unknown>;
};

const defaultBasePath = "/api/v1/ws";

export class ChannelHttpFallback {
  private readonly client: AxiosInstance;
  private readonly basePath: string;

  constructor(client: AxiosInstance = api, basePath = defaultBasePath) {
    this.client = client;
    this.basePath = basePath.replace(/\/$/, "");
  }

  async request<T>(message: ChannelMessage): Promise<T> {
    const route = this.toRoute(message);
    const config: AxiosRequestConfig = {
      method: route.method,
      url: `${this.basePath}${route.path}`,
      params: route.params,
      data: route.data,
    };
    const response = await this.client.request<T>(config);
    return response.data;
  }

  private toRoute(message: ChannelMessage): HttpRoute {
    const room = enc(message.room);
    const user = enc(message.user);
    const msg = enc(message.message);
    const id = enc(message.id);
    const workspace = enc(message.workspace);
    const threadId = enc(message.thread_id);
    const username = enc(message.username);
    const agentId = enc(message.agent_id);

    switch (message.type) {
      case "ping":
        return { method: "GET", path: "/ping" };
      case "csrf_token":
        return { method: "GET", path: "/csrf" };
      case "subscribe":
        return { method: "POST", path: `/rooms/${room}/subscribe` };
      case "unsubscribe":
        return { method: "DELETE", path: `/rooms/${room}/subscribe` };
      case "typing_start":
        return {
          method: "POST",
          path: `/rooms/${room}/typing`,
          data: { action: "Start" },
        };
      case "typing_stop":
        return {
          method: "POST",
          path: `/rooms/${room}/typing`,
          data: { action: "Stop" },
        };
      case "read_receipt":
      case "state_set_read_seq":
        return {
          method: "POST",
          path: `/rooms/${room}/read-receipt`,
          data: { last_read_seq: message.last_read_seq },
        };
      case "message_list":
        return {
          method: "GET",
          path: `/rooms/${room}/messages`,
          params: pick(message, ["before_seq", "after_seq", "limit"]),
        };
      case "message_around":
        return {
          method: "GET",
          path: `/rooms/${room}/messages/around`,
          params: pick(message, ["seq", "limit", "thread"]),
        };
      case "missed_messages":
        return {
          method: "GET",
          path: `/rooms/${room}/messages/missed`,
          params: pick(message, ["after_seq", "limit"]),
        };
      case "message_create":
        return {
          method: "POST",
          path: `/rooms/${room}/messages`,
          data: pick(message, [
            "content",
            "content_type",
            "thread",
            "in_reply_to",
          ]),
        };
      case "message_update":
        return {
          method: "PATCH",
          path: `/messages/${msg}`,
          data: { content: message.content },
        };
      case "message_revoke":
        return { method: "DELETE", path: `/messages/${msg}` };
      case "room_get":
        return { method: "GET", path: `/rooms/${room}` };
      case "room_create":
        return {
          method: "POST",
          path: "/rooms",
          data: pick(message, ["workspace", "room_name", "public", "category"]),
        };
      case "room_update":
        return {
          method: "PATCH",
          path: `/rooms/${room}`,
          data: pick(message, ["room_name", "public", "category"]),
        };
      case "room_delete":
        return { method: "DELETE", path: `/rooms/${room}` };
      case "category_create":
        return {
          method: "POST",
          path: `/workspaces/${workspace}/categories`,
          data: pick(message, ["name", "position"]),
        };
      case "category_update":
        return {
          method: "PATCH",
          path: `/categories/${id}`,
          data: pick(message, ["name", "position"]),
        };
      case "category_delete":
        return { method: "DELETE", path: `/categories/${id}` };
      case "access_grant":
        return {
          method: "POST",
          path: `/rooms/${room}/members`,
          data: { user: message.user },
        };
      case "access_revoke":
        return {
          method: "DELETE",
          path: `/rooms/${room}/members/${user}`,
        };
      case "state_update_dnd":
        return {
          method: "PATCH",
          path: `/rooms/${room}/dnd`,
          data: pick(message, [
            "do_not_disturb",
            "dnd_start_hour",
            "dnd_end_hour",
          ]),
        };
      case "reaction_add":
        return {
          method: "POST",
          path: `/rooms/${room}/reactions`,
          data: pick(message, ["message", "emoji"]),
        };
      case "reaction_remove":
        return {
          method: "DELETE",
          path: `/rooms/${room}/reactions`,
          data: pick(message, ["message", "emoji"]),
        };
      case "thread_create":
        return {
          method: "POST",
          path: `/rooms/${room}/threads`,
          data: { parent: message.parent },
        };
      case "thread_list":
        return {
          method: "GET",
          path: `/rooms/${room}/threads`,
        };
      case "thread_resolve":
        return { method: "PATCH", path: `/threads/${threadId}/resolve` };
      case "thread_archive":
        return { method: "PATCH", path: `/threads/${threadId}/archive` };
      case "pin_add":
        return {
          method: "POST",
          path: `/rooms/${room}/pins`,
          data: { message: message.message },
        };
      case "pin_remove":
        return {
          method: "DELETE",
          path: `/rooms/${room}/pins`,
          data: { message: message.message },
        };
      case "draft_save":
        return {
          method: "PUT",
          path: `/rooms/${room}/drafts`,
          data: { content: message.content },
        };
      case "draft_clear":
        return { method: "DELETE", path: `/rooms/${room}/drafts` };
      case "search":
        return {
          method: "GET",
          path: "/search",
          params: pick(message, ["q", "room", "limit", "offset"]),
        };
      case "notification_mark_read":
        return { method: "PATCH", path: `/notifications/${id}/read` };
      case "notification_mark_all_read":
        return {
          method: "POST",
          path: "/notifications/read-all",
          data: { workspace_id: message.workspace_id },
        };
      case "notification_archive":
        return { method: "DELETE", path: `/notifications/${id}` };
      case "presence_update":
        return {
          method: "POST",
          path: "/presence",
          data: { status: message.status },
        };
      case "custom_status_update":
        return {
          method: "POST",
          path: "/custom-status",
          data: pick(message, ["emoji", "text", "expires_at"]),
        };
      case "invite_create":
        return {
          method: "POST",
          path: "/invites",
          data: pick(message, ["workspace", "room", "max_uses", "expires_at"]),
        };
      case "invite_accept":
        return {
          method: "POST",
          path: "/invites/accept",
          data: { code: message.code },
        };
      case "invite_revoke":
        return { method: "DELETE", path: `/invites/${id}` };
      case "ban_create":
        return {
          method: "POST",
          path: `/workspaces/${workspace}/bans`,
          data: pick(message, ["user", "reason", "expires_at"]),
        };
      case "ban_remove":
        return {
          method: "DELETE",
          path: `/workspaces/${workspace}/bans/${user}`,
        };
      case "voice_join":
        return { method: "POST", path: `/rooms/${room}/voice/join` };
      case "voice_leave":
        return { method: "POST", path: `/rooms/${room}/voice/leave` };
      case "voice_mute":
        return {
          method: "POST",
          path: `/rooms/${room}/voice/mute`,
          data: { muted: message.muted },
        };
      case "voice_deaf":
        return {
          method: "POST",
          path: `/rooms/${room}/voice/deaf`,
          data: { deafened: message.deafened },
        };
      case "screen_share":
        return {
          method: "POST",
          path: `/rooms/${room}/screen-share`,
          data: { start: message.start },
        };
      case "ai_list":
        return { method: "GET", path: `/rooms/${room}/ai` };
      case "ai_upsert":
        return {
          method: "POST",
          path: `/rooms/${room}/ai`,
          data: { agent_session: message.model },
        };
      case "ai_delete":
        return {
          method: "DELETE",
          path: `/rooms/${room}/ai/${agentId}`,
        };
      case "ai_stop":
        return { method: "POST", path: `/rooms/${room}/ai/stop` };
      case "user_summary":
        return { method: "GET", path: `/users/summary/${username}` };
      default:
        throw new Error(`No HTTP fallback route for channel message: ${message.type}`);
    }
  }
}

function enc(value: unknown): string {
  if (value === null || value === undefined) {
    return "";
  }
  return encodeURIComponent(String(value));
}

function pick(
  source: ChannelMessage,
  keys: readonly string[],
): Record<string, unknown> {
  return keys.reduce<Record<string, unknown>>((acc, key) => {
    const value = source[key];
    if (value !== undefined) {
      acc[key] = value;
    }
    return acc;
  }, {});
}

