import type { AsyncAPIObject, TagObject } from '@/types';
import { stringify } from 'yaml';
import {
  type GeneratedPageProps,
  getPageProps,
  type OperationItem,
  type OperationOutput,
  type PageOutput,
} from '@/utils/pages/builder';
import type { InternalAsyncAPIMeta } from '@/server';
import { toStaticData } from '@/utils/pages/to-static-data';
import { doubleQuote } from '@/utils/string-utils';

export interface PagesToTextOptions {
  imports?: {
    names: string[];
    from: string;
  }[];
  frontmatter?: (
    title: string,
    description: string | undefined,
    context: DocumentContext,
  ) => Record<string, unknown>;
  includeDescription?: boolean;
  addGeneratedComment?: boolean | string;
}

export function toText(
  entry: PageOutput | OperationOutput,
  doc: AsyncAPIObject,
  options: PagesToTextOptions = {},
) {
  const { frontmatter, includeDescription = false } = options;
  const extend = frontmatter?.(
    entry.info.title,
    entry.info.description,
    entry.type === 'page'
      ? entry.tag
        ? { type: 'tag', tag: entry.tag }
        : { type: 'file' }
      : { type: 'operation' },
  );
  const pageProps = getPageProps(entry);
  if (!includeDescription) {
    pageProps.showDescription = false;
  }

  let meta: InternalAsyncAPIMeta = {
    preload: [entry.schemaId],
  };
  if (entry.type === 'operation') {
    meta.action = entry.item.action;
    meta.deprecated = entry.info.deprecated;
  }

  const data = toStaticData(pageProps, doc);

  return generateDocument(
    {
      title: entry.info.title,
      description: !includeDescription ? entry.info.description : undefined,
      full: true,
      ...extend,
      _asyncapi: {
        ...meta,
        ...data,
        ...(extend?._asyncapi as object | undefined),
      },
    },
    pageContent(pageProps),
    options,
  );
}

export function generateDocument(
  frontmatter: unknown,
  content: string,
  options: PagesToTextOptions,
): string {
  const { addGeneratedComment = true, imports } = options;
  const out: string[] = [];
  const banner = stringify(frontmatter, {
    compat: 'yaml-1.1',
    singleQuote: true,
  }).trimEnd();
  if (banner.length > 0) out.push(`---\n${banner}\n---`);

  if (addGeneratedComment) {
    let commentContent =
      'This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again.';

    if (typeof addGeneratedComment === 'string') {
      commentContent = addGeneratedComment;
    }

    commentContent = commentContent.replaceAll('/', '\\/');
    out.push(`{/* ${commentContent} */}`);
  }

  if (imports) {
    out.push(
      ...imports
        .map((item) => `import { ${item.names.join(', ')} } from ${doubleQuote(item.from)};`)
        .join('\n'),
    );
  }

  out.push(content);
  return out.join('\n\n');
}

export type DocumentContext =
  | {
      type: 'tag';
      tag: TagObject;
    }
  | {
      type: 'operation';
    }
  | {
      type: 'file';
    };

function pageContent({
  showTitle,
  showDescription,
  document,
  operations,
}: GeneratedPageProps): string {
  const propStrs: string[] = [`document=${doubleQuote(document)}`];

  if (operations) {
    propStrs.push(
      `operations={${JSON.stringify(
        operations.map(
          (item) =>
            ({
              id: item.id,
              action: item.action,
            }) satisfies OperationItem,
        ),
      )}}`,
    );
  }
  if (showTitle) {
    propStrs.push(`showTitle`);
  }
  if (showDescription) {
    propStrs.push(`showDescription`);
  }

  return `<AsyncAPIPage ${propStrs.join(' ')} />`;
}
