import { CameraScroller, PARALLAX_LAYERS, PARALLAX_RATIOS, cameraFromLevel } from '@logic/CameraScroller';
import { LevelMgr } from '@logic/LevelMgr';
import { EnemyType, ILevelConfig } from '@data/Interfaces';

const HORIZONTAL_LEVEL: ILevelConfig = {
    id: '1-1',
    chapter: 1,
    displayName: '初始森林',
    sceneTheme: 'forest',
    scrollDirection: 'horizontal',
    timeLimitSec: 75,
    objective: { kind: 'kill_count', enemy: EnemyType.YaoFang, count: 3 },
    levelLengthPx: 3840,
    bgm: 'bgm_forest',
    enemySpawns: [],
};

describe('CameraScroller — horizontal (req 8.1)', () => {
    it('camera scrolls forward as player advances', () => {
        const cam = cameraFromLevel(HORIZONTAL_LEVEL, 960, 540);
        cam.followPlayer(480, 270);
        expect(cam.offsetX).toBe(0);
        cam.followPlayer(900, 270);
        expect(cam.offsetX).toBe(420);
    });

    it('camera never rewinds on horizontal scroll (req 8.1)', () => {
        const cam = cameraFromLevel(HORIZONTAL_LEVEL);
        cam.followPlayer(1500, 270);
        const forward = cam.offsetX;
        cam.followPlayer(100, 270);
        expect(cam.offsetX).toBe(forward); // did not rewind
    });

    it('camera stops at level end', () => {
        const cam = cameraFromLevel(HORIZONTAL_LEVEL);
        cam.followPlayer(10_000, 270);
        expect(cam.offsetX).toBe(HORIZONTAL_LEVEL.levelLengthPx - 960);
    });
});

describe('CameraScroller — parallax layers (req 8.8 — 1:2:4:4)', () => {
    it('exposes 4 layers with ratios 1,2,4,4', () => {
        expect(PARALLAX_LAYERS).toEqual(['far', 'mid', 'near', 'fx']);
        expect([...PARALLAX_RATIOS]).toEqual([1, 2, 4, 4]);
    });

    it('far/mid/near produce progressively smaller offsets', () => {
        const cam = cameraFromLevel(HORIZONTAL_LEVEL);
        cam.followPlayer(1200, 270);
        const far = cam.offsetForLayer('far');
        const mid = cam.offsetForLayer('mid');
        const near = cam.offsetForLayer('near');
        expect(far.x).toBeGreaterThan(mid.x);
        expect(mid.x).toBeGreaterThan(near.x);
    });
});

describe('CameraScroller — bi-directional + vertical', () => {
    it('horizontal_bi rewinds when the player walks backward', () => {
        const cam = new CameraScroller({
            direction: 'horizontal_bi',
            lengthX: 4800,
            viewportW: 960,
            viewportH: 540,
        });
        cam.followPlayer(2000, 0);
        cam.followPlayer(400, 0);
        expect(cam.offsetX).toBe(0);
    });

    it('vertical rising clamps at top', () => {
        const cam = new CameraScroller({
            direction: 'vertical',
            lengthX: 960,
            lengthY: 3240,
            viewportW: 960,
            viewportH: 540,
        });
        cam.followPlayer(480, 6000);
        expect(cam.offsetY).toBe(3240 - 540);
    });
});

describe('LevelMgr — objective / timer / result', () => {
    it('reports victory when kill objective is met', () => {
        const lv = new LevelMgr(HORIZONTAL_LEVEL);
        lv.onEnemyKilled(EnemyType.YaoFang);
        lv.onEnemyKilled(EnemyType.YaoFang);
        lv.onEnemyKilled(EnemyType.YaoFang);
        expect(lv.tick(0.016)).toBe('victory');
    });

    it('reports timeout when time-limit expires', () => {
        const lv = new LevelMgr(HORIZONTAL_LEVEL);
        expect(lv.tick(HORIZONTAL_LEVEL.timeLimitSec + 0.1)).toBe('timeout');
    });

    it('player_dead is terminal and outranks victory', () => {
        const lv = new LevelMgr(HORIZONTAL_LEVEL);
        lv.onPlayerDied();
        expect(lv.tick(0.1)).toBe('player_dead');
    });

    it('reach_top objective', () => {
        const cfg: ILevelConfig = { ...HORIZONTAL_LEVEL, objective: { kind: 'reach_top' } };
        const lv = new LevelMgr(cfg);
        lv.onReachedTop();
        expect(lv.tick(0.016)).toBe('victory');
    });

    it('defeat_boss objective', () => {
        const cfg: ILevelConfig = {
            ...HORIZONTAL_LEVEL,
            objective: { kind: 'defeat_boss', bossId: 'shuang_huan_fang' },
        };
        const lv = new LevelMgr(cfg);
        lv.onBossKilled();
        expect(lv.tick(0.016)).toBe('victory');
    });

    it('result() returns kills and remaining seconds', () => {
        const lv = new LevelMgr(HORIZONTAL_LEVEL);
        lv.onEnemyKilled(EnemyType.QingRen);
        lv.tick(10);
        const r = lv.result();
        expect(r.kills[EnemyType.QingRen]).toBe(1);
        expect(r.remainingSec).toBeCloseTo(65, 1);
    });
});
