/**
 * Canvas 平台适配器
 * 封装微信小程序 Canvas API，为未来 Web 平台迁移做准备
 */

type QueryHost =
    | WechatMiniprogram.Page.TrivialInstance
    | WechatMiniprogram.Component.TrivialInstance;

type Canvas2DContextLike = WechatMiniprogram.RenderingContext & {
    scale: (x: number, y: number) => void;
};

export interface CanvasInitResult {
    canvas: WechatMiniprogram.Canvas;
    ctx: Canvas2DContextLike;
    width: number;
    height: number;
    dpr: number;
}

/**
 * 与架构文档 ICanvasAdapter 对齐的小程序实现
 */
export interface ICanvasAdapter {
    getCanvas(): WechatMiniprogram.Canvas;
    getContext(): Canvas2DContextLike;
    getSize(): { width: number; height: number; dpr: number };
    setSize(width: number, height: number): void;
    loadImage(src: string): Promise<WechatMiniprogram.Image>;
    toDataURL(type?: 'png' | 'jpg', quality?: number): Promise<string>;
    toBlob(): Promise<unknown>;
    toTempFilePath(options?: {
        quality?: number;
        fileType?: 'png' | 'jpg';
    }): Promise<string>;
}

function createQuery(host?: QueryHost): WechatMiniprogram.SelectorQuery {
    if (host && typeof host.createSelectorQuery === 'function') {
        return host.createSelectorQuery();
    }
    return wx.createSelectorQuery();
}

function getDpr(): number {
    try {
        return Math.max(1, wx.getSystemInfoSync().pixelRatio || 1);
    } catch {
        return 1;
    }
}

function ensureAspectRatio(aspectRatio: number): number {
    if (!Number.isFinite(aspectRatio) || aspectRatio <= 0) return 1;
    return aspectRatio;
}

/**
 * 初始化 Canvas 节点
 * @param selector Canvas 选择器 (如 '#canvasContent')
 * @param wrapperSelector 容器选择器 (如 '#canvasWrapper')
 * @param aspectRatio 宽高比 (width / height)
 * @param host 可选：页面/组件实例，组件内调用建议传入 this
 */
export function initCanvas(
    selector: string,
    wrapperSelector: string,
    aspectRatio: number,
    host?: QueryHost,
): Promise<CanvasInitResult> {
    return new Promise((resolve, reject) => {
        const ratio = ensureAspectRatio(aspectRatio);
        createQuery(host)
            .select(wrapperSelector)
            .boundingClientRect((rect) => {
                if (!rect || !rect.width) {
                    reject(new Error('Canvas wrapper not found'));
                    return;
                }

                const boxWidth = rect.width;
                const boxHeight = boxWidth / ratio;

                createQuery(host)
                    .select(selector)
                    .fields({ node: true, size: true })
                    .exec((res) => {
                        if (!res || !res[0] || !res[0].node) {
                            reject(new Error('Canvas node not found'));
                            return;
                        }

                        const canvas = res[0].node as WechatMiniprogram.Canvas;
                        const ctx = canvas.getContext(
                            '2d',
                        ) as Canvas2DContextLike;
                        const dpr = getDpr();

                        canvas.width = boxWidth * dpr;
                        canvas.height = boxHeight * dpr;
                        ctx.scale(dpr, dpr);

                        resolve({
                            canvas,
                            ctx,
                            width: boxWidth,
                            height: boxHeight,
                            dpr,
                        });
                    });
            })
            .exec();
    });
}

/**
 * Canvas 导出为临时文件路径
 */
export function canvasToTempFilePath(
    canvas: WechatMiniprogram.Canvas,
    options?: { quality?: number; fileType?: 'png' | 'jpg' },
): Promise<string> {
    return new Promise((resolve, reject) => {
        wx.canvasToTempFilePath({
            canvas,
            fileType: options?.fileType || 'png',
            quality: options?.quality ?? 1,
            success: (res) => resolve(res.tempFilePath),
            fail: (err) => reject(err),
        });
    });
}

/**
 * 加载图片到 Canvas
 */
export function loadImage(
    canvas: WechatMiniprogram.Canvas,
    src: string,
): Promise<WechatMiniprogram.Image> {
    return new Promise((resolve, reject) => {
        const img = canvas.createImage();
        img.onload = () => resolve(img);
        img.onerror = (err) => reject(err);
        img.src = src;
    });
}

/**
 * 通过初始化结果创建微信 Canvas 适配器实例
 */
export function createWxCanvasAdapter(init: CanvasInitResult): ICanvasAdapter {
    let width = init.width;
    let height = init.height;
    let dpr = init.dpr;
    let scaled = true;

    const applyScale = () => {
        if (!scaled) {
            init.ctx.scale(dpr, dpr);
            scaled = true;
        }
    };

    return {
        getCanvas() {
            return init.canvas;
        },
        getContext() {
            return init.ctx;
        },
        getSize() {
            return { width, height, dpr };
        },
        setSize(nextWidth: number, nextHeight: number) {
            width = Math.max(1, nextWidth);
            height = Math.max(1, nextHeight);
            dpr = getDpr();
            init.canvas.width = width * dpr;
            init.canvas.height = height * dpr;
            scaled = false;
            applyScale();
        },
        loadImage(src: string) {
            return loadImage(init.canvas, src);
        },
        async toDataURL(type = 'png', quality = 1) {
            const maybeCanvas = init.canvas as unknown as {
                toDataURL?: (mimeType?: string, q?: number) => string;
            };
            if (typeof maybeCanvas.toDataURL === 'function') {
                const mimeType = type === 'jpg' ? 'image/jpeg' : 'image/png';
                return maybeCanvas.toDataURL(mimeType, quality);
            }
            throw new Error('toDataURL is not supported in current runtime');
        },
        async toBlob() {
            throw new Error('toBlob is not supported in WeChat Mini Program');
        },
        toTempFilePath(options) {
            return canvasToTempFilePath(init.canvas, options);
        },
    };
}
