import { Graphics } from 'pixi.js';
import { FadeState } from '../core/fade';

/** Линейная интерполяция числа (для фейда оверлея). */
const lerpNum = (a: number, b: number, k: number): number => a + (b - a) * k;

/**
 * Сцена — экран игры (меню, локация, бой). Менеджер держит стек,
 * но в 2D-RPG чаще всего достаточно push/pop/replace.
 */
export interface Scene {
    /** Вызывается один раз при входе. */
    enter(): void | Promise<void>;
    exit(): void | Promise<void>;
    /** dt в секундах, фиксированный шаг. */
    update(dt: number): void;
    render(): void;
}

/** Затемнение экрана при смене сцены. */
export interface SceneTransition {
    /** Общая длительность (затухание + проявление), секунды. По умолчанию 0.5. */
    duration?: number;
    /** Цвет затемнения. По умолчанию чёрный. */
    color?: number;
}

type TransitionKind = 'push' | 'pop' | 'replace';

interface PendingTransition {
    kind: TransitionKind;
    scene?: Scene;
    duration: number;
    color: number;
    phase: 'out' | 'in';
    /** Прозрачность оверлея 0..1 (FadeState вместо ручного t/duration). */
    alpha: FadeState<number>;
}

export class SceneManager {
    private stack: Scene[] = [];
    private overlay: Graphics | null = null;
    private overlayWidth = 0;
    private overlayHeight = 0;
    private pending: PendingTransition | null = null;

    /** Движок подключает оверлей затемнения (верхний слой uiRoot). */
    setOverlay(g: Graphics, width: number, height: number): void {
        this.overlay = g;
        this.overlayWidth = width;
        this.overlayHeight = height;
    }

    async push(scene: Scene, transition?: SceneTransition): Promise<void> {
        if (transition) {
            this.begin('push', scene, transition);
            return;
        }
        this.stack.push(scene);
        await scene.enter();
    }

    async pop(transition?: SceneTransition): Promise<void> {
        if (transition) {
            this.begin('pop', undefined, transition);
            return;
        }
        const scene = this.stack.pop();
        if (scene) {
            await scene.exit();
        }
    }

    /** Заменить верхнюю сцену (например меню -> локация). */
    async replace(scene: Scene, transition?: SceneTransition): Promise<void> {
        if (transition) {
            this.begin('replace', scene, transition);
            return;
        }
        await this.pop();
        await this.push(scene);
    }

    get current(): Scene | undefined {
        return this.stack[this.stack.length - 1];
    }

    /** Идёт ли сейчас переход (для блокировки ввода). */
    get transitioning(): boolean {
        return this.pending !== null;
    }

    update(dt: number): void {
        if (this.pending) {
            this.stepTransition(dt);
        }
        this.current?.update(dt);
    }

    render(): void {
        for (const scene of this.stack) {
            scene.render();
        }
    }

    private begin(kind: TransitionKind, scene: Scene | undefined, tr: SceneTransition): void {
        if (this.pending) return; // параллельные переходы запрещены
        // Оверлей должен быть поверх всего UI: сцены добавляли свои view позже.
        if (this.overlay?.parent) {
            this.overlay.parent.addChild(this.overlay);
        }
        const duration = tr.duration ?? 0.5;
        const alpha = new FadeState(lerpNum, 0);
        alpha.to(1, duration);
        this.pending = {
            kind,
            scene,
            duration,
            color: tr.color ?? 0x000000,
            phase: 'out',
            alpha
        };
    }

    private stepTransition(dt: number): void {
        const p = this.pending!;
        p.alpha.update(dt);
        this.drawOverlay(p.color, p.alpha.value);

        if (p.phase === 'out') {
            if (p.alpha.active) return;
            // экран скрыт — меняем сцену
            this.performSwap(p);
            p.phase = 'in';
            p.alpha.to(0, p.duration);
            return;
        }

        // фаза 'in': проявление
        if (!p.alpha.active) {
            this.pending = null;
            this.overlay?.clear();
        }
    }

    private performSwap(p: PendingTransition): void {
        switch (p.kind) {
            case 'push':
                if (p.scene) {
                    this.stack.push(p.scene);
                }
                break;
            case 'pop':
                this.stack.pop()?.exit();
                break;
            case 'replace':
                this.stack.pop()?.exit();
                if (p.scene) {
                    this.stack.push(p.scene);
                }
                break;
        }
        // enter может быть асинхронным — переход не ждёт его,
        // новая сцена проявляется параллельно со своей инициализацией.
        void this.stack[this.stack.length - 1]?.enter();
    }

    private drawOverlay(color: number, alpha: number): void {
        if (!this.overlay) return;
        const g = this.overlay;
        g.clear();
        if (alpha > 0) {
            g.rect(0, 0, this.overlayWidth, this.overlayHeight).fill({ color, alpha });
        }
    }
}