import type {
  CollapsibleOption,
  ConfigPrintTypeOptions,
  FrontMatterOptions,
  MetaInfo,
  TypeDeprecatedOption,
  TypeExampleSectionOption,
  TypeHierarchyObjectType,
} from "./core";
import type { ICancellableEvent } from "./event";
import type { MDXString, Maybe } from "./utils";

/**
 * Represents a single header section of a page with only content.
 */
export interface PageHeader {
  /** The header content */
  content?: MDXString | string;
}

/**
 * Represents a single section of a page with optional title and content.
 */
export interface PageSection {
  /** Optional title/heading for the section */
  title?: MDXString | string;
  /** Optional section level for hierarchical structuring */
  level?: number;
  /** The section content */
  content?: MDXString | PageSection | PageSection[] | string;
}

/**
 * Map of all available sections in a type page.
 */
export interface PageSections {
  /** Additional custom sections can be added by event handlers */
  [key: string]: Maybe<PageHeader | PageSection>;
  /** YAML frontmatter or top-level heading */
  header?: PageHeader;
  /** HTML meta tags */
  metatags?: PageHeader;
  /** MDX import declarations */
  mdxDeclaration?: PageHeader;
  /** Custom tags (e.g., \@deprecated) */
  tags?: PageSection;
  /** Type description from GraphQL comments */
  description?: PageSection;
  /** GraphQL code block */
  code?: PageSection;
  /** Custom directives */
  customDirectives?: PageSection;
  /** Type metadata (fields, arguments, etc.) */
  metadata?: PageSection;
  /** Usage examples */
  example?: PageSection;
  /** Related types */
  relations?: PageSection;
}

/**
 * Data payload for print code events.
 */
export interface PrintCodeEventData {
  /** The GraphQL type being printed */
  readonly type: unknown;
  /** The name of the type */
  readonly typeName: string;
  /** The print options in effect */
  readonly options: PrintTypeOptions;
}

/**
 * Data payload for print type events.
 */
export interface PrintTypeEventData {
  /** The GraphQL type being printed */
  readonly type: unknown;
  /** The name identifier for the type */
  readonly name: Maybe<string>;
  /** The print options in effect */
  readonly options: PrintTypeOptions;
}

/**
 * Public interface contract for print code events.
 *
 * Matches the runtime shape of printer event class instances.
 */
export interface IPrintCodeEvent extends ICancellableEvent {
  readonly data: PrintCodeEventData;
  output: string;
}

/**
 * Public interface contract for print type events.
 *
 * Matches the runtime shape of printer event class instances.
 */
export interface IPrintTypeEvent extends ICancellableEvent {
  readonly data: PrintTypeEventData;
  output: Maybe<MDXString>;
}


/**
 * Data payload for compose page type events.
 */
export interface ComposePageTypeEventData {
  /** The GraphQL type being composed */
  readonly type: unknown;
  /** The name identifier for the type */
  readonly name: Maybe<string>;
  /** The print options in effect */
  readonly options: PrintTypeOptions;
  /** The map of all page sections (mutable in BEFORE event) */
  readonly sections: PageSections;
}

import type { Formatter } from "./formatter";
import type {
  GraphQLDirective,
  GraphQLSchema,
  SchemaEntitiesGroupMap,
} from "./graphql";
import type { CustomDirectiveMap } from "./helpers";

/**
 * Minimal event emitter interface for printer event emission.
 *
 * This interface allows the printer to emit events without depending on the core package.
 * The actual implementation is provided by the CancellableEventEmitter from \@graphql-markdown/core.
 *
 * @example
 * ```typescript
 * // In core package
 * const events = getEvents(); // Returns CancellableEventEmitter
 * await Printer.init(schema, baseURL, linkRoot, options, formatter, mdxDeclaration, events);
 *
 * // In printer
 * if (this.eventEmitter) {
 *   const event = new PrintCodeEvent({ type, typeName, options }, output);
 *   await this.eventEmitter.emitAsync(PrintTypeEvents.AFTER_PRINT_CODE, event);
 *   output = event.output; // Use potentially modified output
 * }
 * ```
 */
export interface PrinterEventEmitter {
  /**
   * Emit an async event with support for cancellable events.
   * Handlers can modify the event's mutable properties.
   *
   * @param eventName - The event name to emit
   * @param event - The event object (PrinterEvent instance)
   * @returns Promise that resolves when all handlers have executed
   */
  emitAsync: (
    eventName: string,
    event: ICancellableEvent,
  ) => Promise<{ errors: Error[]; defaultPrevented: boolean }>;
}

/**
 * Represents the root GraphQL type names supported by the documentation generator
 */
export type RootTypeName =
  | "DIRECTIVE"
  | "ENUM"
  | "INPUT"
  | "INTERFACE"
  | "MUTATION"
  | "OPERATION"
  | "QUERY"
  | "SCALAR"
  | "SUBSCRIPTION"
  | "TYPE"
  | "UNION";

/**
 * Represents localized text that can be either a string or an object with singular/plural forms
 */
export type TypeLocale = string | { singular: string; plural: string };

/**
 * Maps root type names to their localized representations
 */
export type RootTypeLocale = Record<RootTypeName, TypeLocale>;

/**
 * Represents an admonition (notice/warning) block in the documentation
 */
export interface AdmonitionType {
  icon?: Maybe<string>;
  text: string;
  title: Maybe<string>;
  type: string;
}

/**
 * Configuration options for printing type documentation
 */
export interface PrinterConfigPrintTypeOptions {
  deprecated?: TypeDeprecatedOption;
  exampleSection?: TypeExampleSectionOption;
  hierarchy?: TypeHierarchyObjectType;
  metatags?: Record<string, string>[];
  parentTypePrefix?: boolean;
  typeBadges?: boolean;
}

/**
 * Comprehensive options for printing type documentation
 */
export type PrintTypeOptions = Partial<Formatter> & {
  basePath: string;
  collapsible?: Maybe<CollapsibleOption>;
  customDirectives?: Maybe<CustomDirectiveMap>;
  deprecated?: Maybe<TypeDeprecatedOption>;
  exampleSection?: Maybe<TypeExampleSectionOption>;
  formatCategoryFolderName?: Maybe<(categoryName: string) => string>;
  frontMatter?: Maybe<FrontMatterOptions>;
  groups?: Maybe<SchemaEntitiesGroupMap>;
  hierarchy?: Maybe<TypeHierarchyObjectType>;
  level?: Maybe<SectionLevelValue>;
  meta?: Maybe<MetaInfo>;
  metatags?: Maybe<Record<string, string>[]>;
  onlyDocDirectives?: GraphQLDirective[];
  operationNamespaceParts?: Maybe<string[]>;
  parentType?: Maybe<string>;
  parentTypePrefix?: boolean;
  schema?: Maybe<GraphQLSchema>;
  sectionHeaderId?: boolean;
  skipDocDirectives?: GraphQLDirective[];
  typeBadges?: boolean;
  withAttributes?: boolean;
};

/**
 * Represents valid section level values for documentation hierarchy
 */
export type SectionLevelValue = {
  _opaque: typeof SECTION_LEVEL_VALUE;
} & (0 | 3 | 4 | 5);
declare const SECTION_LEVEL_VALUE: unique symbol;

/**
 * Represents a badge with text and optional styling
 */
export interface Badge {
  text: TypeLocale;
  classname?: string[] | string;
}

/**
 * Represents a link with text and URL
 */
export interface TypeLink {
  text: string;
  url: string;
  id?: Maybe<string>;
}

/**
 * Options for printing links in documentation
 */
export type PrintLinkOptions = Partial<PrintTypeOptions> &
  Pick<
    PrintTypeOptions,
    | "basePath"
    | "deprecated"
    | "formatMDXLink"
    | "groups"
    | "hierarchy"
    | "onlyDocDirectives"
    | "parentType"
    | "parentTypePrefix"
    | "skipDocDirectives"
    | "withAttributes"
  >;

/**
 * Options for printing directive documentation
 */
export type PrintDirectiveOptions = Partial<PrintTypeOptions> &
  Pick<PrintTypeOptions, "basePath" | "deprecated" | "parentTypePrefix">;

/**
 * Options accepted by printer initialization.
 */
export type PrinterInitOptions = {
  customDirectives?: Maybe<CustomDirectiveMap>;
  deprecated?: Maybe<TypeDeprecatedOption>;
  groups?: Maybe<SchemaEntitiesGroupMap>;
  meta?: Maybe<MetaInfo>;
  metatags?: Maybe<Record<string, string>[]>;
  onlyDocDirectives?: Maybe<GraphQLDirective[]>;
  printTypeOptions?: Maybe<ConfigPrintTypeOptions>;
  skipDocDirectives?: Maybe<GraphQLDirective[]>;
  sectionHeaderId?: Maybe<boolean>;
};

/**
 * Abstract printer class that handles the generation of markdown documentation
 * from GraphQL schema components.
 * @public
 * @abstract
 */
export abstract class IPrinter {
  /**
   * Initializes the printer with schema and configuration
   * @param schema - The GraphQL schema to process
   * @param baseURL - The base URL for generating links
   * @param linkRoot - The root path for relative links
   * @param options - Printer configuration options
   * @param mdxModule - Optional MDX module for rendering
   * @param mdxDeclaration - Optional MDX import declaration
   * @param eventEmitter - Optional event emitter for print events
   */
  static init(
    schema: Maybe<GraphQLSchema>,
    baseURL?: Maybe<string>,
    linkRoot?: Maybe<string>,
    options?: PrinterInitOptions,
    mdxModule?: Partial<Formatter>,
    mdxDeclaration?: Maybe<string>,
    eventEmitter?: Maybe<PrinterEventEmitter>,
  ): Promise<void>;

  /**
   * Generates a markdown header for a type documentation
   * @param id - Unique identifier for the section
   * @param title - Title of the section
   * @param options - Combined printer configuration and options
   * @returns Markdown string containing the header
   */
  static printHeader(
    id: string,
    title: string,
    options: PrintTypeOptions,
  ): string;

  /**
   * Prints the description section for a type
   * @param type - The GraphQL type to document
   * @param options - Combined printer configuration and options
   * @param noText - Text to display when no description is available
   * @returns Markdown string containing the description
   */
  static printDescription(
    type: unknown,
    options: PrintTypeOptions,
    noText?: string,
  ): MDXString | string;

  /**
   * Prints the code representation of a GraphQL type
   * @param type - The GraphQL type to document
   * @param options - Combined printer configuration and options
   * @returns Markdown string containing the code representation
   */
  static printCode(
    type: unknown,
    options: PrintTypeOptions,
  ): string;

  /**
   * Prints custom directives associated with a type
   * @param type - The GraphQL type to document
   * @param options - Combined printer configuration and options
   * @returns MDX string containing custom directives
   */
  static printCustomDirectives(
    type: unknown,
    options: PrintTypeOptions,
  ): Maybe<PageSection>;

  /**
   * Prints custom tags associated with a type
   * @param type - The GraphQL type to document
   * @param options - Combined printer configuration and options
   * @returns MDX string containing custom tags
   */
  static printCustomTags(
    type: unknown,
    options: PrintTypeOptions,
  ): MDXString | string;

  /**
   * Prints metadata information for a type
   * @param type - The GraphQL type to document
   * @param options - Combined printer configuration and options
   * @returns MDX string containing type metadata
   */
  static printTypeMetadata(
    type: unknown,
    options: PrintTypeOptions,
  ): Maybe<PageSection | PageSection[]>;

  /**
   * Prints related types and their relationships
   * @param type - The GraphQL type to document
   * @param options - Combined printer configuration and options
   * @returns MDX string containing related types
   */
  static printRelations(
    type: unknown,
    options: PrintTypeOptions,
  ): MDXString | string;

  /**
   * Prints complete documentation for a GraphQL type
   * @param name - The name of the type
   * @param type - The GraphQL type to document
   * @param options - Optional configuration options for printing
   * @returns Promise resolving to MDX string containing complete type documentation
   */
  static printType(
    name: Maybe<string>,
    type: unknown,
    options?: Maybe<Partial<PrintTypeOptions>>,
  ): Promise<Maybe<MDXString>>;
}

/**
 * Basic printer configuration
 */
export interface PrinterConfig {
  /** Base URL used for generating absolute links */
  baseURL: string;
  /** Root path for generating relative links */
  linkRoot: string;
  /** GraphQL schema to generate documentation from */
  schema: Maybe<GraphQLSchema>;
}

/**
 * Extended printer options for customizing documentation generation
 */
export interface PrinterOptions {
  /** Map of custom directive handlers */
  customDirectives?: Maybe<CustomDirectiveMap>;
  /** Configuration for handling deprecated types and fields */
  deprecated?: Maybe<TypeDeprecatedOption>;
  /** Schema entity grouping configuration */
  groups?: Maybe<SchemaEntitiesGroupMap>;
  /** Generator metadata options */
  meta?: Maybe<MetaInfo>;
  /** Custom metatags to include in documentation */
  metatags?: Maybe<Record<string, string>[]>;
  /** List of directives to exclusively include in documentation */
  onlyDocDirectives?: GraphQLDirective[];
  /** Type-specific printing options */
  printTypeOptions?: Maybe<ConfigPrintTypeOptions>;
  /** List of directives to exclude from documentation */
  skipDocDirectives?: GraphQLDirective[];
  /** Flag to disable custom header section ID for permalink */
  sectionHeaderId?: boolean;
}
