/**
 * Lightweight `cc` module mock used by Jest to allow platform-agnostic
 * modules (`common/*`, `data/*`, some `logic/*`) to be unit-tested without
 * pulling in the full Cocos Creator engine.
 *
 * Anything that actually touches `cc.Node` / scene graph must **not** live
 * in a module that is directly tested by Jest; put it behind a thin facade
 * instead (see `common/StorageMgr.ts` which isolates `sys.localStorage`).
 */

class FakeEventTarget {
    private listeners = new Map<string, Array<(...args: unknown[]) => void>>();

    on(event: string, cb: (...args: unknown[]) => void): void {
        const list = this.listeners.get(event) ?? [];
        list.push(cb);
        this.listeners.set(event, list);
    }

    off(event: string, cb?: (...args: unknown[]) => void): void {
        if (!cb) {
            this.listeners.delete(event);
            return;
        }
        const list = this.listeners.get(event);
        if (!list) return;
        this.listeners.set(
            event,
            list.filter((fn) => fn !== cb)
        );
    }

    emit(event: string, ...args: unknown[]): void {
        const list = this.listeners.get(event);
        if (!list) return;
        for (const fn of list) {
            fn(...args);
        }
    }
}

export const EventTarget = FakeEventTarget;

export const _decorator = {
    ccclass: (_name?: string) => (target: unknown) => target as any,
    property: (_opts?: unknown) => (_target: unknown, _key: string) => {},
};

/**
 * Minimal stand-in for `cc.Node`. Only the surface actually exercised by
 * Scene Entry components is modelled so unit tests stay deterministic.
 */
export class Node {
    public name: string = '';
    public active: boolean = true;
    public layer: number = 0;

    constructor(name?: string) {
        this.name = name ?? '';
    }

    public addChild(_child: Node): void {}
    public on(_ev: string, _cb: (...args: unknown[]) => void, _target?: unknown): void {}
    public off(_ev: string, _cb?: (...args: unknown[]) => void): void {}
    public getComponent<T>(_ctor: new (...args: unknown[]) => T): T | null { return null; }
    public addComponent<T>(_ctor: new (...args: unknown[]) => T): T { return {} as T; }
    public setPosition(..._args: unknown[]): void {}

    public static EventType = {
        TOUCH_START: 'touch-start',
        TOUCH_MOVE: 'touch-move',
        TOUCH_END: 'touch-end',
        TOUCH_CANCEL: 'touch-cancel',
    };
}

/**
 * Minimal stand-in for `cc.Component`. Provides `.node` so Scene Entry TS
 * types resolve. Runtime behaviour is injected by the Cocos Creator editor.
 */
export class Component {
    public node: Node = new Node();
    public enabled: boolean = true;
}
export const director = {
    addPersistRootNode: (..._args: unknown[]) => {},
    loadScene: (..._args: unknown[]) => {},
};
export const view = {
    setDesignResolutionSize: (..._args: unknown[]) => {},
};
export const screen = {
    orientation: 0,
};
export const game = {
    frameRate: 60,
};
export const sys = {
    localStorage: {
        _store: new Map<string, string>(),
        getItem(key: string): string | null {
            return (sys.localStorage as any)._store.get(key) ?? null;
        },
        setItem(key: string, value: string): void {
            (sys.localStorage as any)._store.set(key, value);
        },
        removeItem(key: string): void {
            (sys.localStorage as any)._store.delete(key);
        },
        clear(): void {
            (sys.localStorage as any)._store.clear();
        },
    },
};
export const settings = {};
export const Settings = {};
export const profiler = {};

// ---------------------------------------------------------------------
// Additional stubs for types imported by Scene Entry components.  All the
// methods below are NO-OPs; the Cocos Creator editor injects the real
// implementations at runtime. Having them declared here keeps the TS
// language server happy and lets Jest exercise scene-entry unit tests.
// ---------------------------------------------------------------------

export class Color {
    public r: number;
    public g: number;
    public b: number;
    public a: number;
    constructor(r: number = 255, g: number = 255, b: number = 255, a: number = 255) {
        this.r = r; this.g = g; this.b = b; this.a = a;
    }
    public static WHITE = new Color(255, 255, 255, 255);
    public static BLACK = new Color(0, 0, 0, 255);
}

export class Vec3 {
    public x: number;
    public y: number;
    public z: number;
    constructor(x: number = 0, y: number = 0, z: number = 0) {
        this.x = x; this.y = y; this.z = z;
    }
}

export class UITransform {
    public width: number = 0;
    public height: number = 0;
    public setContentSize(_w: number, _h: number): void {}
}

export class Label {
    public string: string = '';
    public fontSize: number = 24;
    public lineHeight: number = 28;
    public color: Color = Color.WHITE;
    public horizontalAlign: number = 0;
    public verticalAlign: number = 0;
    public useSystemFont: boolean = true;
}

export class Button {
    public transition: number = 0;
    public target: Node | null = null;
    public zoomScale: number = 1;

    public static Transition = { NONE: 0, COLOR: 1, SPRITE: 2, SCALE: 3 };
}

export class Graphics {
    public fillColor: Color = Color.WHITE;
    public strokeColor: Color = Color.BLACK;
    public lineWidth: number = 1;
    public rect(_x: number, _y: number, _w: number, _h: number): void {}
    public fill(): void {}
    public stroke(): void {}
}

export class Sprite {
    public spriteFrame: unknown = null;
    public type: number = 0;
    public sizeMode: number = 0;
    public static Type = { SIMPLE: 0, SLICED: 1, TILED: 2, FILLED: 3 };
    public static SizeMode = { CUSTOM: 0, TRIMMED: 1, RAW: 2 };
}

export class SpriteFrame {
    public texture: unknown = null;
}

export class Texture2D {
    public image: unknown = null;
    public static PixelFormat = { RGBA8888: 35 };
}

export class ImageAsset {
    constructor(_opts?: unknown) {}
}

export class JsonAsset {
    public json: unknown = null;
}

export class Canvas {}
export class EventTouch {}

export const resources = {
    load: (_path: string, _type: unknown, _cb: (err: Error | null, asset: unknown) => void): void => {},
};
