import { _decorator, Component, Vec3 } from 'cc';
import {
    Direction, GameState, GridType, MoveState, MoverRole, addDirection,
} from '../core/Define';
import { GameManager } from '../manager/GameManager';
import { markIsoDrawOrderDirty, resolveLevelDrawRoot } from '../level/TileLayout';
import { scaledMoveSpeed, UNITY_VEHICLE_MOVE_SPEED } from '../core/GridConstants';
import { cellToWorldCenter } from '../core/GridCoords';
import {
    entityWorldPositionForRole,
    moverLogicalGridPositionForRole,
    roleUsesPlayerStandOffset,
    worldToMoverCellForRole,
} from '../level/EntitySpawnPlacement';
import { checkMoveStep, MoveCheckResult } from './MoveRules';

const { ccclass, property } = _decorator;

/**
 * 离散格子移动基类（对齐 Unity Movement）
 * - 查表判定能否进入下一格
 * - FixedUpdate 式插值到 targetPosition
 * - 骑乘联动由 Player/Vehicle 子类覆写
 */
@ccclass('Movement')
export class Movement extends Component {
    private static speedMultiplier = 1;

    static setSpeedMultiplier(m: number) {
        Movement.speedMultiplier = m > 0 ? m : 1;
        if (typeof globalThis !== 'undefined') {
            (globalThis as { __tfrhGameSpeed?: number }).__tfrhGameSpeed = Movement.speedMultiplier;
        }
    }

    static getSpeedMultiplier(): number {
        return Movement.speedMultiplier;
    }

    @property
    moveSpeed = scaledMoveSpeed(UNITY_VEHICLE_MOVE_SPEED);

    direction: Direction = Direction.North;
    moveState: MoveState = MoveState.Idle;
    moverRole: MoverRole = 'player';

    protected targetPosition = new Vec3();
    /** 目标格类型（含 Ride 动态覆盖），对齐 Unity Movement.targetGridType */
    protected targetGridType: GridType = GridType.None;
    protected lastPosition = new Vec3();
    protected step = 0;

    protected moveWait = false;
    protected committedCell: Vec3 | null = null;
    /** 关卡 spawn 逻辑格（优先于从世界坐标反推，避免站立 Y 微调导致格偏移） */
    protected spawnCell: Vec3 | null = null;
    protected landingCell: Vec3 | null = null;
    private moveStepFrom = new Vec3();
    private queue: Promise<void> = Promise.resolve();
    static callEach = false;

    protected getMoverLocalPosition(): Vec3 {
        return this.node.position.clone();
    }

    setSpawnCell(cell: Vec3) {
        this.spawnCell = cell.clone();
        if (!this.committedCell) this.committedCell = new Vec3();
        this.committedCell.set(cell);
    }

    getSpawnCell(): Vec3 | null {
        return this.spawnCell ? this.spawnCell.clone() : null;
    }

    protected syncCommittedCellFromPosition() {
        const gm = GameManager.instance;
        if (!gm) return;
        if (this.spawnCell) {
            if (!this.committedCell) this.committedCell = new Vec3();
            this.committedCell.set(this.spawnCell);
            this.spawnCell = null;
            return;
        }
        const config = gm.getCurLevel();
        const theme = config?.theme ?? gm.uiStyle;
        if (!this.committedCell) this.committedCell = new Vec3();
        const cell = worldToMoverCellForRole(
            this.getMoverLocalPosition(),
            config ?? undefined,
            theme,
            this.moverRole,
        );
        this.committedCell.set(cell);
    }

    public shareCommittedCell(cell: Vec3) {
        if (!this.committedCell) this.committedCell = new Vec3();
        this.committedCell.set(cell);
    }

    getCommittedCell(): Vec3 | null {
        return this.committedCell ? this.committedCell.clone() : null;
    }

    getLandingCell(): Vec3 | null {
        return this.landingCell ? this.landingCell.clone() : null;
    }

    isMoving(): boolean {
        return this.moveState === MoveState.Moving;
    }

    /** 当前移动步进度 0（起步）→ 1（落点） */
    getMoveStepProgress(): number {
        if (this.moveState !== MoveState.Moving) return 1;
        const total = Vec3.distance(this.moveStepFrom, this.targetPosition);
        if (total < 1e-4) return 1;
        const remain = Vec3.distance(this.node.position, this.targetPosition);
        return Math.max(0, Math.min(1, 1 - remain / total));
    }

    /** 逻辑格采样（含动态 Ride）；骑乘时由 PlayerController 覆写 */
    protected getGridSamplePosition(): Vec3 {
        if (this.committedCell) {
            return cellToWorldCenter(this.committedCell);
        }
        const gm = GameManager.instance;
        const local = this.getMoverLocalPosition();
        if (!gm) return local;
        const config = gm.getCurLevel();
        const theme = config?.theme ?? gm.uiStyle;
        if (this.moverRole === 'player' || this.moverRole === 'vehicle') {
            return moverLogicalGridPositionForRole(
                local,
                config ?? undefined,
                theme,
                this.moverRole,
            );
        }
        return local;
    }

    /** 当前格（含 Ride 动态格） */
    get curGrid(): GridType {
        const gm = GameManager.instance!;
        if (this.committedCell) return gm.calculateGridTypeAtCell(this.committedCell);
        return gm.calculateGridType(this.getGridSamplePosition());
    }

    get nextGrid(): GridType {
        const gm = GameManager.instance!;
        if (this.committedCell) return gm.calculateNextGridTypeAtCell(this.committedCell, this.direction);
        return gm.calculateNextGridType(this.getGridSamplePosition(), this.direction);
    }

    get lastGrid(): GridType {
        const gm = GameManager.instance!;
        if (this.committedCell) return gm.calculateLastGridTypeAtCell(this.committedCell, this.direction);
        return gm.calculateLastGridType(this.getGridSamplePosition(), this.direction);
    }

    get isFront(): boolean {
        return this.direction === Direction.South || this.direction === Direction.East;
    }

    start() {
        this.setDirection(this.direction);
        this.syncCommittedCellFromPosition();
    }

    protected commitLandingCell() {
        if (this.landingCell) {
            if (!this.committedCell) this.committedCell = new Vec3();
            this.committedCell.set(this.landingCell);
            this.landingCell = null;
        }
        this.snapMoverToCellStand();
    }

    protected shouldApplyPlayerStandOffset(): boolean {
        return roleUsesPlayerStandOffset(this.moverRole);
    }

    protected snapMoverToCellStand() {
        const gm = GameManager.instance;
        if (!gm || !this.committedCell) return;
        const config = gm.getCurLevel();
        const theme = config?.theme ?? gm.uiStyle;
        const pos = entityWorldPositionForRole(
            this.committedCell,
            config ?? undefined,
            theme,
            this.moverRole,
        );
        this.node.setPosition(pos);
        this.targetPosition.set(pos);
    }

    /** 主题 entityDisplay 变更后重算站立 Y（缩放刷新不会自动更新节点坐标） */
    reapplyCellStandPosition() {
        this.snapMoverToCellStand();
        this.markDrawOrderDirty();
    }

    resetMoveRuntime() {
        this.moveState = MoveState.Idle;
        this.moveWait = false;
        this.step = 0;
        this.queue = Promise.resolve();
    }

    update(dt: number) {
        if (this.moveState !== MoveState.Moving) return;
        const pos = this.node.position;
        const next = new Vec3();
        const speedMul = GameManager.instance?.getGameSpeed() ?? Movement.speedMultiplier;
        Vec3.moveTowards(next, pos, this.targetPosition, this.moveSpeed * dt * speedMul);
        this.node.setPosition(next);
        this.onMoving();
        this.syncRideAfterMoveStep();
        if (Vec3.distance(next, this.targetPosition) < 0.005) {
            this.node.setPosition(this.targetPosition);
            this.moveState = MoveState.Idle;
            this.moveWait = false;
            this.onMoveToTarget();
            this.markDrawOrderDirty();
        }
    }

    private markDrawOrderDirty() {
        const root = resolveLevelDrawRoot(this.node);
        if (root) markIsoDrawOrderDirty(root);
    }

    protected syncRideAfterMoveStep() {}

    setDirection(dir: Direction) {
        this.direction = dir;
    }

    protected onMoving() {}
    protected onMoveToTarget() {
        this.commitLandingCell();
    }
    /** 转向结束（Unity RotateCoroutine 只改朝向，不重新 snap 落点） */
    protected onRotateComplete() {}
    protected onMoveNextSet(_isJump: boolean) {}
    protected onMoveFail(_isJump: boolean) {}
    protected playMoveAnim() {}

    /** 本步移动起点（子类可去掉骑乘视觉抬高等） */
    protected resolveMoveStepOrigin(_targetCell: Vec3, _landingGrid: GridType): Vec3 {
        return this.node.position.clone();
    }

    /** 本步移动落点世界坐标 */
    protected resolveTargetWorldPosition(targetCell: Vec3, landingGrid: GridType): Vec3 {
        const gm = GameManager.instance!;
        const config = gm.getCurLevel();
        const theme = config?.theme ?? gm.uiStyle;
        return entityWorldPositionForRole(
            targetCell,
            config ?? undefined,
            theme,
            this.moverRole,
        );
    }

    protected extraMoveStepCheck(_isJump: boolean, _toFront: boolean): MoveCheckResult | null {
        return null;
    }

    private moveNextCheck(isJump: boolean, toFront: boolean): number {
        const gm = GameManager.instance!;
        const mult = gm.isMultMode();
        const dir = toFront ? this.direction : addDirection(this.direction, 2);
        const nextG = toFront ? this.nextGrid : this.lastGrid;
        const blocked = this.extraMoveStepCheck(isJump, toFront);
        if (blocked !== null) return blocked;
        const r = checkMoveStep(this.moverRole, this.curGrid, nextG, isJump, mult);
        if (r !== 1) return r;

        const targetTmp = gm.nextGridPosition(this.getGridSamplePosition(), dir);
        const targetCell = gm.worldToCell(targetTmp);
        if (mult && nextG === GridType.Ride) {
            const rideNode = gm.getGameObjectAtCell(targetCell);
            const expectedVehicle = this.node.name.replace('Player', 'Vehicle');
            if (rideNode && rideNode.name !== expectedVehicle) return 0;
        }
        if (!this.landingCell) this.landingCell = new Vec3();
        this.landingCell.set(targetCell);

        const landingGrid = gm.calculateGridTypeAtCell(targetCell);
        const targetPos = this.resolveTargetWorldPosition(targetCell, landingGrid);
        this.targetGridType = landingGrid;
        this.moveStepFrom.set(this.resolveMoveStepOrigin(targetCell, landingGrid));
        this.targetPosition.set(targetPos);
        return 1;
    }

    private enqueue(fn: () => Promise<void>) {
        this.queue = this.queue.then(fn).catch((e) => console.error(e));
    }

    private waitUntil(cond: () => boolean): Promise<void> {
        return new Promise((resolve) => {
            const tick = () => {
                if (cond()) resolve();
                else requestAnimationFrame(tick);
            };
            tick();
        });
    }

    private async moveCoroutine(n: number, isJump: boolean) {
        await this.waitUntil(() => !this.moveWait);
        const toFront = n > 0;
        this.step = Math.abs(n);
        const gm = GameManager.instance!;
        while (this.step > 0 && gm.gameState === GameState.Run) {
            this.step--;
            const r = this.moveNextCheck(isJump, toFront);
            if (r === -1) {
                this.onMoveFail(isJump);
                return;
            }
            if (r === 1) {
                this.moveWait = true;
                this.lastPosition.set(this.getGridSamplePosition());
                this.moveState = MoveState.Moving;
                this.onMoveNextSet(isJump);
                // 起步 dirty：角色朝前会切落点；载具仍按起点，人车可能同帧都要重排
                this.markDrawOrderDirty();
                await this.waitUntil(() => !this.moveWait);
            } else {
                this.playMoveAnim();
                this.moveState = MoveState.Moving;
                // 本步不可走但步数已消耗：须回传 processData / processVehicleData
                if (this.step === 0) {
                    this.moveState = MoveState.Idle;
                    this.onMoveToTarget();
                }
            }
        }
    }

    private async rotateCoroutine(n: number) {
        await this.waitUntil(() => !this.moveWait);
        this.moveWait = true;
        this.setDirection(addDirection(this.direction, n));
        this.moveWait = false;
        this.onRotateComplete();
        // 对齐 Unity RotateCoroutine → OnMoveToTarget → ExternalCall（Scratch/Python 等待 processData）
        this.onMoveToTarget();
    }

    protected jsCallCheck(n: number): boolean {
        const gm = GameManager.instance!;
        const name = this.node.name;
        if (
            name === 'Player' || name === 'Vehicle'
            || /^Player[AB]\d$/.test(name) || /^Vehicle[AB]\d$/.test(name)
        ) {
            return gm.jsCallCheck(n);
        }
        return false;
    }

    callMove(n: number) {
        if (!this.jsCallCheck(n)) return;
        this.enqueue(() => this.moveCoroutine(n, false));
    }

    callRotateLeft(n: number) {
        if (!this.jsCallCheck(n)) return;
        this.enqueue(() => this.rotateCoroutine(-n));
    }

    callRotateRight(n: number) {
        if (!this.jsCallCheck(n)) return;
        this.enqueue(() => this.rotateCoroutine(n));
    }

    callJump() {
        if (this.moverRole !== 'player') return;
        if (!this.jsCallCheck(1)) return;
        this.enqueue(() => this.moveCoroutine(1, true));
    }
}
