import type { EncodingAction, LocationUri, MimeTypeAction, StorageAction } from './squared';

import type { IDocument, IFileManager, IModule } from './index';
import type { ExternalAsset, InitialValue } from './asset';
import type { CloudStorage } from './cloud';
import type { LogComponent, LogDate } from './logger';
import type { DefaultAction } from './module';
import type { DocumentTransform, ImportModule } from './settings';

export interface DocumentAsset extends ExternalAsset, StorageAction<CloudStorage> {
    inlineFilename?: string;
    initialValue?: InitialValue & { inlineFilename?: string };
}

export interface StartOfSourceMap {
    file?: string;
    sourceRoot?: string;
}

export interface RawSourceMap<T = number | string> extends StartOfSourceMap {
    version: T;
    sources: string[];
    names: string[];
    sourcesContent?: string[];
    mappings: string;
}

export interface SourceInput<T = [string, string?, string?][]> {
    code?: string;
    sourceFile?: T;
    sourceName?: string;
    sourcesRelativeTo?: string;
}

export interface SourceCode {
    code: string;
    map?: RawSourceMap;
    sourceMappingURL?: string;
}

export interface ChunkFile {
    code: string;
    filename?: string;
    entryPoint?: boolean;
}

export interface ChunkData extends ChunkFile {
    sourceMap?: SourceMap;
}

export interface CacheData extends EncodingAction {
    uri?: string;
    etag?: string;
    override?: boolean;
}

export interface TransformAction {
    cacheData?: CacheData;
}

export interface TransformResult extends SourceCode {
    type?: string;
    chunks?: Array<SourceCode & ChunkFile> | null;
    sourceFiles?: string[];
}

export interface TransformOutput extends Partial<LocationUri>, Omit<SourceInput<string>, "code">, MimeTypeAction {
    imported?: boolean;
    sourceMap?: SourceMap;
    metadata?: unknown;
    external?: PlainObject;
    productionRelease?: boolean;
    getMainFile?(code?: string, imports?: ImportModule): SourceInput<string> | undefined;
    getSourceFiles?(imports?: ImportModule): SourceInput | undefined;
}

export interface TransformOptions<T = AnyObject, U = T> extends TransformOutput {
    baseConfig: T;
    outputConfig: U;
    sourceMap: SourceMap;
    supplementChunks: ChunkData[];
}

export interface ITransformSeries<T = AnyObject, U = T, V = object> extends IModule, TransformOptions<T, U> {
    readonly type: string;
    init(instance: IModule, dirname?: string): this;
    close(instance: IModule): void;
    createSourceMap(value: string): SourceMap;
    getMainFile(code?: string, imports?: ImportModule): SourceInput<string> | undefined;
    getSourceFiles(imports?: ImportModule): SourceInput | undefined;
    toBaseConfig(all?: boolean): T;
    upgrade<W = unknown>(context: W, options: FindModuleOptions): W;
    upgrade<W = unknown>(context: W, startDir?: string, packageName?: string, version?: string): W;
    upgradeESM<W = unknown>(context: W, options: FindModuleOptions): Promise<W>;
    upgradeESM<W = unknown>(context: W, startDir?: string, packageName?: string, version?: string): Promise<W>;
    findModule(startDir: string, packageName?: string, version?: string): string;
    set code(value);
    get code(): string;
    get out(): IOut;
    get metadata(): V;
    get options(): TransformOutput;
    get productionRelease(): boolean;
    get imported(): boolean;
    set version(value);
    get version(): string;
    set packageName(value);
    get packageName(): string;
    get packageVersion(): string;
    get username(): string;
}

export interface TransformSeriesConstructor {
    readonly prototype: ITransformSeries;
    new(type: string, code: string, options: TransformOutput): ITransformSeries;
}

export interface IOut extends OutV3, OutV4 {}

export interface OutV3 {
    sourceFiles?: string[];
    ignoreCache?: boolean;
    logAppend?: LogComponent[];
    messageAppend?: string;
}

export interface OutV4 {
    logQueued?: LogComponent[];
}

export interface SourceMapOptions extends MimeTypeAction {
    file?: string;
    hash?: string;
    sourceRoot?: string;
    sourceMappingURL?: string;
    inlineMap?: boolean;
    emptySources?: boolean;
}

export interface SourceMap extends SourceCode {
    output: Map<string, SourceCode>;
    reset(): void;
    nextMap(name: string, code: string, map: unknown, emptySources: boolean): boolean;
    nextMap(name: string, code: string, map: unknown, sourceMappingURL?: string, emptySources?: boolean): boolean;
    parseMap(map: unknown): RawSourceMap | null;
    set map(value);
    get map(): RawSourceMap | undefined;
}

export interface SourceMapConstructor {
    readonly SOURCE_MAPPING_URL: RegExp;
    findSourceMap(code?: string, uri?: string): RawSourceMap | undefined;
    removeSourceMappingURL(value: string): [string, string?, (RawSourceMap | null)?];
    isRaw(map: unknown): map is RawSourceMap;
    readonly prototype: SourceMap;
    new(code: string, remove: boolean): SourceMap;
    new(code: string, uri?: string, remove?: boolean): SourceMap;
}

export interface FindModuleOptions {
    startDir?: string;
    packageName?: string;
    version?: string;
}

export interface ImportMap {
    imports?: StringMap;
    scopes?: ObjectMap<StringMap>;
    integrity?: StringMap;
}

export interface UpdateGradleOptions {
    multiple?: boolean;
    addendum?: string;
    upgrade?: boolean;
    updateOnly?: boolean;
}

export interface LintMessage {
    ruleId: string;
    message: string;
    line: number;
    column: number;
    endLine?: number;
    endColumn?: number;
    severity?: 0 | 1 | 2 | "" | "error" | "warning";
    fatal?: boolean;
}

export interface GenerateLintTableOptions extends Partial<LocationUri> {
    leadingText?: string;
    trailingText?: string;
    errorCount?: number | [number, number];
    warningCount?: number | [number, number];
    fatalErrorCount?: number;
    timeStamp?: LogDate;
    ruleWidth?: number;
    messageWidth?: number;
}

export interface CustomizeOptions {
    transform?: DocumentTransform;
}

export interface AsSourceFileOptions extends EncodingAction, DefaultAction {
    cache?: boolean;
}

export type Transformer = FunctionType<Promise<string> | string>;
export type ConfigOrTransformer = AnyObject | Transformer;
export type PluginConfig<T = ConfigOrTransformer> = [string, T | undefined, AnyObject | undefined, boolean?] | [];
export type TransformCallback<T extends IFileManager<U>, U extends ExternalAsset = ExternalAsset> = (this: T, instance: IDocument<T, U>, require?: NodeJS.Require) => Promise<void>;