import type { UnnormalizedTypeDefPointer } from "@graphql-tools/load";
import type { BaseLoaderOptions } from "@graphql-tools/utils";

import type { DirectiveName, GraphQLDirective, GraphQLSchema } from "./graphql";

import type { CustomDirective } from "./helpers";

import type { Maybe, MDXString } from "./utils";
import type { AdmonitionType, Badge, TypeLink } from "./printer";
import type {
  DiffCheckHook,
  GenerateIndexMetafileHook,
  RenderFilesHook,
  RenderHomepageHook,
  RenderRootTypesHook,
  RenderTypeEntitiesHook,
  SchemaLoadHook,
} from "./event";

/**
 * Core type definitions for configuration and document generation.
 * Contains types for configuring documentation generation, rendering, and formatting.
 *
 * @packageDocumentation
 */

/**
 * Front matter options for documentation pages.
 * Can be a record of key-value pairs or false to disable front matter.
 */
export type FrontMatterOptions = Record<string, unknown> | false;

/**
 * Function type for generating index metafiles.
 * Creates index files for documentation categories.
 *
 * @param dirPath - Directory path where the index should be created
 * @param category - Name of the category
 * @param options - Optional configuration for index generation
 */
export type GenerateIndexMetafileType = (
  dirPath: string,
  category: string,
  options?: Record<string, unknown>,
) => Promise<void> | void;

/**
 * Options for collapsible sections in documentation.
 * Controls the open/close states of collapsible content.
 */
export interface CollapsibleOption {
  /** Text shown when section is open */
  dataOpen: string;
  /** Text shown when section is closed */
  dataClose: string;
}

/**
 * MDX rendering support configuration.
 * Defines functions for formatting different MDX components.
 */
export interface MDXSupportType {
  generateIndexMetafile: GenerateIndexMetafileType;
  formatMDXAdmonition: (
    { text, title, type, icon }: AdmonitionType,
    meta: Maybe<MetaInfo>,
  ) => MDXString;
  formatMDXBadge: ({ text, classname }: Badge) => MDXString;
  formatMDXBullet: (text?: string) => MDXString;
  formatMDXDetails: (option: CollapsibleOption) => MDXString;
  formatMDXLink: (link: TypeLink) => TypeLink;
  formatMDXNameEntity: (name: string, parentType?: Maybe<string>) => MDXString;
  formatMDXSpecifiedByLink: (url: string) => MDXString;
  formatMDXFrontmatter: (
    props: Maybe<FrontMatterOptions>,
    formatted: Maybe<string[]>,
  ) => MDXString;
  mdxDeclaration: string;
  // Event hooks
  beforeSchemaLoadHook?: SchemaLoadHook;
  afterSchemaLoadHook?: SchemaLoadHook;
  beforeDiffCheckHook?: DiffCheckHook;
  afterDiffCheckHook?: DiffCheckHook;
  beforeRenderRootTypesHook?: RenderRootTypesHook;
  afterRenderRootTypesHook?: RenderRootTypesHook;
  beforeRenderHomepageHook?: RenderHomepageHook;
  afterRenderHomepageHook?: RenderHomepageHook;
  beforeRenderTypeEntitiesHook?: RenderTypeEntitiesHook;
  afterRenderTypeEntitiesHook?: RenderTypeEntitiesHook;
  beforeGenerateIndexMetafileHook?: GenerateIndexMetafileHook;
  afterGenerateIndexMetafileHook?: GenerateIndexMetafileHook;
  afterRenderFilesHook?: RenderFilesHook;
}

/**
 * Configuration for grouping API documentation sections.
 * Allows customizing folder names for operations and types.
 */
export interface ApiGroupOverrideType {
  /** Custom folder name for operations (queries/mutations) */
  operations?: string;
  /** Custom folder name for type definitions */
  types?: string;
}

/**
 * Category sorting function type.
 * A compare function similar to Array.sort() that takes two category names
 * and returns a number indicating their relative order.
 */
export type CategorySortFn = (a: string, b: string) => number;

/**
 * Core documentation generation configuration.
 * Controls front matter, indexing and framework metadata.
 */
export interface ConfigDocOptions {
  /** Front matter configuration or false to disable */
  frontMatter?: Maybe<FrontMatterOptions>;
  /** Whether to generate index files */
  index?: boolean;
  /** Name of the documentation generator framework */
  generatorFrameworkName?: Maybe<string>;
  /** Version of the documentation generator framework */
  generatorFrameworkVersion?: Maybe<string>;
  /**
   * Category sorting: "natural" for alphabetical or custom compare function.
   * When enabled, folder names are automatically prefixed with order numbers (e.g., 01-objects, 02-queries).
   */
  categorySort?: CategorySortFn | "natural";
  /**
   * Whether to use section header IDs for generating permalinks using format \{#id\}.
   * If true, links will be generated with IDs that can be used as anchors in the documentation.
   * This is useful for enabling direct linking to specific sections of the documentation.
   */
  sectionHeaderId?: boolean;
}

/**
 * Valid hierarchy values for organizing documentation.
 * Can be "api" for API-based, "entity" for entity-based, or "flat" for no hierarchy.
 */
export type TypeHierarchyValueType = "api" | "entity" | "flat";

/**
 * Custom options for each hierarchy type.
 * Allows configuring how each hierarchy organizes types.
 */
export type TypeHierarchyTypeOptions = Record<string, unknown>;

/**
 * Complete hierarchy configuration object.
 * Maps hierarchy types to their options.
 */
export type TypeHierarchyObjectType = Partial<
  Record<TypeHierarchyValueType, TypeHierarchyTypeOptions>
>;

export type TypeHierarchyType =
  | TypeHierarchyObjectType
  | TypeHierarchyValueType;

export type RendererDocOptions = ConfigDocOptions & {
  /** How to handle deprecated items */
  deprecated?: Maybe<TypeDeprecatedOption>;
} & {
  /** Whether to force generation */
  force?: boolean;
  /** Type hierarchy configuration */
  hierarchy?: Maybe<TypeHierarchyObjectType>;
};

/** Flag for API group setting */
export type UseApiGroupOptionType = ApiGroupOverrideType | boolean;

/**
 * Options for handling deprecated schema items.
 * Controls how deprecated items are displayed in docs.
 */
export type TypeDeprecatedOption = "default" | "group" | "skip";

/**
 * Function for parsing directive examples.
 * Processes example values from directives.
 */
export type DirectiveExampleParserFunction = (
  value?: unknown,
  type?: unknown,
) => unknown;

/**
 * Configuration for directive examples.
 * Specifies how directive examples should be rendered.
 */
export interface TypeDirectiveExample {
  /** The directive being exemplified */
  directive: GraphQLDirective;
  /** Field the example applies to */
  field: string;
  /** Optional custom parser for the example */
  parser?: DirectiveExampleParserFunction;
}

/**
 * Example section configuration options.
 * Controls how directive examples are displayed in documentation.
 */
export type TypeExampleSectionOption = Partial<
  Omit<TypeDirectiveExample, "directive"> & { directive: string }
>;

export type DiffMethodName = string & { _opaque: typeof DIFF_METHOD_NAME };
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in opaque type pattern
declare const DIFF_METHOD_NAME: unique symbol;
export type TypeDiffMethod = DiffMethodName | "FORCE" | "NONE";

export type Pointer = UnnormalizedTypeDefPointer;

export interface ConfigOptions {
  /** Base URL for the documentation links */
  baseURL?: Maybe<string>;
  /** List of custom directives to include in documentation */
  customDirective?: Maybe<CustomDirective>;
  /** Method to use for diffing schema changes */
  diffMethod?: Maybe<TypeDiffMethod>;
  /** Documentation framework specific options */
  docOptions?: Maybe<ConfigDocOptions>;
  /** Forces regeneration of all files regardless of changes */
  force?: boolean;
  /** Directives used to group schema types */
  groupByDirective?: Maybe<GroupByDirectiveOptions>;
  /** Location of the homepage content */
  homepage?: Maybe<string | false>;
  /** Identifier for the project */
  id?: Maybe<string>;
  /** Root path for generating links */
  linkRoot?: Maybe<string>;
  /** List of loaders to use for loading the schema */
  loaders?: Maybe<LoaderOption>;
  /** Package to be used for formatting MDX output */
  formatter?: Maybe<PackageName | string>;
  /**
   * @deprecated Use `formatter` instead.
   * @see formatter
   */
  mdxParser?: Maybe<PackageName | string>;
  /** Metadata tags to include in documentation */
  metatags?: Record<string, string>[];
  /** Only document types with these directives */
  onlyDocDirective?: Maybe<DirectiveName | DirectiveName[]>;
  /** Use prettier to make the output pretty */
  pretty?: Maybe<boolean>;
  /** Options for printing GraphQL types */
  printTypeOptions?: Maybe<ConfigPrintTypeOptions>;
  /** Root path for the project */
  rootPath?: Maybe<string>;
  /** Location of the GraphQL schema */
  schema?: Maybe<Pointer>;
  /** Skip documenting types with these directives */
  skipDocDirective?: Maybe<DirectiveName | DirectiveName[]>;
  /** Temporary directory for processing */
  tmpDir?: Maybe<string>;
}

/**
 * Print type configuration options.
 * Controls how types are rendered in the documentation.
 */
export interface ConfigPrintTypeOptions {
  /** How to handle deprecated items */
  deprecated?: TypeDeprecatedOption;
  /** Configuration for example sections */
  exampleSection?: TypeExampleSectionOption;
  /** Documentation hierarchy structure */
  hierarchy?: TypeHierarchyType;
  /** Whether to prefix fields with parent type names */
  parentTypePrefix?: boolean;
  /** Whether to show type badges */
  typeBadges?: boolean;
}


/**
 * Command line interface options.
 * Maps CLI flags to their values.
 */
export interface CliOptions {
  /** Base URL for documentation */
  base?: string;
  /** Whether to output configuration */
  config?: boolean;
  /** How to handle deprecated items */
  deprecated?: TypeDeprecatedOption;
  /** Schema diff method to use */
  diff?: TypeDiffMethod;
  /** Force regeneration flag */
  force?: boolean;
  /** Directive for grouping */
  groupByDirective?: string;
  /** Homepage location */
  homepage?: string | false;
  /** Generate index files flag */
  index?: boolean;
  /** Root for documentation links */
  link?: string;
  /** Documentation hierarchy type */
  hierarchy?: TypeHierarchyValueType;
  /** Formatter package */
  formatter?: string;
  /**
   * @deprecated Use `formatter` instead.
   * @see formatter
   */
  mdxParser?: string;
  /** Disable parent type prefix flag */
  noParentType?: boolean;
  /** Disable type badges flag */
  noTypeBadges?: boolean;
  /** Disable custom section header IDs for permalinks */
  noSectionId?: boolean;
  /** Prettify output flag */
  pretty?: boolean;
  /** Output root directory */
  root?: string;
  /** Schema location */
  schema?: Pointer;
  /** Directives to skip */
  skip?: string[] | string;
  /** Directives to only include */
  only?: string[] | string;
  /** Temporary directory */
  tmp?: string;
}


/**
 * Core options type that combines config options with required fields.
 * Used as the main configuration type throughout the application.
 */
export type Options = Omit<
  ConfigOptions,
  "homepage" | "linkRoot" | "pretty" | "rootPath"
> &
  Required<
    Pick<
      ConfigOptions,
      | "diffMethod"
      | "docOptions"
      | "metatags"
      | "onlyDocDirective"
      | "printTypeOptions"
      | "skipDocDirective"
    > & {
      /** Base URL for documentation */
      baseURL: string;
      /** Homepage file location */
      homepageLocation: Maybe<string>;
      /** Root for documentation links */
      linkRoot: string;
      /** Directives to only document */
      onlyDocDirective: DirectiveName[];
      /** Output directory */
      outputDir: string;
      /** Whether to prettify output */
      prettify: boolean;
      /** Schema location */
      schemaLocation: Pointer;
      /** Temporary directory */
      tmpDir: string;
      /** Directives to skip */
      skipDocDirective: DirectiveName[];
    }
  >;

/**
 * Function type for checking schema changes.
 * Used to determine if documentation needs to be regenerated.
 */
export type FunctionCheckSchemaChanges = (
  schema: GraphQLSchema,
  tmpDir: string,
  diffMethod?: DiffMethodName,
) => Promise<boolean>;

/**
 * Generator options that include logger configuration.
 * Extends base options with optional logger module.
 */
export type GeneratorOptions = Options & {
  /** Module to use for logging */
  loggerModule?: string;
};

/**
 * Configuration for directive-based grouping.
 * Specifies how to group schema items using directives.
 */
export interface GroupByDirectiveOptions {
  /** Directive to use for grouping */
  directive: DirectiveName;
  /** Field in the directive containing group name */
  field: string;
  /** Default group for items without the directive */
  fallback?: string;
}

/**
 * Map of GraphQL schema loaders by class name.
 * Associates loader class names with their configuration.
 */
export type LoaderOption = Record<ClassName, PackageConfig | PackageName>;

/**
 * Base configuration options for packages.
 * Extends base loader options with root type settings.
 */
export type PackageOptionsConfig = BaseLoaderOptions & RootTypes;

/**
 * Complete package configuration object.
 * Specifies the module and its options.
 */
export interface PackageConfig {
  /** NPM package name of the module */
  module: PackageName;
  /** Configuration options for the module */
  options?: PackageOptionsConfig;
}

/**
 * Root type configuration for a GraphQL schema.
 * Maps operation types to their root type names.
 */
export interface RootTypes {
  /** Name of the Query root type */
  query?: string;
  /** Name of the Mutation root type */
  mutation?: string;
  /** Name of the Subscription root type */
  subscription?: string;
}

export type PackageName = string & { _opaque: typeof PACKAGE_NAME };
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in opaque type pattern
declare const PACKAGE_NAME: unique symbol;

export type ClassName = string & { _opaque: typeof CLASS_NAME };
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in opaque type pattern
declare const CLASS_NAME: unique symbol;

export type LocationPath = string & { _opaque: typeof LOCATION_PATH };
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in opaque type pattern
declare const LOCATION_PATH: unique symbol;

// Re-export event system types from event.d.ts
export type {
  DefaultAction,
  DiffCheckHook,
  GenerateIndexMetafileHook,
  ICancellableEvent,
  PrintCodeHook,
  PrintTypeHook,
  RenderFilesHook,
  RenderHomepageHook,
  RenderRootTypesHook,
  RenderTypeEntitiesHook,
  SchemaLoadHook,
} from "./event";

/**
 * Metadata information about the documentation generator framework.
 *
 * @remarks
 * This type contains optional information about the framework used to generate
 * the GraphQL documentation, including its name and version.
 *
 * @property generatorFrameworkName - The name of the generator framework (e.g., "docusaurus", "vuepress")
 * @property generatorFrameworkVersion - The version of the generator framework
 */
export type MetaInfo = Maybe<{
  generatorFrameworkName?: Maybe<string>;
  generatorFrameworkVersion?: Maybe<string>;
}>;
