import { IStorySceneConfig, IStoryPageConfig } from '../data/Interfaces';
import { StorageMgr, globalStorageMgr } from '../common/StorageMgr';
import { STORAGE_KEY } from '../common/Constants';

/**
 * Story-intro cutscene controller (task 9.1, req 19.1 — 19.9).
 *
 * Responsibilities:
 *  1. Decide whether the intro must play (first-time gate, req 19.5).
 *  2. Drive the 3-page typewriter sequence (req 19.2-19.3).
 *  3. Honour taps (speed up printing) and "Skip" (immediate dismiss, req 19.4).
 *  4. Persist the "seen" flag so it plays only once (req 19.5).
 *  5. Provide a `reset()` API the Settings menu calls (req 19.6).
 *
 * The view layer binds `onTextChanged` / `onFinished` to render text and
 * trigger the next scene load.
 */

export type StoryPhase = 'idle' | 'typing' | 'waiting_next' | 'finished';

export interface IStorySceneCallbacks {
    onTextChanged?: (text: string, page: IStoryPageConfig) => void;
    onPageEntered?: (page: IStoryPageConfig) => void;
    onFinished?: (skipped: boolean) => void;
}

/**
 * How many characters per real-time second a page types out. Boosts to
 * `FAST_MULTIPLIER` while the user is tapping (req 19.3).
 */
export const BASE_TYPING_CPS = 30;
export const FAST_MULTIPLIER = 4;

export class StorySceneCtrl {
    private phase: StoryPhase = 'idle';
    private pageIndex = 0;
    private cursor = 0;
    private elapsedSecOnPage = 0;
    private typingFast = false;

    constructor(
        private readonly scene: IStorySceneConfig,
        private readonly storage: StorageMgr = globalStorageMgr,
        private readonly callbacks: IStorySceneCallbacks = {}
    ) {}

    /** Returns true if the user has already seen / skipped the intro. */
    public hasBeenSeen(): boolean {
        return this.storage.get<boolean>(STORAGE_KEY.StoryIntroSeen, false);
    }

    /**
     * Called by the boot flow. If the intro was already consumed, the view
     * should skip straight to the next scene; otherwise this begins playback.
     */
    public start(): 'playing' | 'already_seen' {
        if (this.hasBeenSeen()) {
            return 'already_seen';
        }
        this.phase = 'typing';
        this.pageIndex = 0;
        this.cursor = 0;
        this.elapsedSecOnPage = 0;
        this.typingFast = false;
        this.callbacks.onPageEntered?.(this.currentPage());
        this.emitText();
        return 'playing';
    }

    /** Call every frame with real-time delta. */
    public tick(dtSec: number): void {
        if (this.phase !== 'typing') return;
        this.elapsedSecOnPage += dtSec;
        const page = this.currentPage();
        const cps = BASE_TYPING_CPS * (this.typingFast ? FAST_MULTIPLIER : 1);
        const targetCursor = Math.floor(this.elapsedSecOnPage * cps);
        if (targetCursor !== this.cursor) {
            this.cursor = Math.min(targetCursor, page.text.length);
            this.emitText();
        }
        if (this.cursor >= page.text.length) {
            this.phase = 'waiting_next';
        }
    }

    /** Tap anywhere — speed up typewriter or advance to next page (req 19.3). */
    public onTap(): void {
        if (this.phase === 'typing') {
            // First tap: reveal full page immediately (req 19.3 "accelerate").
            const page = this.currentPage();
            this.cursor = page.text.length;
            this.emitText();
            this.phase = 'waiting_next';
            return;
        }
        if (this.phase === 'waiting_next') {
            this.advancePage();
        }
    }

    /** Skip button pressed — immediate dismissal (req 19.4). */
    public onSkip(): void {
        if (this.phase === 'finished') return;
        this.markSeen();
        this.phase = 'finished';
        this.callbacks.onFinished?.(true);
    }

    /** Called by the Settings screen to re-enable the intro (req 19.6). */
    public reset(): void {
        this.storage.remove(STORAGE_KEY.StoryIntroSeen);
    }

    /** Expose current page for HUD rendering. */
    public get currentPageNumber(): number {
        return this.pageIndex + 1;
    }

    /** Current visible text on the active page. */
    public get visibleText(): string {
        return this.currentPage().text.slice(0, this.cursor);
    }

    public get status(): StoryPhase {
        return this.phase;
    }

    public get totalPages(): number {
        return this.scene.pages.length;
    }

    // -----------------------------------------------------------------

    private currentPage(): IStoryPageConfig {
        return this.scene.pages[this.pageIndex];
    }

    private emitText(): void {
        this.callbacks.onTextChanged?.(this.visibleText, this.currentPage());
    }

    private advancePage(): void {
        if (this.pageIndex < this.scene.pages.length - 1) {
            this.pageIndex++;
            this.cursor = 0;
            this.elapsedSecOnPage = 0;
            this.phase = 'typing';
            this.typingFast = false;
            this.callbacks.onPageEntered?.(this.currentPage());
            this.emitText();
        } else {
            // Last page complete → finish naturally.
            this.markSeen();
            this.phase = 'finished';
            this.callbacks.onFinished?.(false);
        }
    }

    private markSeen(): void {
        this.storage.set(STORAGE_KEY.StoryIntroSeen, true);
    }
}
