import type { Memo, Reaction } from "@/types/proto/api/v1/memo_service_pb";
import { State } from "@/types/proto/api/v1/common_pb";
import { fetchJson, parseResourceId } from "./apiClient";
import { applyMemoFilter, extractTagFilter, memoFromApi, visibilityToApi } from "./converters";
import type { ApiMemo } from "./types";

export const memoServiceClient = {
  async listMemos(request: { filter?: string; state?: number; workspaceId?: string } = {}): Promise<{ memos: Memo[]; nextPageToken: string }> {
    const tag = extractTagFilter(request.filter);
    const params = new URLSearchParams();
    if (tag) params.append("tag", tag);
    if (request.filter) {
      console.log("DEBUG listMemos filter:", request.filter);
      params.append("filter", request.filter);
    }
    // Pass state as row_status query param for archive filtering
    if (request.state !== undefined) {
      const rowStatus = request.state === State.ARCHIVED ? "ARCHIVED" : "NORMAL";
      params.append("row_status", rowStatus);
    }
    // Workspace isolation filter
    if (request.workspaceId) {
      params.append("workspace_id", request.workspaceId);
    }
    const query = params.toString() ? `?${params.toString()}` : "";
    console.log("DEBUG listMemos URL:", `/memos${query}`);
    const data = await fetchJson<ApiMemo[]>(`/memos${query}`, { method: "GET" });
    console.log("DEBUG listMemos response count:", Array.isArray(data) ? data.length : 0);
    const memos = Array.isArray(data) ? data.map(memoFromApi) : [];
    const filtered = applyMemoFilter(memos, request.filter);
    console.log("DEBUG listMemos after filter count:", filtered.length);
    return { memos: filtered, nextPageToken: "" };
  },
  async getMemo(request: { name: string; workspaceId?: string }): Promise<Memo> {
    const memoId = parseResourceId(request.name);
    const params = new URLSearchParams();
    if (request.workspaceId) {
      params.append("workspace_id", request.workspaceId);
    }
    const query = params.toString() ? `?${params.toString()}` : "";
    const data = await fetchJson<ApiMemo>(`/memos/${memoId}${query}`, { 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: string;
      visibility: string;
      tags: string[];
      create_time?: string;
      workspace_id?: string;
      is_read?: boolean;
    } = {
      content: memo.content,
      visibility: visibilityToApi(memo.visibility),
      tags: memo.tags || [],
    };
    if (memo.workspaceId) {
      payload.workspace_id = memo.workspaceId;
    }
    if (memo.isRead !== undefined) {
      payload.is_read = memo.isRead;
    }
    if (memo.createTime) {
      payload.create_time = new Date(Number(memo.createTime.seconds) * 1000).toISOString();
    }
    const data = await fetchJson<ApiMemo>("/memos", { method: "POST", body: payload });
    return memoFromApi(data);
  },
  async updateMemo(request: { memo?: Memo; updateMask?: { paths?: string[] } }): Promise<Memo> {
    const memo = request.memo;
    if (!memo?.name) {
      throw new Error("Missing memo name");
    }
    const memoId = parseResourceId(memo.name);
    const payload: { content?: string; visibility?: string; tags?: string[]; pinned?: boolean; row_status?: string; is_read?: boolean } = {};

    // Use updateMask to determine which fields to send (prevents proto default values from overwriting data)
    const paths = request.updateMask?.paths ?? [];
    const hasMask = paths.length > 0;

    if ((!hasMask && memo.content !== undefined) || (hasMask && paths.includes("content"))) {
      payload.content = memo.content;
    }
    if ((!hasMask && memo.visibility !== undefined) || (hasMask && paths.includes("visibility"))) {
      payload.visibility = visibilityToApi(memo.visibility);
    }
    if ((!hasMask && memo.tags) || (hasMask && paths.includes("tags"))) {
      payload.tags = memo.tags;
    }
    if ((!hasMask && memo.pinned !== undefined) || (hasMask && paths.includes("pinned"))) {
      payload.pinned = memo.pinned;
    }
    if ((!hasMask && memo.state !== undefined) || (hasMask && paths.includes("state"))) {
      payload.row_status = memo.state === State.ARCHIVED ? "ARCHIVED" : "NORMAL";
    }
    const data = await fetchJson<ApiMemo>(`/memos/${memoId}`, { method: "PATCH", body: payload });
    return memoFromApi(data);
  },
  async markAsRead(request: { name: string; isRead?: boolean }): Promise<Memo> {
    const memoId = parseResourceId(request.name);
    const payload = { is_read: request.isRead !== undefined ? request.isRead : true };
    const data = await fetchJson<ApiMemo>(`/memos/${memoId}`, { method: "PATCH", body: payload });
    return memoFromApi(data);
  },
  async deleteMemo(request: { name: string }): Promise<void> {
    const memoId = parseResourceId(request.name);
    await fetchJson(`/memos/${memoId}`, { method: "DELETE" });
  },
  async createMemoComment(request: { name: string; comment?: Memo; anonymousId?: string; anonymousName?: string }): Promise<Memo> {
    if (!request.comment) {
      throw new Error("Missing comment payload");
    }
    const memoId = parseResourceId(request.name);
    const payload: {
      content: string;
      visibility: string;
      tags: string[];
      anonymous_id?: string;
      anonymous_name?: string;
    } = {
      content: request.comment.content,
      visibility: visibilityToApi(request.comment.visibility),
      tags: request.comment.tags || [],
    };
    // Add anonymous fields if provided
    if (request.anonymousId) {
      payload.anonymous_id = request.anonymousId;
    }
    if (request.anonymousName) {
      payload.anonymous_name = request.anonymousName;
    }
    const data = await fetchJson<ApiMemo>(`/memos/${memoId}/comments`, {
      method: "POST",
      body: payload,
    });
    return memoFromApi(data);
  },
  async listMemoComments(request: { name: string; anonymousId?: string }): Promise<{ memos: Memo[]; nextPageToken: string; totalSize: number }> {
    const memoId = parseResourceId(request.name);
    const url = new URL(`/memos/${memoId}/comments`, window.location.origin);
    if (request.anonymousId) {
      url.searchParams.set("anonymous_id", request.anonymousId);
    }
    const data = await fetchJson<{ memos: ApiMemo[]; nextPageToken: string; totalSize: number }>(
      url.pathname + url.search,
      { method: "GET" }
    );
    const memos = Array.isArray(data.memos) ? data.memos.map(memoFromApi) : [];
    return {
      memos,
      nextPageToken: data.nextPageToken || "",
      totalSize: data.totalSize || memos.length,
    };
  },
  async upsertMemoReaction(request: { name: string; reaction?: Partial<Reaction> }): Promise<Reaction> {
    const reactionType = request.reaction?.reactionType || "";
    const parentId = parseResourceId(request.name);
    return await fetchJson<Reaction>(
      `/memos/${parentId}/reactions`,
      { method: "POST" },
      { reactionType }
    );
  },
  async deleteMemoReaction(request: { name: string }): Promise<void> {
    const parts = request.name.split("/");
    const parentId = parts[1];
    const reactionType = parts[3];
    await fetchJson<void>(
      `/memos/${parentId}/reactions/${reactionType}`,
      { method: "DELETE" }
    );
  },
};

