import {
    Engine,
    SaveManager,
    AssetLoader,
    AudioManager,
    GameState,
    Settings,
    Inventory,
    parseMap,
    type Renderer,
    type SceneManager,
    type TileMapData
} from '@rpg/engine';
import { AUDIO_KEYS } from './data/audio';
// Шрифт идёт с движком (OFL): Vite кладёт файл в сборку и даёт готовый URL.
import fontUrl from '@rpg/engine/assets/fonts/VT323-Regular.ttf?url';

/**
 * Игровой контекст: движок, сохранения, загрузчик ассетов, состояние, настройки.
 * Движок ничего не знает об этом классе — он только потребляет его API.
 */
export class Game {
    /** Виртуальное разрешение (пиксель-арт). */
    static readonly VIRTUAL_W = 480;
    static readonly VIRTUAL_H = 270;

    readonly saves: SaveManager;
    readonly assets: AssetLoader;
    /** Звуки и музыка (WAV из apps/game/tools/audio/gen.mjs). */
    readonly audio = new AudioManager((key) => `${import.meta.env.BASE_URL}audio/${key}.wav`);
    /** Флаги и переменные прохождения. */
    readonly state = new GameState();
    /** Сумка героя (предметы квестов и торговли). */
    readonly inventory = new Inventory();
    /** Настройки игрока (громкости и т.п.) — вне сейвов. */
    readonly settings = new Settings(window.localStorage);

    /** Текстуры тайлов и одиночные спрайты NPC. */
    static readonly ASSET_KEYS = [
        'tiles/grass',
        'tiles/grass_2',
        'tiles/grass_3',
        'tiles/path',
        'tiles/path_2',
        'tiles/water_1',
        'tiles/water_2',
        'tiles/ash',
        'tiles/ash_2',
        'tiles/decor_tuft',
        'tiles/decor_stone',
        'tiles/decor_bald',
        'tiles/bellflower',
        'tiles/tree',
        'tiles/house',
        'tiles/tower',
        'tiles/floor',
        'tiles/wall',
        'tiles/well'
    ];

    /** Ключи аудио (WAV): sfx + амбиент-лупы областей (реестр в data/audio.ts). */
    static readonly AUDIO_KEYS = AUDIO_KEYS;

    constructor(readonly engine: Engine) {
        this.saves = new SaveManager(window.localStorage);
        // Ключи без расширения -> .png; ключи .json (атласы) резолвятся как есть.
        this.assets = new AssetLoader((key) =>
            key.endsWith('.json') ? `${import.meta.env.BASE_URL}${key}` : `${import.meta.env.BASE_URL}${key}.png`
        );
        this.settings.load();
        // Настройки громкости -> шины аудио (до unlock шин нет — применится после).
        this.settings.onChange((s) => {
            this.audio.setBusVolume('master', s.master);
            this.audio.setBusVolume('ambience', s.ambience);
            this.audio.setBusVolume('sfx', s.sfx);
        });
        // Применить текущие значения сразу.
        this.audio.setBusVolume('master', this.settings.data.master);
        this.audio.setBusVolume('ambience', this.settings.data.ambience);
        this.audio.setBusVolume('sfx', this.settings.data.sfx);
        // Хук для смоук-тестов (apps/game/tools/smoke*.mjs): доступ к контексту из страницы.
        if (import.meta.env.DEV) {
            (window as unknown as { __game?: Game }).__game = this;
            // Шпион аудио: последние 24 запуска (sfx + лупы) — для агентных
            // проверок звука (на слух в headless не проверить, tools/checks/audio.mjs).
            const audioLog: { key: string; t: number; volume: number; pan: number }[] = [];
            this.audio.onPlayed = (key, opts) => {
                audioLog.push({ key, t: Math.round(performance.now()), volume: opts.volume ?? 1, pan: opts.pan ?? 0 });
                if (audioLog.length > 24) audioLog.shift();
            };
            (window as unknown as { __gameAudioLog?: typeof audioLog }).__gameAudioLog = audioLog;
            // Агентный мост: window.__agent (см. docs/engine/agent.md, apps/game/tools/agent.mjs).
            import('./agent/GameAgent').then(({ registerAgent }) => registerAgent(this));
        }
    }

    /** URL пиксельного шрифта (VT323 идёт вместе с движком, OFL). */
    static readonly FONT_URL: string = fontUrl;

    /** Карты локаций, распарсенные из assets/maps/<id>.map (формат rpg-map). */
    readonly mapFiles = new Map<string, TileMapData>();

    /** Загрузить и распарсить карту локации (вызывается в BootScene). */
    async loadMap(id: string): Promise<TileMapData> {
        const res = await fetch(`${import.meta.env.BASE_URL}maps/${id}.map`);
        if (!res.ok) throw new Error(`Карта ${id}: HTTP ${res.status}`);
        const data = parseMap(await res.json());
        this.mapFiles.set(id, data);
        return data;
    }

    get scenes(): SceneManager {
        return this.engine.scenes;
    }

    get renderer(): Renderer {
        return this.engine.renderer;
    }

    get camera() {
        return this.engine.camera;
    }

    get worldRoot() {
        return this.engine.renderer.worldRoot;
    }

    /** Амбиент области с кроссфейдом; повторный вызов с тем же ключом не перезапускает его. */
    playAmbience(key: string, volume = 0.7): void {
        if (this.ambienceKey === key) return;
        this.ambienceKey = key;
        if (key === '') {
            this.audio.stopAmbience(1.5);
            return;
        }
        void this.audio.playAmbience(key, { fade: 1.5, volume });
    }

    /** Текущий амбиент (защита от перезапуска того же ключа; '' — тишина). */
    private ambienceKey: string | null = null;
}