import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
import {
  request as graphqlRequest,
  type RequestDocument,
  type Variables,
} from "graphql-request";

import {
  DATOCMS_API_URL,
  DATOCMS_ENV,
  DATOCMS_READ_ONLY_API_TOKEN,
} from "@/config/constants";
import {
  AllBannersDocument,
  type AllBannersQuery,
  AllNewsDocument,
  type AllNewsQuery,
  AllPagesDocument,
  AllPageSlugsDocument,
  type AllPagesQuery,
  AllPostsDocument,
  AllPostSlugsDocument,
  type AllPostsQuery,
  PageBySlugDocument,
  type PageBySlugQuery,
  PostBySlugDocument,
  type PostBySlugQuery,
  SiteMetaDocument,
  type SiteMetaQuery,
} from "@/generated/graphql";

interface DatoRequestOptions {
  includeDrafts?: boolean;
  excludeInvalid?: boolean;
}

const defaultDatoRequestOptions = {
  includeDrafts: false,
  excludeInvalid: true,
};

function datoRequest<TDocument = any, TVariables = Record<string, any>>(
  document: RequestDocument | TypedDocumentNode<TDocument, Variables>,
  variables?: Variables,
  options: DatoRequestOptions = defaultDatoRequestOptions,
) {
  const { includeDrafts, excludeInvalid } = options;

  return graphqlRequest<TDocument, Variables>(
    DATOCMS_API_URL,
    document,
    variables,
    {
      Authorization: DATOCMS_READ_ONLY_API_TOKEN,
      ...(includeDrafts ? { "X-Include-Drafts": "true" } : {}),
      ...(excludeInvalid ? { "X-Exclude-Invalid": "true" } : {}),
      ...(DATOCMS_ENV ? { "X-Environment": DATOCMS_ENV } : {}),
    },
  );
}

/**
 * Pages
 * ===================================================================
 */

export async function getAllPages(): Promise<AllPagesQuery["allPages"]> {
  const data = await datoRequest(AllPagesDocument);
  return data.allPages.filter((page) => page !== null && page !== undefined);
}

export async function getAllPagesSlugs(): Promise<string[]> {
  const data = await datoRequest(AllPageSlugsDocument);
  return data.allPages.map((page) => page.slug);
}

export async function getPageBySlug(
  slug: string,
): Promise<PageBySlugQuery["page"]> {
  const data = await datoRequest(PageBySlugDocument, { slug });
  return data.page;
}

/**
 * Articles: Posts
 * ===================================================================
 */

export async function getAllPosts(
  options: DatoRequestOptions = defaultDatoRequestOptions,
): Promise<AllPostsQuery["allPosts"]> {
  const data = await datoRequest(AllPostsDocument, undefined, options);
  return data.allPosts.filter((post) => post !== null && post !== undefined);
}

export async function getAllPostsSlugs(
  options: DatoRequestOptions = defaultDatoRequestOptions,
): Promise<string[]> {
  const data = await datoRequest(AllPostSlugsDocument, undefined, options);
  return data.allPosts.map((post) => post.slug);
}

export async function getPostBySlug(
  slug: string,
  options: DatoRequestOptions = defaultDatoRequestOptions,
): Promise<PostBySlugQuery["post"]> {
  const data = await datoRequest(PostBySlugDocument, { slug }, options);
  return data.post;
}

/**
 * Articles: News
 * ===================================================================
 */

export async function getAllNews(
  options: DatoRequestOptions = defaultDatoRequestOptions,
): Promise<AllNewsQuery["allNews"]> {
  const data = await datoRequest(AllNewsDocument, undefined, options);
  return data.allNews.filter((news) => news !== null && news !== undefined);
}

/**
 * Site Globals
 * ===================================================================
 */

export async function getGlobalSiteMeta(): Promise<SiteMetaQuery["site"]> {
  const data = await datoRequest(SiteMetaDocument);
  return data.site;
}

/**
 * Banners
 * ===================================================================
 */

export async function getAllBanners(
  options: DatoRequestOptions = defaultDatoRequestOptions,
): Promise<AllBannersQuery["allBanners"]> {
  const data = await datoRequest(AllBannersDocument, undefined, options);
  return data.allBanners.filter(
    (banners) => banners !== null && banners !== undefined,
  );
}
