import {
    _decorator, Button, Component, director, find,
    Label, Node, Sprite, SpriteFrame, UITransform, view, AudioSource, Color, Layers,
    tween, Tween, Vec3, Widget,
} from 'cc';
import { EventManager, EventType } from '../core/EventManager';
import { GameManager } from '../manager/GameManager';
import { ViewController } from '../controller/ViewController';
import { LineGridRenderer } from '../gameplay/LineGridRenderer';
import { Movement } from '../gameplay/Movement';
import { GameAudio } from '../audio/GameAudio';
import { loadThemeCharacterPortrait, loadUIIcon, UIIconKey } from './UIStyleAssets';
import {
    getThemeHudIconScale, getThemePortraitFlipX, getThemePortraitScale,
} from '../theme/ThemeRegistry';
import { DESIGN_HEIGHT, DESIGN_WIDTH } from '../core/GridConstants';
import { syncEmbeddedCamerasOrtho } from '../core/EmbeddedView';
import { spriteOriginalSize } from '../visual/EntityDisplayRefs';

const { ccclass } = _decorator;

type IconSlot = { node: Node; sprite: Sprite };
type BtnLayout = { node: Node };

/** Unity UIMain/Right：VerticalLayout spacing=20, padding right=20 top=20, 按钮 165×165 */
const UNITY_BTN = 165;
const UNITY_SPACING = 20;
const UNITY_PAD_RIGHT = 20;
const UNITY_PAD_TOP = 20;
/** Unity UIMain/ImageBall：左上角角色肖像 194×194，距左 40、距顶 28 */
const UNITY_PORTRAIT = 194;
const UNITY_PAD_LEFT = 40;
const UNITY_PAD_TOP_PORTRAIT = 28;
const REF_WIDTH = 2560;

const scaleUi = (v: number) => v * (DESIGN_WIDTH / REF_WIDTH);

const BTN_SIZE = Math.round(scaleUi(UNITY_BTN));
const BTN_SPACING = Math.round(scaleUi(UNITY_SPACING));
const PAD_RIGHT = Math.round(scaleUi(UNITY_PAD_RIGHT));
const PAD_TOP = Math.round(scaleUi(UNITY_PAD_TOP));
const PORTRAIT_SIZE = Math.round(scaleUi(UNITY_PORTRAIT));
const PAD_LEFT = Math.round(scaleUi(UNITY_PAD_LEFT));
const PAD_TOP_PORTRAIT = Math.round(scaleUi(UNITY_PAD_TOP_PORTRAIT));

/** 点击时短暂放大再回弹 */
const BTN_POP_SCALE = 1.14;
const BTN_POP_UP = 0.07;
const BTN_POP_DOWN = 0.13;

const HUD_LAYER = Layers.Enum.UI_3D;

/** 左上角肖像等比缩放系数（统一高度优先，与 Unity ImageBall 194 框一致） */
function portraitUniformScale(sf: SpriteFrame, scaleMul = 1): number {
    const max = PORTRAIT_SIZE * scaleMul;
    const { width: ow, height: oh } = spriteOriginalSize(sf);
    return Math.min(max / ow, max / oh);
}

/** 对齐 Unity UIMain：右侧 6 个圆形功能按钮（HUD 相机固定，不随关卡缩放） */
@ccclass('UIMain')
export class UIMain extends Component {
    private static readonly SPEEDS = [1, 2, 4];
    private speedIndex = 0;
    private audioMute = false;
    private textVisible = false;

    private nodePlaySpeed: IconSlot | null = null;
    private nodeAudio: IconSlot | null = null;
    private portraitSprite: Sprite | null = null;
    private textLabel: Label | null = null;
    private uiBuilt = false;
    private readonly buttons: BtnLayout[] = [];
    /** 忽略过期的异步贴图回调，避免重置关卡时 HUD 闪一下 */
    private hudStyleGen = 0;

    static ensure(parent: Node): UIMain {
        parent.getChildByName('UIMain')?.destroy();
        const root = new Node('UIMain');
        root.parent = parent;
        root.layer = HUD_LAYER;
        return root.addComponent(UIMain);
    }

    static findInstance(): UIMain | null {
        const scene = director.getScene();
        if (!scene) return null;
        for (const rootName of ['UIOverlay', 'GameRoot', 'Canvas']) {
            const ui = scene.getChildByName(rootName)?.getChildByName('UIMain')?.getComponent(UIMain);
            if (ui) return ui;
        }
        return null;
    }

    setMuted(mute: boolean) {
        if (this.audioMute === mute) return;
        this.audioMute = mute;
        this.applyAudioVolume();
        void this.setAudioIcon(this.resolveHudTheme());
    }

    onLoad() {
        const speed = UIMain.SPEEDS[this.speedIndex];
        GameManager.instance?.setGameSpeed(speed);
        Movement.setSpeedMultiplier(speed);
        this.buildUI();
        EventManager.register(EventType.LevelInit, this.onLevelInit);
    }

    onDestroy() {
        view.off('canvas-resize', this.layoutPanel, this);
        EventManager.remove(EventType.LevelInit, this.onLevelInit);
    }

    SetText(str: string) { this.setText(str); }
    SetTextActive(active: string) { this.setTextActive(active); }

    setText(str: string) {
        if (this.textLabel) {
            this.textLabel.string = str;
            if (this.textLabel.node.parent) {
                this.textLabel.node.parent.active = this.textVisible && str.length > 0;
            }
        }
    }

    setTextActive(active: string) {
        this.textVisible = active === 'true';
        if (this.textLabel?.node.parent) {
            this.textLabel.node.parent.active = this.textVisible && (this.textLabel.string?.length ?? 0) > 0;
        }
    }

    refreshStyle(uiStyle?: string) {
        const style = uiStyle ?? this.resolveHudTheme();
        const gen = ++this.hudStyleGen;
        void this.setPlaySpeedSprite(style, gen);
        void this.setAudioIcon(style, gen);
        const keys: UIIconKey[] = ['navigation', 'revert', 'zoomIn', 'zoomOut'];
        const names = ['NodeNavigation', 'NodeRevert', 'NodeZoomIn', 'NodeZoomOut'];
        names.forEach((name, i) => {
            const slot = this.node.getChildByName(name)?.getChildByName('Icon')?.getComponent(Sprite);
            if (!slot) return;
            void loadUIIcon(style, keys[i]).then((sf) => {
                if (gen !== this.hudStyleGen || !sf || !slot.isValid) return;
                slot.spriteFrame = sf;
            });
        });
        this.applyHudIconScale(style);
        void this.refreshThemePortrait(style, gen);
    }

    /** Unity redArmy 等主题的按钮图标缩放 */
    private applyHudIconScale(themeId?: string) {
        const { x, y } = getThemeHudIconScale(themeId);
        for (const { node } of this.buttons) {
            const icon = node.getChildByName('Icon');
            if (icon?.isValid) icon.setScale(x, y, 1);
        }
    }

    private buildUI() {
        if (this.uiBuilt) {
            this.layoutPanel();
            return;
        }
        this.uiBuilt = true;

        const rootUi = this.node.getComponent(UITransform) ?? this.node.addComponent(UITransform);
        rootUi.setAnchorPoint(1, 1);
        this.setupButtonColumnWidget();

        const specs: { name: string; onClick: () => void }[] = [
            { name: 'NodeNavigation', onClick: () => this.onToggleLineGrid() },
            { name: 'NodeRevert', onClick: () => this.onRevertGame() },
            { name: 'NodePlaySpeed', onClick: () => this.onPlaySpeedChange() },
            { name: 'NodeZoomIn', onClick: () => this.onZoomIn() },
            { name: 'NodeZoomOut', onClick: () => this.onZoomOut() },
            { name: 'NodeAudio', onClick: () => this.onAudioMute() },
        ];

        this.buttons.length = 0;
        for (const spec of specs) {
            const slot = this.ensureIconButton(spec.name, spec.onClick);
            if (spec.name === 'NodePlaySpeed') this.nodePlaySpeed = slot;
            if (spec.name === 'NodeAudio') this.nodeAudio = slot;
        }

        this.buildTextArea();
        this.buildThemePortrait();
        this.node.setSiblingIndex(this.node.parent!.children.length - 1);
        this.layoutPanel();
        view.on('canvas-resize', this.layoutPanel, this);
        this.refreshStyle(this.resolveHudTheme());
    }

    /** HUD 贴图始终跟当前关卡主题，避免倍速/音量误用 uiStyle 默认 silu */
    private resolveHudTheme(): string | undefined {
        return GameManager.instance?.getCurLevel()?.theme ?? GameManager.instance?.uiStyle;
    }

    private syncOverlaySize() {
        const vis = view.getVisibleSize();
        const overlay = this.node.parent;
        const ui = overlay?.getComponent(UITransform);
        if (!ui) return;
        if (ui.contentSize.width !== vis.width || ui.contentSize.height !== vis.height) {
            ui.setContentSize(vis.width, vis.height);
        }
        this.ensureOverlayWidget(overlay);
    }

    private ensureOverlayWidget(overlay: Node) {
        const widget = overlay.getComponent(Widget) ?? overlay.addComponent(Widget);
        widget.isAlignTop = true;
        widget.isAlignBottom = true;
        widget.isAlignLeft = true;
        widget.isAlignRight = true;
        widget.top = 0;
        widget.bottom = 0;
        widget.left = 0;
        widget.right = 0;
        widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
        widget.updateAlignment();
    }

    private setupButtonColumnWidget() {
        const widget = this.node.getComponent(Widget) ?? this.node.addComponent(Widget);
        widget.isAlignRight = true;
        widget.isAlignTop = true;
        widget.right = PAD_RIGHT;
        widget.top = PAD_TOP;
        widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
    }

    private setupPortraitWidget(portrait: Node) {
        const widget = portrait.getComponent(Widget) ?? portrait.addComponent(Widget);
        widget.isAlignTop = true;
        widget.isAlignLeft = true;
        widget.top = PAD_TOP_PORTRAIT;
        widget.left = PAD_LEFT;
        widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
    }

    private layoutPanel = () => {
        syncEmbeddedCamerasOrtho();
        this.syncOverlaySize();

        const count = this.buttons.length;
        const totalHeight = count > 0
            ? count * BTN_SIZE + (count - 1) * BTN_SPACING
            : 0;

        const rootUi = this.node.getComponent(UITransform)!;
        rootUi.setContentSize(BTN_SIZE, totalHeight);

        const btnX = -BTN_SIZE * 0.5;
        for (let i = 0; i < count; i++) {
            const y = -BTN_SIZE * 0.5 - i * (BTN_SIZE + BTN_SPACING);
            this.buttons[i].node.setPosition(btnX, y, 0);
        }

        this.node.getComponent(Widget)?.updateAlignment();

        const h = view.getVisibleSize().height || DESIGN_HEIGHT;
        const texts = this.node.parent?.getChildByName('NodeTexts');
        if (texts) texts.setPosition(0, -h * 0.5 + 36, 0);

        const portrait = this.portraitSprite?.node.parent
            ?? this.node.parent?.getChildByName('ImageBall');
        portrait?.getComponent(Widget)?.updateAlignment();
    };

    private ensureIconButton(name: string, onClick: () => void): IconSlot {
        let btnNode = this.node.getChildByName(name);
        if (!btnNode) {
            btnNode = new Node(name);
            btnNode.parent = this.node;
        }
        btnNode.layer = HUD_LAYER;
        btnNode.active = true;

        const ui = btnNode.getComponent(UITransform) ?? btnNode.addComponent(UITransform);
        ui.setContentSize(BTN_SIZE, BTN_SIZE);
        ui.setAnchorPoint(0.5, 0.5);

        let iconNode = btnNode.getChildByName('Icon');
        if (!iconNode) {
            iconNode = new Node('Icon');
            iconNode.parent = btnNode;
        }
        iconNode.layer = HUD_LAYER;
        const iconUi = iconNode.getComponent(UITransform) ?? iconNode.addComponent(UITransform);
        iconUi.setContentSize(BTN_SIZE, BTN_SIZE);
        const sprite = iconNode.getComponent(Sprite) ?? iconNode.addComponent(Sprite);
        sprite.sizeMode = Sprite.SizeMode.CUSTOM;

        const btn = btnNode.getComponent(Button) ?? btnNode.addComponent(Button);
        btn.transition = Button.Transition.NONE;
        btn.target = iconNode;
        btnNode.off(Button.EventType.CLICK);
        btnNode.on(Button.EventType.CLICK, () => {
            GameAudio.resumeAll();
            this.playClickPop(btnNode);
            onClick();
        }, this);

        this.buttons.push({ node: btnNode });
        return { node: btnNode, sprite };
    }

    /** 点击反馈：只缩放 Icon，避免整颗按钮放大遮挡下方倍速键 */
    private playClickPop(target: Node) {
        if (!target.isValid) return;
        const popTarget = target.getChildByName('Icon') ?? target;
        const { x, y } = getThemeHudIconScale(this.resolveHudTheme());
        const base = new Vec3(x, y, 1);
        Tween.stopAllByTarget(popTarget);
        popTarget.setScale(base);
        const pop = new Vec3(x * BTN_POP_SCALE, y * BTN_POP_SCALE, 1);
        tween(popTarget)
            .to(BTN_POP_UP, { scale: pop }, { easing: 'quadOut' })
            .to(BTN_POP_DOWN, { scale: base }, { easing: 'backOut' })
            .start();
    }

    /** 对齐 Unity UIMain/ImageBall：关卡主题角色待机贴图 */
    private buildThemePortrait() {
        const overlay = this.node.parent;
        if (!overlay) return;

        let portrait = overlay.getChildByName('ImageBall');
        if (!portrait) {
            portrait = new Node('ImageBall');
            portrait.parent = overlay;
        }
        portrait.layer = HUD_LAYER;
        portrait.active = true;
        portrait.getComponent(Sprite)?.destroy();

        const ui = portrait.getComponent(UITransform) ?? portrait.addComponent(UITransform);
        ui.setAnchorPoint(0, 1);

        let icon = portrait.getChildByName('Portrait');
        if (!icon) {
            icon = new Node('Portrait');
            icon.parent = portrait;
        }
        icon.layer = HUD_LAYER;
        const iconUi = icon.getComponent(UITransform) ?? icon.addComponent(UITransform);
        iconUi.setAnchorPoint(0, 1);

        const sprite = icon.getComponent(Sprite) ?? icon.addComponent(Sprite);
        sprite.sizeMode = Sprite.SizeMode.TRIMMED;
        this.portraitSprite = sprite;
        this.setupPortraitWidget(portrait);
    }

    private applyPortraitSprite(sf: SpriteFrame, themeId?: string) {
        if (!this.portraitSprite?.isValid) return;
        const icon = this.portraitSprite.node;
        const container = icon.parent;
        if (!container?.isValid) return;

        const theme = themeId ?? this.resolveHudTheme();
        const s = portraitUniformScale(sf, getThemePortraitScale(theme));
        const { width: ow, height: oh } = spriteOriginalSize(sf);
        const flipX = getThemePortraitFlipX(theme);
        const dispW = Math.round(ow * s);
        const dispH = Math.round(oh * s);
        const iconUi = icon.getComponent(UITransform) ?? icon.addComponent(UITransform);

        this.portraitSprite.spriteFrame = sf;
        if (flipX) {
            // 绕右缘翻转，避免 scaleX<0 时贴图画到屏幕左侧外
            iconUi.setAnchorPoint(1, 1);
            icon.setPosition(dispW, 0, 0);
            icon.setScale(-s, s, 1);
        } else {
            iconUi.setAnchorPoint(0, 1);
            icon.setPosition(0, 0, 0);
            icon.setScale(s, s, 1);
        }

        const containerUi = container.getComponent(UITransform) ?? container.addComponent(UITransform);
        containerUi.setAnchorPoint(0, 1);
        containerUi.setContentSize(dispW, dispH);

        container.getComponent(Widget)?.updateAlignment();
    }

    private async refreshThemePortrait(themeId?: string, gen = this.hudStyleGen) {
        if (!this.portraitSprite) return;
        const gm = GameManager.instance;
        const theme = themeId ?? gm?.getCurLevel()?.theme ?? gm?.uiStyle;
        const options = gm?.getEntityVisualOptions() ?? { theme };
        const sf = await loadThemeCharacterPortrait({ ...options, theme: options.theme ?? theme });
        if (gen !== this.hudStyleGen || !sf || !this.portraitSprite?.isValid) return;
        this.applyPortraitSprite(sf, theme);
    }

    private buildTextArea() {
        let texts = this.node.parent?.getChildByName('NodeTexts');
        if (!texts) {
            texts = new Node('NodeTexts');
            texts.parent = this.node.parent!;
            texts.layer = HUD_LAYER;
            texts.addComponent(UITransform).setContentSize(400, 48);
            texts.active = false;

            const labelNode = new Node('Text1');
            labelNode.parent = texts;
            labelNode.addComponent(UITransform).setContentSize(400, 48);
            this.textLabel = labelNode.addComponent(Label);
            this.textLabel.fontSize = 22;
            this.textLabel.color = new Color(255, 240, 200);
            this.textLabel.horizontalAlign = Label.HorizontalAlign.CENTER;
        } else {
            this.textLabel = texts.getChildByName('Text1')?.getComponent(Label) ?? null;
        }
    }

    private onToggleLineGrid() {
        const entrance = this.resolveLevelEntrance();
        if (!entrance) {
            console.warn('[UIMain] 未找到 MainLevelEntrance，无法切换网格');
            return;
        }
        const grid = LineGridRenderer.instance?.isValid
            ? LineGridRenderer.instance
            : LineGridRenderer.ensure(entrance);
        grid.toggleGridVisibility();
        console.log(`[UIMain] 导航网格 ${grid.isGridVisible() ? '显示' : '隐藏'}`);
    }

    private resolveLevelEntrance(): Node | null {
        const gm = GameManager.instance;
        if (gm?.mainLevelEntrance?.isValid) return gm.mainLevelEntrance;
        const scene = director.getScene();
        if (!scene) return null;
        return find('MainLevelEntrance', scene)
            ?? find('GameRoot/MainLevelEntrance', scene);
    }

    private onRevertGame() {
        this.speedIndex = 0;
        GameManager.instance?.setGameSpeed(UIMain.SPEEDS[this.speedIndex]);
        GameManager.instance?.resetLevel();
        void this.setPlaySpeedSprite(this.resolveHudTheme());
    }

    private onPlaySpeedChange() {
        this.speedIndex = (this.speedIndex + 1) % UIMain.SPEEDS.length;
        const speed = UIMain.SPEEDS[this.speedIndex];
        GameManager.instance?.setGameSpeed(speed);
        console.log(`[UIMain] 倍速 x${speed}`);
        void this.setPlaySpeedSprite(this.resolveHudTheme());
    }

    private onZoomIn() {
        ViewController.instance?.zoomIn();
    }

    private onZoomOut() {
        ViewController.instance?.zoomOut();
    }

    private onAudioMute() {
        this.audioMute = !this.audioMute;
        this.applyAudioVolume();
        void this.setAudioIcon(this.resolveHudTheme());
    }

    private async setPlaySpeedSprite(uiStyle?: string, gen = this.hudStyleGen) {
        const keys: UIIconKey[] = ['speed1', 'speed2', 'speed4'];
        const icon = keys[this.speedIndex];
        const sf = await loadUIIcon(uiStyle ?? this.resolveHudTheme(), icon);
        if (gen !== this.hudStyleGen || !sf || !this.nodePlaySpeed) return;
        this.nodePlaySpeed.sprite.spriteFrame = sf;
    }

    private async setAudioIcon(uiStyle?: string, gen = this.hudStyleGen) {
        const icon: UIIconKey = this.audioMute ? 'audioOff' : 'audioOn';
        const sf = await loadUIIcon(uiStyle ?? this.resolveHudTheme(), icon);
        if (gen !== this.hudStyleGen || !sf || !this.nodeAudio) return;
        this.nodeAudio.sprite.spriteFrame = sf;
    }

    private applyAudioVolume() {
        const vol = this.audioMute ? 0 : 1;
        const scene = director.getScene();
        if (!scene) return;
        for (const src of scene.getComponentsInChildren(AudioSource)) {
            src.volume = vol;
        }
    }

    private onLevelInit = () => {
        this.setText('');
        this.applyAudioVolume();
    };
}
