import {
    Container,
    Sprite,
    StateMachine,
    Texture,
    moveTowardsW,
    worldDist,
    worldToScreen,
    type EventBus,
    type IsoDepthLayer,
    type TileMapData,
    type Vec2
} from '@rpg/engine';

/**
 * Пейзажная фауна (витрина StateMachine вне боя): безгласный олень.
 * Стоит, медленно бродит по соседним тайлам; на громкий звон рядом
 * подходит к источнику и стоит у колокольчиков. Не боевой.
 */

/** Радиус слуха на звон (мировые юниты). */
const HEAR_RADIUS = 7;
/** Скорость брожения / подхода (юниты/сек). */
const WANDER_SPEED = 0.35;
const APPROACH_SPEED = 0.7;

interface FaunaEntity {
    /** Позиция (ноги) в мировых юнитах. */
    pos: Vec2;
    machine: StateMachine;
    /** Куда идём (если идём). */
    dest: Vec2 | null;
    view: Container;
    sprite: Sprite;
    frames: [Texture, Texture];
    animT: number;
    animI: number;
    /** Пауза перед следующим шагом (idle). */
    pause: number;
}

export class FaunaSystem {
    private entities: FaunaEntity[] = [];
    private rng: () => number;
    private off: () => void;

    constructor(
        private actors: IsoDepthLayer,
        events: EventBus,
        private data: TileMapData,
        spawns: Vec2[],
        seed = 20260908
    ) {
        // Детерминированный ГПСЧ — брожение одинаково между запусками.
        let s = seed;
        this.rng = () => {
            s = (s + 0x6d2b79f5) | 0;
            let t = Math.imul(s ^ (s >>> 15), 1 | s);
            t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
            return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
        };
        for (const p of spawns) this.spawn(p);
        // Громкий звон рядом — олень подходит к источнику и стоит у колокольчиков.
        this.off = events.on<{ origin: Vec2 }>('combat:attack', ({ origin }) => {
            for (const e of this.entities) {
                if (worldDist(e.pos, origin) <= HEAR_RADIUS) {
                    e.dest = { ...origin };
                    e.machine.change('approach');
                }
            }
        });
    }

    /** Привязать кадры (вызывается сценой после конструктора). */
    setFrames(frames: [Texture, Texture]): void {
        for (const e of this.entities) {
            e.frames = frames;
            e.sprite.texture = frames[0];
        }
    }

    /** Заспавнить оленя в точке (юниты). */
    private spawn(pos: Vec2): void {
        const view = new Container();
        const sprite = new Sprite();
        sprite.anchor.set(0.5, 1);
        view.addChild(sprite);
        const sp = worldToScreen(pos.x, pos.y);
        view.position.set(sp.x, sp.y);
        this.actors.add(view, Math.floor(pos.x), Math.floor(pos.y));

        const e: FaunaEntity = {
            pos: { ...pos },
            machine: new StateMachine(),
            dest: null,
            view,
            sprite,
            frames: [sprite.texture, sprite.texture],
            animT: 0,
            animI: 0,
            pause: 0
        };

        // Стоит: через паузу делает шаг к соседнему тайлу.
        e.machine.add('idle', {
            update: () => {
                if (e.machine.time >= e.pause) this.startWander(e);
            }
        });
        // Бродит: медленно к случайной соседней точке.
        e.machine.add('wander', {
            update: (dt) => this.stepTo(e, dt, WANDER_SPEED, 'idle')
        });
        // Подходит на звон: быстрее, к источнику звука.
        e.machine.add('approach', {
            update: (dt) => this.stepTo(e, dt, APPROACH_SPEED, 'idle')
        });
        e.machine.change('idle');
        e.pause = 1.5 + this.rng() * 3;
        this.entities.push(e);
    }

    /** Начать шаг к случайной соседней проходимой точке. */
    private startWander(e: FaunaEntity): void {
        const dirs = [
            [1, 0],
            [-1, 0],
            [0, 1],
            [0, -1]
        ];
        const options = dirs
            .map(([dx, dy]) => ({ x: e.pos.x + dx, y: e.pos.y + dy }))
            .filter((p) => this.walkable(p));
        if (options.length === 0) {
            e.pause = 2;
            return;
        }
        e.dest = options[Math.floor(this.rng() * options.length)]!;
        e.pause = 1.5 + this.rng() * 3;
        e.machine.change('wander');
    }

    /** Движение к dest; по приходе — переход в idle. */
    private stepTo(e: FaunaEntity, dt: number, speed: number, back: string): void {
        if (!e.dest) {
            e.machine.change(back);
            return;
        }
        e.pos = moveTowardsW(e.pos, e.dest, speed * dt);
        if (e.pos.x === e.dest.x && e.pos.y === e.dest.y) {
            e.dest = null;
            e.machine.change(back);
        }
    }

    private walkable(p: Vec2): boolean {
        const tx = Math.floor(p.x);
        const ty = Math.floor(p.y);
        if (tx < 0 || ty < 0 || tx >= this.data.width || ty >= this.data.height) return false;
        return !this.blocked.includes(this.data.tiles[ty * this.data.width + tx]!);
    }

    private blocked: number[] = [];

    /** Задать непроходимые id тайлов (сцена синхронизирует со своей картой). */
    setBlocked(ids: number[]): void {
        this.blocked = ids;
    }

    update(dt: number): void {
        for (const e of this.entities) {
            e.machine.update(dt);
            // Покадровая анимация: 2 кадра, 3 Гц, только на ходу.
            if (e.dest) {
                e.animT += dt;
                if (e.animT >= 0.33) {
                    e.animT -= 0.33;
                    e.animI = 1 - e.animI;
                    e.sprite.texture = e.frames[e.animI]!;
                }
            } else {
                e.animI = 0;
                e.sprite.texture = e.frames[0]!;
            }
            const sp = worldToScreen(e.pos.x, e.pos.y);
            e.view.position.set(sp.x, sp.y);
            this.actors.setDepth(e.view, Math.floor(e.pos.x), Math.floor(e.pos.y));
        }
    }

    exit(): void {
        this.off?.();
        for (const e of this.entities) {
            this.actors.removeChild(e.view);
            e.view.destroy({ children: true });
        }
        this.entities = [];
    }
}