import { Node, UITransform, Vec3, Layers, Sprite } from 'cc';
import { cellToWorldCenter, parseTileNodeName } from '../core/GridCoords';
import { CommonDefine } from '../core/Define';
import { GameManager } from '../manager/GameManager';
import { getTilePivot } from '../visual/TilePivots';
import { getTileDrawSize, resolveTilePixelSize } from '../visual/TileSizes';
import { LevelConfig } from './LevelTypes';
import { getLevelRuntimeContext } from './LevelRuntimeContext';

const UI_LAYER = Layers.Enum.UI_2D;

/**
 * 遮挡分类（仅在关卡加载时按格子坐标排序一次）
 * - walkable : Baseblock / JumpBlock，永不在 actor 之上
 * - wall     : Border / WallBlock，与 actor 按 iso 深度比较
 * - actor    : 角色 / 载具
 * - pickable : 可拾取物（Prop），永不在墙砖/路径砖之下；玩家仍在其上
 * - scenery  : 其它
 */
type DrawKind = 'walkable' | 'wall' | 'actor' | 'pickable' | 'scenery';

const DRAW_RANK = {
    groundProp: 10,
    groundTile: 20,
    borderTile: 22,
    vehicle: 28,
    coin: 30,
    propDecor: 30,
    player: 40,
} as const;

interface DrawEntry {
    node: Node;
    x: number;
    y: number;
    rank: number;
    kind: DrawKind;
}

function safeNodeName(node: Node | null | undefined): string {
    return (node?.isValid ? node.name : '') || '';
}

function isPlayerEntityName(name: string | undefined | null): boolean {
    if (!name) return false;
    return name === 'Player' || /^Player[AB]\d$/.test(name);
}

function isVehicleEntityName(name: string | undefined | null): boolean {
    if (!name) return false;
    return name === 'Vehicle' || /^Vehicle[AB]\d$/.test(name);
}

function isCoinEntityName(name: string | undefined | null): boolean {
    if (!name) return false;
    return name === 'Prop' || name.startsWith('Prop_');
}

function isActorEntity(node: Node): boolean {
    return isPlayerEntityName(safeNodeName(node))
        || isVehicleEntityName(safeNodeName(node));
}

/** 可拾取金币 / 道具（PropController） */
function isPickableProp(node: Node): boolean {
    if (!node?.isValid) return false;
    if (node.getComponent('PropController')) return true;
    return isCoinEntityName(safeNodeName(node));
}

function isEntityDrawNode(node: Node): boolean {
    const n = safeNodeName(node);
    if (!n) return false;
    return isActorEntity(node) || isPickableProp(node)
        || n === 'PropDecor' || n.startsWith('PropDecor_');
}

function isWalkablePathTile(tileName: string): boolean {
    return tileName === CommonDefine.BlockBase || tileName === CommonDefine.BlockJump;
}

function isWallTileName(tileName: string): boolean {
    return tileName === 'WallBlock' || tileName === 'kuai11';
}

function resolveGroundTileName(cellX: number, cellY: number, config?: LevelConfig): string {
    return config?.ground?.[`${cellX},${cellY}`] ?? CommonDefine.BlockBase;
}

function classifyTileKind(
    parsed: { layer: 'ground' | 'border'; x: number; y: number },
    config?: LevelConfig,
): DrawKind {
    if (parsed.layer === 'border') return 'wall';
    const name = resolveGroundTileName(parsed.x, parsed.y, config);
    if (isWalkablePathTile(name)) return 'walkable';
    if (isWallTileName(name)) return 'wall';
    return 'scenery';
}

function classifyEntityKind(node: Node): DrawKind {
    if (isPickableProp(node)) return 'pickable';
    if (isActorEntity(node)) return 'actor';
    return 'scenery';
}

export interface EntityDrawOrderOptions {
    /** @deprecated 保留字段避免旧调用报错 */
    preferVehicleOverPlayer?: boolean;
}

function entityRank(node: Node): number {
    const n = safeNodeName(node);
    if (isPlayerEntityName(n)) return DRAW_RANK.player;
    if (isVehicleEntityName(n)) return DRAW_RANK.vehicle;
    if (isCoinEntityName(n)) return DRAW_RANK.coin;
    if (n === 'PropDecor' || n.startsWith('PropDecor_')) return DRAW_RANK.propDecor;
    return DRAW_RANK.groundProp;
}

function tileRank(kind: DrawKind, layer: 'ground' | 'border'): number {
    if (kind === 'walkable') return DRAW_RANK.groundTile;
    if (kind === 'wall') return DRAW_RANK.borderTile;
    return layer === 'border' ? DRAW_RANK.borderTile : DRAW_RANK.groundTile;
}

/** 等距深度：返回值 > 0 表示 a 应排在 b 之后（更靠前） */
export function compareIsoDrawOrder(cellX: number, cellY: number, otherX: number, otherY: number): number {
    const ka = cellX + cellY;
    const kb = otherX + otherY;
    if (ka !== kb) return kb - ka;
    return otherX - cellX;
}

function wallFaceSamples(wallX: number, wallY: number): { x: number; y: number }[] {
    return [{ x: wallX, y: wallY - 1 }, { x: wallX - 1, y: wallY }];
}

function compareWallActorIso(actor: DrawEntry, wall: DrawEntry): number {
    let actorAhead = 0;
    for (const face of wallFaceSamples(wall.x, wall.y)) {
        const iso = compareIsoDrawOrder(actor.x, actor.y, face.x, face.y);
        if (iso < 0) return iso;
        if (iso > actorAhead) actorAhead = iso;
    }
    return actorAhead;
}

function compareDrawEntries(a: DrawEntry, b: DrawEntry): number {
    if (a.kind === 'walkable' && b.kind === 'actor') return -1;
    if (b.kind === 'walkable' && a.kind === 'actor') return 1;

    // 可拾取物：永远在墙砖/路径砖/载具之上；玩家仍在其上
    if (a.kind === 'pickable' && b.kind === 'actor') {
        return isPlayerEntityName(safeNodeName(b.node)) ? -1 : 1;
    }
    if (a.kind === 'actor' && b.kind === 'pickable') {
        return isPlayerEntityName(safeNodeName(a.node)) ? 1 : -1;
    }
    if (a.kind === 'pickable' && b.kind !== 'pickable') return 1;
    if (b.kind === 'pickable' && a.kind !== 'pickable') return -1;

    if (a.kind === 'actor' && b.kind === 'wall') {
        const wallIso = compareWallActorIso(a, b);
        if (wallIso !== 0) return wallIso;
    }
    if (a.kind === 'wall' && b.kind === 'actor') {
        const wallIso = compareWallActorIso(b, a);
        if (wallIso !== 0) return -wallIso;
    }

    const iso = compareIsoDrawOrder(a.x, a.y, b.x, b.y);
    if (iso !== 0) return iso;
    return a.rank - b.rank;
}

/** 实体所在关卡根（Ground/Border/Tiles 的父节点，不是 Tiles 容器本身） */
export function resolveLevelDrawRoot(from: Node): Node | null {
    let cur: Node | null = from;
    while (cur?.isValid) {
        if (cur.getChildByName('Ground') || cur.getChildByName('Border') || cur.getChildByName('Tiles')) {
            return cur;
        }
        if (/^Level_\d+/.test(cur.name)) return cur;
        cur = cur.parent;
    }
    return null;
}

/** 实体逻辑格（仅 spawn / committed，移动中不重算） */
function getEntityLogicCell(node: Node): { x: number; y: number } {
    const propCtrl = node.getComponent('PropController') as { getSpawnCell?: () => Vec3 | null } | null;
    const propCell = propCtrl?.getSpawnCell?.();
    if (propCell) return { x: propCell.x, y: propCell.y };

    const m = /^Prop_(-?\d+)_(-?\d+)$/.exec(safeNodeName(node));
    if (m) return { x: Number(m[1]), y: Number(m[2]) };

    const vehicle = node.getComponent('VehicleController') as {
        getPlayer?: () => { getCommittedCell?: () => Vec3 | null; getSpawnCell?: () => Vec3 | null } | null;
    } | null;
    const rider = vehicle?.getPlayer?.() ?? null;
    if (rider) {
        const riderCell = rider.getCommittedCell?.() ?? rider.getSpawnCell?.();
        if (riderCell) return { x: riderCell.x, y: riderCell.y };
    }

    const mov = node.getComponent('Movement') as {
        getCommittedCell?: () => Vec3 | null;
        getSpawnCell?: () => Vec3 | null;
    } | null;
    if (mov) {
        const cell = mov.getCommittedCell?.() ?? mov.getSpawnCell?.();
        if (cell) return { x: cell.x, y: cell.y };
    }

    const gm = GameManager.instance;
    if (gm) {
        const c = gm.worldToCell(node.position);
        return { x: c.x, y: c.y };
    }
    return { x: 0, y: 0 };
}

/** 关卡内实体（可能在 levelRoot 或 Tiles 下） */
export function forEachLevelEntityNode(levelRoot: Node, fn: (node: Node) => void) {
    if (!levelRoot?.isValid) return;
    for (const ch of levelRoot.children) {
        if (ch?.isValid && isEntityDrawNode(ch)) fn(ch);
    }
    const tiles = levelRoot.getChildByName('Tiles');
    if (!tiles?.isValid) return;
    for (const ch of tiles.children) {
        if (ch?.isValid && isEntityDrawNode(ch)) fn(ch);
    }
}

export function findLevelChildByName(levelRoot: Node, name: string): Node | null {
    if (!levelRoot?.isValid) return null;
    const direct = levelRoot.getChildByName(name);
    if (direct) return direct;
    return levelRoot.getChildByName('Tiles')?.getChildByName(name) ?? null;
}

function resolveLevelConfig(): LevelConfig | undefined {
    return getLevelRuntimeContext()?.getCurLevel()
        ?? GameManager.instance?.getCurLevel?.()
        ?? undefined;
}

function collectTileEntries(levelRoot: Node, tilesRoot: Node, seen: Set<Node>): DrawEntry[] {
    const config = resolveLevelConfig();
    const entries: DrawEntry[] = [];
    const pushTile = (node: Node, parsed: { layer: 'ground' | 'border'; x: number; y: number }) => {
        if (seen.has(node)) return;
        seen.add(node);
        const kind = classifyTileKind(parsed, config);
        entries.push({
            node,
            x: parsed.x,
            y: parsed.y,
            rank: tileRank(kind, parsed.layer),
            kind,
        });
    };

    forEachTileChild(levelRoot, pushTile);
    for (const ch of tilesRoot.children) {
        if (!ch?.isValid || seen.has(ch)) continue;
        const parsed = parseTileNodeName(ch.name);
        if (parsed) pushTile(ch, parsed);
    }
    return entries;
}

function collectEntityEntries(levelRoot: Node, tilesRoot: Node, seen: Set<Node>): DrawEntry[] {
    const entries: DrawEntry[] = [];
    const pushEntity = (node: Node | null | undefined) => {
        if (!node?.isValid || seen.has(node) || !isEntityDrawNode(node)) return;
        seen.add(node);
        const cell = getEntityLogicCell(node);
        entries.push({
            node,
            x: cell.x,
            y: cell.y,
            rank: entityRank(node),
            kind: classifyEntityKind(node),
        });
    };
    for (const ch of levelRoot.children) pushEntity(ch);
    for (const ch of tilesRoot.children) pushEntity(ch);
    return entries;
}

function applyDrawOrder(tilesRoot: Node, entries: DrawEntry[]) {
    const sorted = entries.slice().sort(compareDrawEntries);
    const inSort = new Set<Node>();

    for (const { node } of sorted) {
        if (!node.isValid) continue;
        inSort.add(node);
        if (isEntityDrawNode(node)) ensureEntityUILayer(node);
        if (node.parent !== tilesRoot) {
            const lp = node.position.clone();
            node.parent = tilesRoot;
            node.setPosition(lp);
        }
    }

    for (let i = 0; i < sorted.length; i++) {
        const node = sorted[i].node;
        if (!node.isValid) continue;
        if (node.getSiblingIndex() !== i) node.setSiblingIndex(i);
    }

    let tail = sorted.length;
    for (const ch of [...tilesRoot.children]) {
        if (!ch.isValid || inSort.has(ch)) continue;
        if (ch.getSiblingIndex() !== tail) ch.setSiblingIndex(tail);
        tail++;
    }
}

function finalizeLevelRootOrder(levelRoot: Node, tilesRoot: Node) {
    for (const name of ['Ground', 'Border'] as const) {
        const layer = levelRoot.getChildByName(name);
        if (layer && layer.children.length === 0) {
            layer.setSiblingIndex(0);
        }
    }
    tilesRoot.setSiblingIndex(levelRoot.children.length - 1);
}

/**
 * 关卡加载 / 重置时按格子坐标排序一次；移动过程中不再改 sibling。
 */
export function sortIsoTiles(levelRoot: Node) {
    if (!levelRoot?.isValid) return;
    const tilesRoot = ensureTilesRoot(levelRoot);
    const seen = new Set<Node>();
    const tileEntries = collectTileEntries(levelRoot, tilesRoot, seen);
    const entityEntries = collectEntityEntries(levelRoot, tilesRoot, seen);
    applyDrawOrder(tilesRoot, [...tileEntries, ...entityEntries]);
    finalizeLevelRootOrder(levelRoot, tilesRoot);
}

/** @deprecated 与 sortIsoTiles 相同 */
export function refreshIsoEntityDrawOrder(levelRoot: Node) {
    sortIsoTiles(levelRoot);
}

/** @deprecated 与 sortIsoTiles 相同 */
export function refreshIsoDrawOrder(levelRoot: Node) {
    sortIsoTiles(levelRoot);
}

/** @deprecated 与 sortIsoTiles 相同 */
export function refreshIsoDrawOrderImmediate(levelRoot: Node) {
    sortIsoTiles(levelRoot);
}

export function bringEntityNodesToFront(levelRoot: Node, _opts?: EntityDrawOrderOptions) {
    sortIsoTiles(levelRoot);
}

export function ensureEntityUILayer(node: Node) {
    const walk = (n: Node) => {
        n.layer = UI_LAYER;
        for (const ch of n.children) walk(ch);
    };
    walk(node);
}

function ensureTilesRoot(levelRoot: Node): Node {
    let tilesRoot = levelRoot.getChildByName('Tiles');
    if (!tilesRoot) {
        tilesRoot = new Node('Tiles');
        tilesRoot.layer = UI_LAYER;
        tilesRoot.parent = levelRoot;
        setupLayerContainer(tilesRoot);
    }
    tilesRoot.setSiblingIndex(levelRoot.children.length - 1);
    return tilesRoot;
}

const TILE_LAYER_NAMES = ['Ground', 'Border', 'Tiles'] as const;

function forEachTileChild(levelRoot: Node, fn: (node: Node, parsed: { layer: 'ground' | 'border'; x: number; y: number }) => void) {
    for (const layerName of TILE_LAYER_NAMES) {
        const layer = levelRoot.getChildByName(layerName);
        if (!layer) continue;
        for (const ch of layer.children) {
            if (!ch?.isValid || !ch.active) continue;
            const parsed = parseTileNodeName(ch.name);
            if (!parsed) continue;
            fn(ch, parsed);
        }
    }
}

export function alignTileNode(node: Node, cellX: number, cellY: number, tileName: string, theme?: string) {
    const w = cellToWorldCenter(new Vec3(cellX, cellY, 0));
    let ui = node.getComponent(UITransform);
    if (!ui) ui = node.addComponent(UITransform);
    const spr = node.getComponent(Sprite);
    const source = resolveTilePixelSize(tileName, spr?.spriteFrame ?? null, theme);
    const draw = getTileDrawSize(tileName, source.width, source.height, theme);
    const pivot = getTilePivot(tileName, theme);
    ui.setContentSize(draw.width, draw.height);
    ui.setAnchorPoint(pivot.x, pivot.y);
    node.setPosition(w.x, w.y, 0);
    node.setScale(1, 1, 1);
}

export function setupLayerContainer(layer: Node) {
    let ui = layer.getComponent(UITransform);
    if (!ui) ui = layer.addComponent(UITransform);
    ui.setAnchorPoint(0, 0);
    ui.setContentSize(1, 1);
    layer.setPosition(0, 0, 0);
}

function tileNameFromConfig(
    layer: 'ground' | 'border',
    key: string,
    config: LevelConfig,
): string {
    if (layer === 'ground') {
        return config.ground?.[key] ?? CommonDefine.BlockBase;
    }
    let v = config.border?.[key];
    if (v === true || v === undefined) return 'WallBlock';
    if (typeof v === 'string') return v;
    return 'WallBlock';
}

export function layoutLevelTiles(levelRoot: Node, config: LevelConfig) {
    if (!levelRoot?.isValid) return;
    const theme = config.theme;
    const ground = levelRoot.getChildByName('Ground');
    const border = levelRoot.getChildByName('Border');
    if (ground) {
        setupLayerContainer(ground);
        for (const ch of ground.children) {
            const parsed = parseTileNodeName(ch.name);
            if (!parsed || parsed.layer !== 'ground') continue;
            const key = `${parsed.x},${parsed.y}`;
            alignTileNode(ch, parsed.x, parsed.y, tileNameFromConfig('ground', key, config), theme);
        }
    }
    if (border) {
        setupLayerContainer(border);
        for (const ch of border.children) {
            const parsed = parseTileNodeName(ch.name);
            if (!parsed || parsed.layer !== 'border') continue;
            const key = `${parsed.x},${parsed.y}`;
            alignTileNode(ch, parsed.x, parsed.y, tileNameFromConfig('border', key, config), theme);
        }
    }
    const tilesRoot = levelRoot.getChildByName('Tiles');
    if (tilesRoot) {
        setupLayerContainer(tilesRoot);
        for (const ch of tilesRoot.children) {
            const parsed = parseTileNodeName(ch.name);
            if (!parsed) continue;
            const key = `${parsed.x},${parsed.y}`;
            const layer = parsed.layer;
            alignTileNode(ch, parsed.x, parsed.y, tileNameFromConfig(layer, key, config), theme);
        }
    }
    sortIsoTiles(levelRoot);
}
