import { NgDocBaseEntity, JSDocMetadata, NgDocPageIndex } from '@ng-doc/core/interfaces';
import { Type, Injector } from '@angular/core';
import { InputType } from '@ng-doc/core';
import { Constructor } from '@ng-doc/core/types/constructor';
import { ControlValueAccessor } from '@angular/forms';
import { NgDocHighlightPosition } from '@ng-doc/ui-kit';

/**
 * Navigation item interface
 */
interface NgDocNavigation extends NgDocBaseEntity {
    expandable?: boolean;
    /** Determines whether the category should be expanded by default */
    expanded?: boolean;
    /** Children of the navigation item */
    children?: NgDocNavigation[];
    /** The route of the entity (current sourceFileFolder name by default) */
    route: string;
    /**
     * The item metadata.
     * Collects all tags based on the JSDoc tags for `NgDocPage` or `NgDocCategory`
     * entities.
     */
    metadata?: JSDocMetadata;
}

/**
 * Application context
 */
interface NgDocContext {
    /**
     * List of navigation items
     */
    navigation: NgDocNavigation[];
}

interface NgDocDemoAssets {
    [name: string]: NgDocDemoAsset[];
}
interface NgDocDemoAsset {
    code: string;
    title: string;
    icon?: string;
    opened?: boolean;
}

interface NgDocProcessorOptions<T, K extends keyof T = keyof T> {
    content?: Node[][];
    inputs?: Partial<{
        [key in K]: InputType<T, key>;
    }>;
}

/**
 * Interface to create a processor that will be used to replace html nodes with an Angular component.
 */
interface NgDocPageProcessor<T = unknown> {
    /**
     * Target component to replace html nodes with.
     */
    component: Type<T>;
    /**
     * Selector to find html nodes to replace.
     */
    selector: string;
    /**
     * Extract options for Angular component from html node.
     * @param element - Html node to extract options from.
     * @param root - Root html node.
     */
    extractOptions: (element: Element, root: HTMLElement) => NgDocProcessorOptions<T>;
    /**
     * Can be used to find a node to replace instead of the node that was found by selector.
     * @param element - Html node to replace.
     * @param injector - Injector to get dependencies.
     */
    nodeToReplace?: (element: Element, injector: Injector) => Element;
}

interface NgDocTocItem {
    title: string;
    path: string;
    hash: string;
    element: HTMLHeadingElement;
    level: number;
}

/**
 * Interface for page navigation implementation
 */
interface NgDocPageNavigation {
    /**
     * Previous page
     */
    prevPage?: NgDocNavigation;
    /**
     * Next page
     */
    nextPage?: NgDocNavigation;
}
/**
 * Interface for page breadcrumb implementation
 */
interface NgDocPageBreadcrumbs {
    /**
     * Breadcrumb items
     */
    breadcrumbs: string[];
}
/**
 * Interface for page table of content implementation
 */
interface NgDocPageToc {
    /**
     * Table of content items.
     */
    tableOfContent: NgDocTocItem[];
}
/**
 * Page skeleton that should be used to create different parts of the page.
 *
 * You can use it to define your own components for the page navigation, breadcrumb, etc.
 */
interface NgDocPageSkeleton {
    /**
     * Page breadcrumbs.
     */
    breadcrumbs?: Type<NgDocPageBreadcrumbs>;
    /**
     * Bottom page navigation.
     */
    navigation?: Type<NgDocPageNavigation>;
    /**
     * Table of content.
     */
    toc?: Type<NgDocPageToc>;
}

/**
 * Interface describing Type Control
 */
interface NgDocTypeControl<T = unknown> extends ControlValueAccessor {
    /**
     * The name of the input for which it is created
     */
    name?: string;
    /**
     * The description of the input (based on the comment)
     */
    description?: string;
    /**
     * The default value of the input
     */
    default?: T;
    /**
     * The list of possible values, it usually works only for Type Aliases which has several values
     */
    options?: string[];
    /**
     * Determines if the property is manually added by the user using `controls` property in playground config
     */
    isManual?: boolean;
}

/**
 * Options for the type control provider
 */
interface NgDocTypeControlProviderOptions {
    /**
     * Allows to hide the label of the type control,
     * if the label is not needed or if you want to use a custom label
     */
    hideLabel?: boolean;
    /**
     * You can use this to change the order of the type control
     * NgDoc's controls start at 5 and go up to 10
     */
    order?: number;
}

interface NgDocProvidedTypeControl {
    control: Constructor<NgDocTypeControl>;
    options?: NgDocTypeControlProviderOptions;
}

/**
 * Results of a search query.
 */
interface NgDocSearchResult {
    /**
     * Index that was found.
     */
    index: NgDocPageIndex;
    /**
     * Positions of the found terms.
     */
    positions: Partial<Record<keyof NgDocPageIndex, NgDocHighlightPosition[]>>;
}

interface NgDocTab {
    title: string;
    content: Element;
    icon?: string;
    active?: boolean;
}

/**
 * Interface describing a custom theme for the application
 */
interface NgDocTheme {
    /**
     * Theme ID that you can use to set theme (e.g. `CustomTheme`)
     */
    id: string;
    /**
     * Path that will be used to load theme dynamically (e.g. `/assets/themes/custom.css`)
     */
    path: string;
}

export type { NgDocContext, NgDocDemoAsset, NgDocDemoAssets, NgDocNavigation, NgDocPageBreadcrumbs, NgDocPageNavigation, NgDocPageProcessor, NgDocPageSkeleton, NgDocPageToc, NgDocProcessorOptions, NgDocProvidedTypeControl, NgDocSearchResult, NgDocTab, NgDocTheme, NgDocTocItem, NgDocTypeControl, NgDocTypeControlProviderOptions };
