import axios, { type AxiosResponse } from "axios";
import pino from "pino";

import { SPARK_BACKEND_BASE_URL } from "@/global/storeConfig";
import type {
  AppReview,
  AppReviewReply,
  AuthSession,
  DownloadedAppList,
  DownloadedAppRecord,
  FavoriteFolder,
  FavoriteItem,
  RatingSummary,
  ReviewTags,
  SparkUser,
  SyncedAppList,
  SyncedAppListItem,
} from "@/global/typedefinition";

const backend = axios.create({
  baseURL: SPARK_BACKEND_BASE_URL,
  timeout: 10000,
});
const logger = pino({ name: "backendApi" });

type ApiRecord = Record<string, unknown>;

const normalizeBackendAuthError = (error: unknown): Error => {
  if (!axios.isAxiosError(error)) {
    return error instanceof Error ? error : new Error("登录失败，请稍后重试。");
  }

  logger.error(
    {
      code: error.code,
      message: error.message,
      status: error.response?.status,
    },
    "Spark backend auth exchange failed",
  );

  if (!error.response) {
    return new Error("无法连接星火账号服务，请确认后端服务已启动或稍后重试。");
  }

  const status = error.response.status;
  if (status === 401) {
    return new Error("论坛登录失败，请检查账号和密码。");
  }
  if (status === 503) {
    return new Error("星火账号服务暂时无法连接论坛，请稍后重试。");
  }
  if (status === 500) {
    return new Error("星火账号服务异常，请确认后端数据库迁移已执行后重试。");
  }

  return new Error(`星火账号服务返回异常 (${status})，请稍后重试。`);
};

const normalizeBackendMutationError = (error: unknown): Error => {
  if (!axios.isAxiosError(error)) {
    return error instanceof Error ? error : new Error("操作失败，请稍后重试。");
  }

  logger.error(
    {
      code: error.code,
      message: error.message,
      status: error.response?.status,
    },
    "Spark backend mutation failed",
  );

  if (!error.response) {
    return new Error("无法连接星火账号服务，请稍后重试。");
  }

  const status = error.response.status;
  if (status === 401) {
    return new Error("登录状态已失效，请重新登录星火账号。");
  }
  if (status === 403) {
    return new Error("请登录星火账号后重试。");
  }
  if (status === 422) {
    return new Error("提交内容格式不正确，请检查后重试。");
  }
  if (status >= 500) {
    return new Error("星火账号服务异常，请稍后重试。");
  }

  return new Error(`星火账号服务返回异常 (${status})，请稍后重试。`);
};

const asApiRecord = (value: unknown): ApiRecord => {
  if (value && typeof value === "object" && !Array.isArray(value)) {
    return value as ApiRecord;
  }
  return {};
};

const asApiRecordArray = (value: unknown): ApiRecord[] => {
  if (!Array.isArray(value)) return [];
  return value.map(asApiRecord);
};

export const parseForumGroups = (raw: unknown): string[] => {
  if (Array.isArray(raw)) {
    return raw.filter((item): item is string => typeof item === "string");
  }
  if (typeof raw !== "string" || raw.length === 0) return [];

  try {
    const parsed: unknown = JSON.parse(raw);
    return Array.isArray(parsed)
      ? parsed.filter((item): item is string => typeof item === "string")
      : [];
  } catch {
    return [];
  }
};

const toUser = (raw: ApiRecord): SparkUser => ({
  id: Number(raw.id),
  flarumUserId: String(raw.flarum_user_id || ""),
  username: String(raw.username || ""),
  displayName: String(raw.display_name || raw.username || ""),
  avatarUrl: String(raw.avatar_url || ""),
  coverUrl: String(raw.cover_url || raw.coverUrl || "") || undefined,
  forumLevel: String(raw.forum_level || "论坛用户"),
  forumGroups: parseForumGroups(raw.forum_groups),
});

const toReview = (raw: ApiRecord): AppReview => ({
  id: Number(raw.id),
  userId: raw.user_id === undefined ? undefined : Number(raw.user_id),
  rating: Number(raw.rating),
  content: String(raw.content || ""),
  version: String(raw.version || "unknown"),
  packageArch: String(raw.package_arch || "unknown"),
  clientArch: String(raw.client_arch || "unknown"),
  distro: String(raw.distro || "unknown"),
  origin: raw.origin === "spark" ? "spark" : "apm",
  category: String(raw.category || ""),
  createdAt: String(raw.created_at || ""),
  updatedAt: String(raw.updated_at || ""),
  userDisplayName: String(raw.user_display_name || ""),
  userAvatarUrl: String(raw.user_avatar_url || ""),
  likeCount: Number(raw.like_count || 0),
  likedByCurrentUser: Boolean(raw.liked_by_current_user),
  canDelete: raw.can_delete === undefined ? undefined : Boolean(raw.can_delete),
  isAuthor: raw.is_author === undefined ? undefined : Boolean(raw.is_author),
  isDeleted: Boolean(raw.is_deleted),
  replies: asApiRecordArray(raw.replies).map(toReviewReply),
});

const toReviewReply = (raw: ApiRecord): AppReviewReply => ({
  id: Number(raw.id),
  reviewId: Number(raw.review_id),
  parentId:
    raw.parent_id === null || raw.parent_id === undefined
      ? null
      : Number(raw.parent_id),
  content: String(raw.content || ""),
  createdAt: String(raw.created_at || ""),
  updatedAt: String(raw.updated_at || ""),
  userDisplayName: String(raw.user_display_name || ""),
  userAvatarUrl: String(raw.user_avatar_url || ""),
  likeCount: Number(raw.like_count || 0),
  likedByCurrentUser: Boolean(raw.liked_by_current_user),
  canDelete: Boolean(raw.can_delete),
  isAuthor: Boolean(raw.is_author),
  isDeleted: Boolean(raw.is_deleted),
  replies: asApiRecordArray(raw.replies).map(toReviewReply),
});

export interface ReviewActionState {
  likedByCurrentUser: boolean;
  likeCount: number;
}

export interface CreateReviewReplyPayload {
  content: string;
  parentId?: number;
}

const toReviewActionState = (raw: ApiRecord): ReviewActionState => ({
  likedByCurrentUser: Boolean(raw.liked_by_current_user),
  likeCount: Number(raw.like_count || 0),
});

const toFavoriteFolder = (raw: ApiRecord): FavoriteFolder => ({
  id: Number(raw.id),
  name: String(raw.name || ""),
  itemCount: Number(raw.item_count || 0),
  createdAt: String(raw.created_at || ""),
  updatedAt: String(raw.updated_at || ""),
});

const toFavoriteItem = (raw: ApiRecord): FavoriteItem => ({
  id: Number(raw.id),
  appKey: String(raw.app_key || ""),
  pkgname: String(raw.pkgname || ""),
  name: String(raw.name || ""),
  category: String(raw.category || ""),
  iconUrl: String(raw.icon_url || ""),
  createdAt: String(raw.created_at || ""),
});

const toDownloadedApp = (raw: ApiRecord): DownloadedAppRecord => ({
  id: Number(raw.id),
  appKey: String(raw.app_key || ""),
  pkgname: String(raw.pkgname || ""),
  name: String(raw.name || ""),
  category: String(raw.category || ""),
  selectedOrigin: raw.selected_origin === "spark" ? "spark" : "apm",
  version: String(raw.version || ""),
  packageArch: String(raw.package_arch || "unknown"),
  downloadedAt: String(raw.downloaded_at || ""),
});

const toSyncedAppListItem = (raw: ApiRecord): SyncedAppListItem => ({
  id: raw.id === undefined ? undefined : Number(raw.id),
  pkgname: String(raw.pkgname || ""),
  origin: raw.origin === "spark" ? "spark" : "apm",
  category: String(raw.category || ""),
  version: String(raw.version || ""),
  packageArch: String(raw.package_arch || "unknown"),
  appName: String(raw.app_name || ""),
  iconUrl: String(raw.icon_url || ""),
});

const toSyncedAppList = (
  raw: ApiRecord,
  fallback?: { clientArch: string; distro: string; items: SyncedAppListItem[] },
): SyncedAppList => ({
  snapshotName: String(raw.snapshot_name || "默认列表"),
  clientArch: String(raw.client_arch || fallback?.clientArch || "unknown"),
  distro: String(raw.distro || fallback?.distro || "unknown"),
  updatedAt: String(raw.updated_at || ""),
  items: raw.items
    ? asApiRecordArray(raw.items).map(toSyncedAppListItem)
    : fallback?.items || [],
});

export const setBackendToken = (token: string | null): void => {
  const backendWithOptionalDefaults = backend as typeof backend & {
    defaults?: { headers?: { common?: Record<string, unknown> } };
  };
  const commonHeaders = backendWithOptionalDefaults.defaults?.headers?.common;
  if (!commonHeaders) return;

  if (token) commonHeaders.Authorization = `Bearer ${token}`;
  else delete commonHeaders.Authorization;
};

export const exchangeFlarumToken = async (payload: {
  flarumUserId: string;
  flarumToken: string;
}): Promise<AuthSession> => {
  let response: AxiosResponse;
  try {
    response = await backend.post("/auth/flarum", {
      flarum_user_id: payload.flarumUserId,
      flarum_token: payload.flarumToken,
    });
  } catch (error) {
    throw normalizeBackendAuthError(error);
  }
  const data = asApiRecord(response.data);

  return {
    accessToken: String(data.access_token || ""),
    tokenType: "bearer",
    user: toUser(asApiRecord(data.user)),
  };
};

export const fetchMe = async (): Promise<SparkUser> => {
  const response = await backend.get("/me");
  return toUser(asApiRecord(response.data));
};

export const fetchRatingSummary = async (
  appKey: string,
): Promise<RatingSummary> => {
  const response = await backend.get(
    `/apps/${encodeURIComponent(appKey)}/rating-summary`,
  );
  const data = asApiRecord(response.data);

  return {
    averageRating: Number(data.average_rating || 0),
    reviewCount: Number(data.review_count || 0),
    starCounts: Object.fromEntries(
      Object.entries(asApiRecord(data.star_counts)).map(([key, value]) => [
        Number(key),
        Number(value),
      ]),
    ),
  };
};

export const fetchReviews = async (appKey: string): Promise<AppReview[]> => {
  const response = await backend.get(
    `/apps/${encodeURIComponent(appKey)}/reviews`,
  );
  return asApiRecordArray(response.data).map(toReview);
};

export const submitReview = async (
  appKey: string,
  payload: { rating: number; content: string; tags: ReviewTags },
): Promise<AppReview> => {
  let response: AxiosResponse;
  try {
    response = await backend.post(
      `/apps/${encodeURIComponent(appKey)}/reviews`,
      {
        rating: payload.rating,
        content: payload.content,
        tags: {
          origin: payload.tags.origin,
          category: payload.tags.category,
          pkgname: payload.tags.pkgname,
          version: payload.tags.version,
          package_arch: payload.tags.packageArch,
          client_arch: payload.tags.clientArch,
          distro: payload.tags.distro,
        },
      },
    );
  } catch (error) {
    throw normalizeBackendMutationError(error);
  }
  return toReview(asApiRecord(response.data));
};

export const likeReview = async (
  appKey: string,
  reviewId: number,
): Promise<ReviewActionState> => {
  let response: AxiosResponse;
  try {
    response = await backend.post(
      `/apps/${encodeURIComponent(appKey)}/reviews/${reviewId}/like`,
    );
  } catch (error) {
    throw normalizeBackendMutationError(error);
  }
  return toReviewActionState(asApiRecord(response.data));
};

export const createReviewReply = async (
  appKey: string,
  reviewId: number,
  payload: CreateReviewReplyPayload,
): Promise<AppReviewReply> => {
  let response: AxiosResponse;
  try {
    response = await backend.post(
      `/apps/${encodeURIComponent(appKey)}/reviews/${reviewId}/replies`,
      {
        content: payload.content,
        ...(payload.parentId === undefined
          ? {}
          : { parent_id: payload.parentId }),
      },
    );
  } catch (error) {
    throw normalizeBackendMutationError(error);
  }
  return toReviewReply(asApiRecord(response.data));
};

export const deleteReview = async (
  appKey: string,
  reviewId: number,
): Promise<void> => {
  try {
    await backend.delete(
      `/apps/${encodeURIComponent(appKey)}/reviews/${reviewId}`,
    );
  } catch (error) {
    throw normalizeBackendMutationError(error);
  }
};

export const likeReviewReply = async (
  appKey: string,
  reviewId: number,
  replyId: number,
): Promise<ReviewActionState> => {
  let response: AxiosResponse;
  try {
    response = await backend.post(
      `/apps/${encodeURIComponent(appKey)}/reviews/${reviewId}/replies/${replyId}/like`,
    );
  } catch (error) {
    throw normalizeBackendMutationError(error);
  }
  return toReviewActionState(asApiRecord(response.data));
};

export const deleteReviewReply = async (
  appKey: string,
  reviewId: number,
  replyId: number,
): Promise<void> => {
  try {
    await backend.delete(
      `/apps/${encodeURIComponent(appKey)}/reviews/${reviewId}/replies/${replyId}`,
    );
  } catch (error) {
    throw normalizeBackendMutationError(error);
  }
};

export const listFavoriteFolders = async (): Promise<FavoriteFolder[]> => {
  const response = await backend.get("/me/favorite-folders");
  return asApiRecordArray(response.data).map(toFavoriteFolder);
};

export const createFavoriteFolder = async (
  name: string,
): Promise<FavoriteFolder> => {
  const response = await backend.post("/me/favorite-folders", { name });
  return toFavoriteFolder(asApiRecord(response.data));
};

export const renameFavoriteFolder = async (
  folderId: number,
  name: string,
): Promise<FavoriteFolder> => {
  const response = await backend.patch(`/me/favorite-folders/${folderId}`, {
    name,
  });
  return toFavoriteFolder(asApiRecord(response.data));
};

export const deleteFavoriteFolder = async (folderId: number): Promise<void> => {
  await backend.delete(`/me/favorite-folders/${folderId}`);
};

export const listFavoriteItems = async (
  folderId: number,
): Promise<FavoriteItem[]> => {
  const response = await backend.get(`/me/favorite-folders/${folderId}/items`);
  return asApiRecordArray(response.data).map(toFavoriteItem);
};

export const addFavoriteItem = async (
  folderId: number | "default",
  item: Omit<FavoriteItem, "id" | "createdAt">,
): Promise<FavoriteItem> => {
  const response = await backend.post(
    `/me/favorite-folders/${folderId}/items`,
    {
      app_key: item.appKey,
      pkgname: item.pkgname,
      name: item.name,
      category: item.category,
      icon_url: item.iconUrl,
    },
  );
  return toFavoriteItem(asApiRecord(response.data));
};

export const deleteFavoriteItem = async (
  folderId: number,
  itemId: number,
): Promise<void> => {
  await backend.delete(`/me/favorite-folders/${folderId}/items/${itemId}`);
};

export const bulkDeleteFavoriteItems = async (
  folderId: number,
  itemIds: number[],
): Promise<number> => {
  const response = await backend.post(
    `/me/favorite-folders/${folderId}/items/bulk-delete`,
    { item_ids: itemIds },
  );
  return Number(asApiRecord(response.data).deleted_count || 0);
};

export const listDownloadedApps = async (
  page = 1,
  pageSize = 20,
): Promise<DownloadedAppList> => {
  const response = await backend.get("/me/downloaded-apps", {
    params: { page, page_size: pageSize },
  });
  const data = asApiRecord(response.data);

  return {
    items: asApiRecordArray(data.items).map(toDownloadedApp),
    total: Number(data.total || 0),
    page: Number(data.page || page),
    pageSize: Number(data.page_size || pageSize),
  };
};

export const recordDownloadedApp = async (
  item: Omit<DownloadedAppRecord, "id" | "downloadedAt">,
): Promise<DownloadedAppRecord> => {
  const response = await backend.post("/me/downloaded-apps", {
    app_key: item.appKey,
    pkgname: item.pkgname,
    name: item.name,
    category: item.category,
    selected_origin: item.selectedOrigin,
    version: item.version,
    package_arch: item.packageArch,
  });
  return toDownloadedApp(asApiRecord(response.data));
};

export const fetchSyncedAppList = async (): Promise<SyncedAppList | null> => {
  const response = await backend.get("/me/app-list");
  if (!response.data) return null;
  return toSyncedAppList(asApiRecord(response.data));
};

export const uploadSyncedAppList = async (payload: {
  clientArch: string;
  distro: string;
  items: SyncedAppListItem[];
}): Promise<SyncedAppList> => {
  const response = await backend.put("/me/app-list", {
    client_arch: payload.clientArch,
    distro: payload.distro,
    items: payload.items.map((item) => ({
      pkgname: item.pkgname,
      origin: item.origin,
      category: item.category,
      version: item.version,
      package_arch: item.packageArch,
      app_name: item.appName,
      icon_url: item.iconUrl,
    })),
  });

  return toSyncedAppList(asApiRecord(response.data), payload);
};
