import { create } from "@bufbuild/protobuf";
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 { IdentityProvider_Type, type IdentityProvider } from "@/types/proto/api/v1/idp_service_pb";
import { type Memo, MemoRelation_Type, MemoRelation_MemoSchema, MemoRelationSchema, Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { type Shortcut } from "@/types/proto/api/v1/shortcut_service_pb";
import { User_Role, type User } from "@/types/proto/api/v1/user_service_pb";
import type { ApiActivity, ApiAttachment, ApiIdentityProvider, ApiMemo, ApiShortcut, ApiUser } from "./types";

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

export 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;
};

export 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;
};

export const memoFromApi = (raw: ApiMemo): Memo => {
  const content = raw.content || "";
  // Use uid if available (for URLs), fallback to id (ObjectId)
  // Route expects uid, so we prefer uid for the name
  const memoIdentifier = raw.uid || raw.id;
  const memoName = `memos/${memoIdentifier}`;

  // Create fake relations for comment count if available
  // This allows MemoView to display comment count badge
  const relations = [];
  if (raw.comment_count && raw.comment_count > 0) {
    // Create a fake relation to represent comment count
    // The MemoViewContext will count these to show comment badge
    for (let i = 0; i < raw.comment_count; i++) {
      relations.push(
        create(MemoRelationSchema, {
          memo: create(MemoRelation_MemoSchema, { name: memoName }),
          relatedMemo: create(MemoRelation_MemoSchema, { name: memoName }),
          type: MemoRelation_Type.COMMENT,
        })
      );
    }
  }

  const result: Partial<Memo> = {
    name: memoName,
    state: raw.row_status === "ARCHIVED" ? State.ARCHIVED : State.NORMAL,
    creator: `users/${raw.creator_id ?? 1}`,
    content,
    visibility: visibilityFromApi(raw.visibility),
    tags: Array.isArray(raw.tags) ? raw.tags : [],
    pinned: raw.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),
    parent: raw.parent ? (raw.parent.startsWith("memos/") ? raw.parent : `memos/${raw.parent}`) : undefined, // Include parent field if exists (for comments)
  };

  // Add workspace_id if present (workspace isolation feature)
  if (raw.workspace_id) {
    result.workspaceId = raw.workspace_id;
  }

  // Add is_read if present (inbox feature)
  if (raw.is_read !== undefined) {
    result.isRead = raw.is_read;
  }

  return result as Memo;
};

export const userFromApi = (raw: ApiUser): User => {
  const email = raw.email || `user${raw.id}@example.com`;
  // Prefer raw.username from API, fallback to email prefix
  const username = raw.username || (email.includes("@") ? email.split("@")[0] : `user${raw.id}`);
  // Prefer raw.displayName from API, fallback to raw.nickname for backwards compat
  const displayName = raw.displayName || raw.nickname || "";
  // Prefer raw.avatarUrl from API
  const avatarUrl = raw.avatarUrl || "";
  // Prefer raw.name from API (resource name format)
  const name = raw.name || `users/${raw.id}`;

  return {
    name,
    role: User_Role.USER,
    username,
    email,
    displayName,
    avatarUrl,
    description: "",
    password: "",
    state: State.NORMAL,
  } as User;
};


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

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

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

export 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: "",
  };
};

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

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

  let filtered = memos;

  // Only apply client-side filters that the backend doesn't handle
  // Backend handles: creator_id, created_ts/updated_ts (date range), tag
  // Frontend only needs to handle: content.contains

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

  // Note: creator_id and timestamp filters are handled by the backend via the filter query parameter.
  // The backend filters by creator_id and created_at/updated_at in the database,
  // so we don't need to filter again on the frontend. This avoids timezone/format mismatches
  // and ensures consistency between backend and frontend filtering.

  return filtered;
};

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

