import { Vec3 } from 'cc';
import { CELL_PIXEL } from '../core/GridConstants';
import { CommonDefine, MoverRole } from '../core/Define';
import { cellToWorldCenter, worldCenterToCell } from '../core/GridCoords';
import {
    getThemePropPlacementOffsets,
    getThemeMoverEmptyCellYOffset,
    getThemeMoverJumpCellYOffset,
    getThemePlayerStandYOffset,
    DEFAULT_PROP_BLOCK_Y_OFFSET,
    DEFAULT_PROP_GROUND_Y_OFFSET,
} from '../theme/ThemeDatabase';
import { getTilePivot } from '../visual/TilePivots';
import { getTileDrawSize } from '../visual/TileSizes';
import { LevelConfig, SpawnConfig } from './LevelTypes';

/** 与 Unity Prop / nProp 一致：砖块上偏高，空地偏低 */
export type PropPlacement = 'block' | 'ground';

const HALF_H = CELL_PIXEL * 0.25;

/** Unity checkPoint 偏移（PPU≈100）：Prop ≈ +14px，nProp ≈ -11px */
export const PROP_BLOCK_Y_OFFSET = DEFAULT_PROP_BLOCK_Y_OFFSET;
export const PROP_GROUND_Y_OFFSET = DEFAULT_PROP_GROUND_Y_OFFSET;

export function cellHasTile(cellX: number, cellY: number, config: LevelConfig | undefined): boolean {
    if (!config) return false;
    const key = `${cellX},${cellY}`;
    const hasGround = !!config.ground?.[key];
    const hasBorder = config.border?.[key] !== undefined && config.border?.[key] !== false;
    return hasGround || hasBorder;
}

/** 砖块 pivot 对齐格心后，行走面相对格心的抬高量（与 Baseblock 差值用于 JumpBlock） */
function tileWalkSurfaceAboveCenter(tileName: string, theme?: string): number {
    const draw = getTileDrawSize(tileName, undefined, undefined, theme);
    const pivot = getTilePivot(tileName, theme);
    return draw.height * (1 - pivot.y);
}

/** JumpBlock 比 Baseblock 更高的行走面补偿（px） */
export function resolveMoverJumpCellYOffset(
    cellX: number,
    cellY: number,
    config: LevelConfig | undefined,
    theme?: string,
): number {
    const key = `${cellX},${cellY}`;
    const tile = config?.ground?.[key];
    if (tile !== CommonDefine.BlockJump) return 0;
    const auto = tileWalkSurfaceAboveCenter(CommonDefine.BlockJump, theme)
        - tileWalkSurfaceAboveCenter(CommonDefine.BlockBase, theme);
    return auto + getThemeMoverJumpCellYOffset(theme);
}

/** 角色/载具：JumpBlock 抬高；有普通砖为 0；空地/载具格补齐 Baseblock 高度 */
export function resolveMoverCellStandYOffset(
    cellX: number,
    cellY: number,
    config: LevelConfig | undefined,
    theme?: string,
): number {
    if (!cellHasTile(cellX, cellY, config)) {
        return getThemeMoverEmptyCellYOffset(theme);
    }
    return resolveMoverJumpCellYOffset(cellX, cellY, config, theme);
}

export function entityMoverWorldPosition(
    cell: Vec3,
    config: LevelConfig | undefined,
    theme?: string,
): Vec3 {
    const pos = cellToWorldCenter(cell);
    pos.y += resolveMoverCellStandYOffset(cell.x, cell.y, config, theme);
    return pos;
}

/**
 * 从带站立/空地 Y 微调的世界坐标反推逻辑格（与 entityMoverWorldPosition 互逆）。
 * 视觉 Y 会干扰等距 worldToCell，移动采样须先还原到格心平面。
 */
export function worldToMoverCell(
    world: Vec3,
    config: LevelConfig | undefined,
    theme?: string,
    forPlayer = false,
): Vec3 {
    let cell = worldCenterToCell(world);
    for (let i = 0; i < 4; i++) {
        const off = resolveMoverCellStandYOffset(cell.x, cell.y, config, theme);
        const stand = forPlayer ? getThemePlayerStandYOffset(theme) : 0;
        const adjustedY = world.y - stand - off;
        const next = worldCenterToCell(new Vec3(world.x, adjustedY, world.z));
        if (next.x === cell.x && next.y === cell.y) break;
        cell = next;
    }
    return cell;
}

/**
 * 移动/格子判定用的逻辑采样点（格心，不含主题站立 Y）。
 * @param levelLocal 关卡根节点本地坐标（与砖块 alignTileNode 同一空间）
 */
export function moverLogicalGridPosition(
    levelLocal: Vec3,
    config: LevelConfig | undefined,
    theme?: string,
    forPlayer = false,
): Vec3 {
    return cellToWorldCenter(worldToMoverCell(levelLocal, config, theme, forPlayer));
}

/** 角色生成/移动位置（含主题站立 Y 微调） */
export function entityPlayerWorldPosition(
    cell: Vec3,
    config: LevelConfig | undefined,
    theme?: string,
): Vec3 {
    const pos = entityMoverWorldPosition(cell, config, theme);
    pos.y += getThemePlayerStandYOffset(theme);
    return pos;
}

/** 角色/载具是否按玩家站立 Y 反推逻辑格（与 worldToMoverCell / entityPlayerWorldPosition 一致） */
export function roleUsesPlayerStandOffset(role: MoverRole): boolean {
    return role === 'player';
}

export function worldToMoverCellForRole(
    world: Vec3,
    config: LevelConfig | undefined,
    theme: string | undefined,
    role: MoverRole,
): Vec3 {
    return worldToMoverCell(world, config, theme, roleUsesPlayerStandOffset(role));
}

export function moverLogicalGridPositionForRole(
    levelLocal: Vec3,
    config: LevelConfig | undefined,
    theme: string | undefined,
    role: MoverRole,
): Vec3 {
    return moverLogicalGridPosition(
        levelLocal,
        config,
        theme,
        roleUsesPlayerStandOffset(role),
    );
}

/** 逻辑格 → 世界站立点（player 含主题站立 Y，vehicle 为 mover 甲板） */
export function entityWorldPositionForRole(
    cell: Vec3,
    config: LevelConfig | undefined,
    theme: string | undefined,
    role: MoverRole,
): Vec3 {
    return roleUsesPlayerStandOffset(role)
        ? entityPlayerWorldPosition(cell, config, theme)
        : entityMoverWorldPosition(cell, config, theme);
}

export function propWorldYOffset(placement: PropPlacement, theme?: string): number {
    const offsets = getThemePropPlacementOffsets(theme);
    return placement === 'ground' ? offsets.ground : offsets.block;
}

/** 无 ground/border 视为空地（nProp）；有砖块视为 Prop */
export function resolvePropPlacement(spawn: SpawnConfig, config: LevelConfig): PropPlacement {
    const key = `${spawn.x},${spawn.y}`;
    const hasGround = !!config.ground?.[key];
    const hasBorder = config.border?.[key] !== undefined && config.border?.[key] !== false;
    if (spawn.propPlacement === 'ground' || spawn.propPlacement === 'block') {
        if (!hasGround && !hasBorder) return 'ground';
        return spawn.propPlacement;
    }
    if (!hasGround && !hasBorder) return 'ground';
    return 'block';
}

export function isGroundPropSpawn(spawn: SpawnConfig, config: LevelConfig): boolean {
    return spawn.kind === 'prop' && resolvePropPlacement(spawn, config) === 'ground';
}
