import {
    findPath,
    tileToWorld,
    worldToScreen,
    worldToTile,
    type Container,
    type CutsceneStep,
    type IsometricTileMap,
    type Vec2
} from '@rpg/engine';

/**
 * Актёры кат-сцен: ходок по A*-пути для вьюхи NPC и фабрики шагов.
 * Мир на время сцены на паузе — ходок без боя/расталкивания, тикается
 * сценой независимо от раннера (исполнитель шага until).
 */

/** Скорость ходьбы актёра по умолчанию (юниты/сек) — чуть медленнее героя. */
const WALKER_SPEED = 1.4;

/** Таймаут ожидания ходьбы по умолчанию (сек) — страховка скипа/зависания. */
const WALK_TIMEOUT = 30;

/**
 * Ходок: ведёт вьюху по пути A* в мировых юнитах, синхронизирует вью
 * (округление до px — единственная точка мир→экран, как у героя).
 */
export class ActorWalker {
    private pos: Vec2;
    private waypoints: Vec2[] = [];

    constructor(
        private map: IsometricTileMap,
        private view: Container,
        startTile: { x: number; y: number },
        /** Смена тайла (для depth-слоя сцены). */
        private onTile?: (tile: Vec2) => void,
        private speed = WALKER_SPEED
    ) {
        this.pos = tileToWorld(startTile.x, startTile.y);
    }

    /** Идёт ли сейчас (шаг until должен ждать). */
    get done(): boolean {
        return this.waypoints.length === 0;
    }

    get position(): Vec2 {
        return { x: this.pos.x, y: this.pos.y };
    }

    /** Построить путь к тайлу и пойти. Непроходимый тайл — остаётся на месте. */
    walkTo(tile: { x: number; y: number }): void {
        const from = worldToTileClamped(this.pos, this.map);
        const path = findPath(this.map, from, tile, false);
        this.waypoints = path ? path.map((t) => tileToWorld(t.x, t.y)) : [];
    }

    update(dt: number): void {
        if (this.waypoints.length === 0) return;
        const target = this.waypoints[0]!;
        const dx = target.x - this.pos.x;
        const dy = target.y - this.pos.y;
        const dist = Math.hypot(dx, dy);
        const step = this.speed * dt;
        let crossedTile = false;
        if (dist <= step) {
            this.pos = { x: target.x, y: target.y };
            this.waypoints.shift();
            crossedTile = true;
        } else {
            this.pos = { x: this.pos.x + (dx / dist) * step, y: this.pos.y + (dy / dist) * step };
        }
        this.syncView();
        if (crossedTile) this.onTile?.(worldToTileClamped(this.pos, this.map));
    }

    /** Вью-позиция: округление до px (вьюха лежит в worldRoot — мировые px). */
    private syncView(): void {
        const s = worldToScreen(this.pos.x, this.pos.y);
        this.view.position.set(Math.round(s.x), Math.round(s.y));
    }
}

/** Тайл под позицией (вне карты — ближайший краевой, как у героя). */
function worldToTileClamped(pos: Vec2, map: IsometricTileMap): Vec2 {
    const w = map.data.width;
    const h = map.data.height;
    const t = worldToTile(pos.x, pos.y, w, h);
    return (
        t ?? {
            x: Math.min(w - 1, Math.max(0, Math.floor(pos.x))),
            y: Math.min(h - 1, Math.max(0, Math.floor(pos.y)))
        }
    );
}

/** Шаги «актёр идёт к тайлу»: запуск + ожидание прихода (со страховкой). */
export function npcWalkSteps(walker: ActorWalker, tile: Vec2, timeout = WALK_TIMEOUT): CutsceneStep[] {
    return [
        { kind: 'call', fn: () => walker.walkTo(tile) },
        { kind: 'until', predicate: () => walker.done, label: `актёр к ${tile.x},${tile.y}`, timeout }
    ];
}

/** Шаг «вьюха плавно уходит в мировую точку» (глайд без пути — по прямой). */
export function npcMoveView(view: Container, toWorld: Vec2, seconds: number): CutsceneStep {
    const p = worldToScreen(toWorld.x, toWorld.y);
    return { kind: 'moveView', view, x: p.x, y: p.y, seconds };
}