import type {
  Translation,
  TranslationFormData,
  TranslationListData,
  TranslationModule,
  TranslationPageOption,
} from "../types/translation-types";

const API_BASE_URL =
  process.env.NEXT_PUBLIC_API_URL?.replace(
    /\/$/,
    "",
  ) ||
  "http://localhost:3001/api/v1";

type ApiEnvelope<T> = {
  success?: boolean;
  statusCode?: number;
  message?:
    | string
    | string[];
  data: T;
};

type NestedApiEnvelope<T> = {
  success?: boolean;
  statusCode?: number;
  message?:
    | string
    | string[];
  data: {
    success?: boolean;
    message?:
      | string
      | string[];
    data: T;
  };
};

function getAccessToken():
  | string
  | null {
  if (
    typeof window ===
    "undefined"
  ) {
    return null;
  }

  return (
    window.localStorage.getItem(
      "accessToken",
    ) ||
    window.localStorage.getItem(
      "access_token",
    ) ||
    window.localStorage.getItem(
      "token",
    )
  );
}

function readMessage(
  value: unknown,
): string | null {
  if (
    typeof value ===
      "string" &&
    value.trim()
  ) {
    return value;
  }

  if (
    Array.isArray(value)
  ) {
    const messages =
      value
        .map((item) =>
          readMessage(item),
        )
        .filter(
          (
            item,
          ): item is string =>
            Boolean(item),
        );

    return messages.length
      ? messages.join(", ")
      : null;
  }

  if (
    typeof value ===
      "object" &&
    value !== null
  ) {
    const record =
      value as Record<
        string,
        unknown
      >;

    for (const key of [
      "message",
      "messages",
      "error",
      "errors",
      "detail",
      "details",
    ]) {
      const message =
        readMessage(
          record[key],
        );

      if (
        message &&
        message.toLowerCase() !==
          "bad request"
      ) {
        return message;
      }
    }

    if ("data" in record) {
      const nested =
        readMessage(
          record.data,
        );

      if (nested) {
        return nested;
      }
    }
  }

  return null;
}

async function getErrorMessage(
  response: Response,
): Promise<string> {
  try {
    const body =
      (await response.json()) as unknown;

    const message =
      readMessage(body);

    if (message) {
      return message;
    }
  } catch {
    // Use fallback below.
  }

  return `Request failed with status ${response.status}`;
}

async function apiFetch(
  url: string,
  options?: RequestInit,
): Promise<Response> {
  const token =
    getAccessToken();

  const response =
    await fetch(url, {
      ...options,
      credentials:
        "include",
      cache: "no-store",
      headers: {
        "Content-Type":
          "application/json",
        ...(token
          ? {
              Authorization:
                `Bearer ${token}`,
            }
          : {}),
        ...options?.headers,
      },
    });

  if (!response.ok) {
    throw new Error(
      await getErrorMessage(
        response,
      ),
    );
  }

  return response;
}

function unwrapData<T>(
  body:
    | ApiEnvelope<T>
    | NestedApiEnvelope<T>,
): T {
  const outerData =
    body.data;

  if (
    typeof outerData ===
      "object" &&
    outerData !== null &&
    "data" in outerData
  ) {
    return (
      outerData as {
        data: T;
      }
    ).data;
  }

  return outerData as T;
}

export async function getTranslations(
  params?: {
    page?: number;
    limit?: number;
    search?: string;
    module?: string;
    pageKey?: string;
    isActive?: boolean;
  },
): Promise<TranslationListData> {
  const query =
    new URLSearchParams();

  query.set(
    "pageNumber",
    String(
      params?.page ?? 1,
    ),
  );

  query.set(
    "limit",
    String(
      params?.limit ?? 10,
    ),
  );

  if (params?.search) {
    query.set(
      "search",
      params.search,
    );
  }

  if (params?.module) {
    query.set(
      "module",
      params.module,
    );
  }

  if (params?.pageKey) {
    query.set(
      "pageKey",
      params.pageKey,
    );
  }

  if (
    params?.isActive !==
    undefined
  ) {
    query.set(
      "isActive",
      String(
        params.isActive,
      ),
    );
  }

  const response =
    await apiFetch(
      `${API_BASE_URL}/translations?${query.toString()}`,
    );

  const body =
    (await response.json()) as
      | ApiEnvelope<TranslationListData>
      | NestedApiEnvelope<TranslationListData>;

  return unwrapData(body);
}

export async function getTranslationPages(
  module: TranslationModule,
): Promise<TranslationPageOption[]> {
  const query =
    new URLSearchParams({
      module,
    });

  const response =
    await apiFetch(
      `${API_BASE_URL}/translations/pages?${query.toString()}`,
    );

  type PagesData = {
    module: string;
    items:
      TranslationPageOption[];
  };

  const body =
    (await response.json()) as
      | ApiEnvelope<PagesData>
      | NestedApiEnvelope<PagesData>;

  return unwrapData(
    body,
  ).items;
}

export async function createTranslation(
  data: TranslationFormData,
): Promise<Translation> {
  const response =
    await apiFetch(
      `${API_BASE_URL}/translations`,
      {
        method: "POST",
        body: JSON.stringify(
          data,
        ),
      },
    );

  const body =
    (await response.json()) as
      | ApiEnvelope<Translation>
      | NestedApiEnvelope<Translation>;

  return unwrapData(body);
}

export async function updateTranslation(
  id: number,
  data: Partial<TranslationFormData>,
): Promise<Translation> {
  const response =
    await apiFetch(
      `${API_BASE_URL}/translations/${id}`,
      {
        method: "PATCH",
        body: JSON.stringify(
          data,
        ),
      },
    );

  const body =
    (await response.json()) as
      | ApiEnvelope<Translation>
      | NestedApiEnvelope<Translation>;

  return unwrapData(body);
}

export async function deleteTranslation(
  id: number,
): Promise<Translation> {
  const response =
    await apiFetch(
      `${API_BASE_URL}/translations/${id}`,
      {
        method: "DELETE",
      },
    );

  const body =
    (await response.json()) as
      | ApiEnvelope<Translation>
      | NestedApiEnvelope<Translation>;

  return unwrapData(body);
}

export async function enableTranslation(
  id: number,
): Promise<Translation> {
  const response =
    await apiFetch(
      `${API_BASE_URL}/translations/${id}/enable`,
      {
        method: "PATCH",
      },
    );

  const body =
    (await response.json()) as
      | ApiEnvelope<Translation>
      | NestedApiEnvelope<Translation>;

  return unwrapData(body);
}