import type { LocationUri, XmlTagNode } from '@e-mc/types/lib/squared';

import type { DocumentConstructor, ICloud, IDocument, IFileManager } from '@e-mc/types/lib';
import type { IFileThread } from '@e-mc/types/lib/asset';
import type { DocumentAsset as IDocumentAsset } from '@e-mc/types/lib/document';
import type { LogTime } from '@e-mc/types/lib/logger';
import type { DbSourceDataType, DocumentComponentOption, DocumentComponent as IDocumentComponent, DocumentComponentOptions as IDocumentComponentOptions, DocumentDirectory as IDocumentDirectory, DocumentEval as IDocumentEval, DocumentModule as IDocumentModule, DocumentSettings as IDocumentSettings, DocumentUserSettings as IDocumentUserSettings } from '@e-mc/types/lib/settings';

import type { CloudScopeOrigin } from '@e-mc/cloud/types';
import type { IDomWriter, IXmlElement } from '@e-mc/document/parse/types';

import type { ChromeAsset, CssRuleData, DataSource, DocumentOutput } from './squared';

export const enum PROPERTIES {
    INDEX = '__index__',
    FROM_HTML = '__fromhtml__',
    ASSIGN = '__assign__',
    SERVERROOT = '__serverroot__'
}

export const enum MIME {
    HTML = 'text/html',
    JS = 'application/javascript',
    JS_TEXT = 'text/javascript',
    CSS = 'text/css',
    SVG = 'image/svg+xml',
    TTF = 'font/ttf',
    OTF = 'font/otf',
    WOFF = 'font/woff',
    WOFF2 = 'font/woff2'
}

interface DocumentDirectory extends IDocumentDirectory {
    template?: string;
    data?: string;
    export?: string;
    schema?: string;
    sql?: string;
}

interface DocumentComponentExtended<T> extends DbSourceDataType<T> {
    uri?: T;
    local?: T;
    export?: T;
    json?: T;
}

interface DocumentComponentOptions<T> extends IDocumentComponentOptions, DocumentComponentExtended<DocumentComponentOption<T>> {}

interface DocumentEval extends IDocumentEval {
    template?: boolean;
    userconfig?: boolean;
}

interface ChromeDocumentUserSettings extends IDocumentUserSettings {
    server_root?: ObjectMap<StringMap>;
}

interface ChromeDocumentSettings extends IDocumentSettings<ChromeDocumentUserSettings> {
    directory?: DocumentDirectory;
    options?: DocumentComponentOptions<boolean | number>;
    server_root?: ObjectMap<StringMap>;
    export?: ObjectMap<string | FunctionType>;
}

type OutputType = "useUnsafeReplace" | "productionRelease" | "productionIncremental" | "serverRootMapping" | "sanitizeFramework" | "templateMap" | "userAgentData";

export interface ChromeExtensionOptions<T extends DocumentAsset = DocumentAsset> {
    file?: T;
}

export interface ChromeExtensionCssOptions<T extends DocumentAsset = DocumentAsset> extends ChromeExtensionOptions<T> {
    config: UserConfig;
}

export type ChromeExtension<T extends DocumentAsset = DocumentAsset> = (source: string, options: ChromeExtensionOptions<T>) => Promise<string | undefined>;
export type ChromeExtensionCss<T extends DocumentAsset = DocumentAsset> = (source: string, options: ChromeExtensionCssOptions<T>) => Promise<string | undefined>;

export interface FormatUri extends Partial<LocationUri> {
    dictionary?: string;
}

export interface DocumentFinalize<T extends IFileManager<U>, U extends DocumentAsset> {
    readonly host: T;
    readonly startTime: LogTime;
    readonly hashed: Set<U>;
    readonly contentMap: StringMap;
    readonly bufferMap: ObjectMap<Bufferable>;
    readonly cacheMiss: string[];
    readonly productionRelease: true | undefined;
    getBuffer(item: U, localUri?: string): Bufferable | null;
}

export interface DocumentComponent<T = boolean> extends IDocumentComponent<T, T>, DocumentComponentExtended<T> {}

export interface DocumentModule extends IDocumentModule {
    eval?: DocumentEval;
    format?: PlainObject & { uuid?: FormatUri };
    settings?: ChromeDocumentSettings;
}

export interface DocumentAsset extends IDocumentAsset, ChromeAsset {
    relativeUrl?: string;
    inlineFilename?: string;
    srcSet?: string[];
    inlineUrl?: string;
    inlineUrlMap?: StringMap;
    inlineUrlCloud?: string;
    inlineUrlCloudMap?: StringMap;
    inlineBase64?: string;
    modified?: boolean;
}

export interface UserConfig extends CssRuleData, Omit<DocumentOutput, OutputType> {
    useUnsafeHtmlReplace?: boolean;
    useUnsafeCssReplace?: boolean;
}

export interface IChromeDocument<T extends IFileManager<U>, U extends DocumentAsset, V extends ICloud = ICloud<T>> extends IDocument<T, U, DocumentModule, DocumentComponent, DocumentComponentOption, V>, Pick<DocumentOutput, OutputType> {
    htmlFile: U | null;
    cssFiles: U[];
    jsFiles: U[];
    svgFiles: U[];
    baseDirectory: string;
    baseUrl: string;
    config: UserConfig;
    related: Map<U, Set<U>>;
    replaced: U[];
    assets: U[];
    formatMap?: ObjectMap<FormatUri>;
    using(data: IFileThread): Promise<void>;
    setElementSrc(file: U, element: IXmlElement, value: string): void;
    findDataValue(name: string): string | undefined;
    setInlinedAttributes(processing: DocumentFinalize<T, U>): void;
    applyTransforms(processing: DocumentFinalize<T, U>): Promise<void>;
    rewriteHtml(processing: DocumentFinalize<T, U>): Promise<void>;
    setElementAttributes(processing: DocumentFinalize<T, U>, domBase: IDomWriter): Promise<void>;
    applyDataSource(processing: DocumentFinalize<T, U>, domBase: IDomWriter): Promise<void>;
    setProductionAttributes(processing: DocumentFinalize<T, U>): void;
    createHash(processing: DocumentFinalize<T, U>, file: U, hash: string): void;
    transformJs(processing: DocumentFinalize<T, U>, file: U, source: string): Promise<string | undefined>;
    transformCss(processing: DocumentFinalize<T, U>, file: U, source: string): Promise<string | undefined>;
    removeServerRoot(value: string): string;
    replaceContent(source: string, match: RegExpExecArray | string, mimeType?: string): string | undefined;
    cloudInit(state: CloudScopeOrigin<T, U, V>): void;
    cloudObject(state: CloudScopeOrigin<T, U, V>, file: U): boolean;
    cloudUpload(state: CloudScopeOrigin<T, U, V>, file: U, url: string, active: boolean): Promise<boolean>;
    cloudFinalize(state: CloudScopeOrigin<T, U, V>): Promise<unknown[]>;
    getSrcUrl(parent: U, file: U): [string, boolean];
    isWatched(file: U): boolean;
    get elements(): XmlTagNode[];
    get editing(): U[];
    set dataSource(value);
    get dataSource(): DataSource[];
    get sessionKey(): string;
    get settings(): ChromeDocumentSettings;
}

export interface ChromeDocumentConstructor<T extends IFileManager<U>, U extends DocumentAsset> extends ConstructorDerived<DocumentConstructor<T, U>> {
    INTERNAL_ASSIGNUUID: string;
    INTERNAL_SERVERROOT: string;
    readonly prototype: IChromeDocument<T, U>;
    new(module?: DocumentModule, ...args: unknown[]): IChromeDocument<T, U>;
}