import type {
  Decorator,
  Enum,
  EnumMember,
  Interface,
  LinterRuleDefinition,
  LinterRuleSet,
  MixedFunctionParameter,
  Model,
  ModelProperty,
  Operation,
  PackageJson,
  Scalar,
  Type,
  Union,
  UnionVariant,
} from "@typespec/compiler";

export type TypeSpecRefDoc = TypeSpecLibraryRefDoc;

export type TypeSpecLibraryRefDoc = TypeSpecRefDocBase & {
  /**
   * Library name
   */
  readonly name: string;

  /**
   * Library package.json
   */
  readonly packageJson: PackageJson;

  /**
   * Library description
   */
  readonly description?: string;

  readonly emitter?: EmitterRefDoc;

  /** Documentation about the linter rules and ruleset provided in this library. */
  readonly linter?: LinterRefDoc;

  /** Documentation for sub-exports (e.g., "./streams", "./testing"). Keyed by export path. */
  readonly subExports?: ReadonlyMap<string, SubExportRefDoc>;
};

export type SubExportRefDoc = TypeSpecRefDocBase & {
  /** The export path (e.g., "./streams") */
  readonly path: string;
};

export type TypeSpecRefDocBase = {
  readonly namespaces: readonly NamespaceRefDoc[];

  /** Returns the named type ref doc mapping to that type if is is part of this library. */
  readonly getNamedTypeRefDoc: (type: Type) => RefDocEntity | undefined;
};

export type EmitterRefDoc = {
  readonly options: EmitterOptionRefDoc[];
};

export type LinterRefDoc = {
  /** List of rulesets provided. */
  readonly ruleSets: LinterRuleSetRefDoc[];
  readonly rules: LinterRuleRefDoc[];
};

export type LinterRuleSetRefDoc = ReferencableElement & {
  readonly kind: "ruleset";
  readonly ruleSet: LinterRuleSet;
};
export type LinterRuleRefDoc = ReferencableElement & {
  readonly kind: "rule";
  readonly rule: LinterRuleDefinition<any, any>;
};

export type EmitterOptionRefDoc = {
  readonly name: string;
  readonly type: string;
  readonly doc: string;
  readonly default?: string;
  readonly allowedValues?: readonly string[];
  /** If set, the option is deprecated. Contains the deprecation message or empty string. */
  readonly deprecated?: string;
  /** Nested options for object-typed options. */
  readonly nestedOptions?: readonly EmitterOptionRefDoc[];
  /** When present, this option is a union of multiple variants (oneOf). */
  readonly variants?: readonly EmitterOptionVariantRefDoc[];
};

export type EmitterOptionVariantRefDoc = {
  readonly type: string;
  readonly doc?: string;
  readonly default?: string;
  readonly allowedValues?: readonly string[];
  /** Nested options for object-typed variants. */
  readonly nestedOptions?: readonly EmitterOptionRefDoc[];
};

export type RefDocEntity =
  | NamespaceRefDoc
  | DecoratorRefDoc
  | OperationRefDoc
  | InterfaceRefDoc
  | ModelRefDoc
  | EnumRefDoc
  | UnionRefDoc
  | ScalarRefDoc
  | LinterRuleSetRefDoc
  | LinterRuleRefDoc;

export type NamespaceRefDoc = {
  readonly kind: "namespace";
  readonly id: string;
  readonly name: string;
  readonly decorators: readonly DecoratorRefDoc[];
  readonly operations: readonly OperationRefDoc[];
  readonly interfaces: readonly InterfaceRefDoc[];
  readonly models: readonly ModelRefDoc[];
  readonly enums: readonly EnumRefDoc[];
  readonly unions: readonly UnionRefDoc[];
  readonly scalars: readonly ScalarRefDoc[];
};

export type ReferencableElement = {
  /**
   * Fully qualified id
   */
  readonly id: string;

  readonly name: string;
};

export type NamedTypeRefDoc = ReferencableElement & {
  readonly signature: string;
  readonly doc: string;
  readonly examples: readonly ExampleRefDoc[];
  readonly deprecated?: DeprecationNotice;
};

export type DeprecationNotice = {
  readonly message: string;
};

export type DecoratorRefDoc = NamedTypeRefDoc & {
  readonly kind: "decorator";
  readonly type: Decorator;
  readonly target: FunctionParameterRefDoc;
  readonly parameters: readonly FunctionParameterRefDoc[];
  readonly otherTags: readonly string[];
};

export type FunctionParameterRefDoc = {
  readonly type: MixedFunctionParameter;
  readonly name: string;
  readonly doc: string;
  readonly optional: boolean;
  readonly rest: boolean;
};

export type ExampleRefDoc = {
  readonly title?: string;
  readonly content: string;
};

export type OperationRefDoc = NamedTypeRefDoc & {
  readonly kind: "operation";
  readonly type: Operation;
  readonly templateParameters?: TemplateParameterRefDoc[];
};

export type InterfaceRefDoc = NamedTypeRefDoc & {
  readonly kind: "interface";
  readonly type: Interface;
  readonly templateParameters?: readonly TemplateParameterRefDoc[];
  readonly interfaceOperations: readonly OperationRefDoc[];
};

export type TemplateParameterRefDoc = {
  readonly name: string;
  readonly doc: string;
};

export type ModelRefDoc = NamedTypeRefDoc & {
  readonly kind: "model";
  readonly type: Model;

  readonly templateParameters?: readonly TemplateParameterRefDoc[];
  readonly properties: ReadonlyMap<string, ModelPropertyRefDoc>;
};

export type ModelPropertyRefDoc = NamedTypeRefDoc & {
  readonly type: ModelProperty;
};

export type EnumRefDoc = NamedTypeRefDoc & {
  readonly kind: "enum";
  readonly type: Enum;
  readonly members: ReadonlyMap<string, EnumMemberRefDoc>;
};

export type EnumMemberRefDoc = NamedTypeRefDoc & {
  readonly type: EnumMember;
};

export type UnionRefDoc = NamedTypeRefDoc & {
  readonly kind: "union";
  readonly type: Union;

  readonly templateParameters?: readonly TemplateParameterRefDoc[];
  readonly variants: ReadonlyMap<string, UnionVariantRefDoc>;
};

export type UnionVariantRefDoc = NamedTypeRefDoc & {
  readonly type: UnionVariant;
};

export type ScalarRefDoc = NamedTypeRefDoc & {
  readonly kind: "scalar";
  readonly type: Scalar;

  readonly templateParameters?: readonly TemplateParameterRefDoc[];
};
