import { PlayerColorState, PLAYER_IFRAME_SECONDS } from '../common/Constants';
import { AttackType } from '../data/Interfaces';

/**
 * Player color-state machine (req 5.1-5.6) + parry (req 3.7-3.8) + i-frames
 * (req 10.2-10.3).
 *
 * Transitions:
 *
 *   [Red] --crystal_jade--> [Green] --crystal_jade--> [Yellow]
 *      ^                      |                         |
 *      |---- shuriken|sword --|---- shuriken|sword -----|
 *      |                                                 |
 *   ---- fireball / smoke_bomb -----------------> ⚰️ dead
 *   ---- shuriken|sword while red ----------------> ⚰️ dead
 *
 * The machine is deliberately engine-agnostic so combat logic can be unit-
 * tested on either the web or inside a future dedicated simulator.
 */

export type DamageOutcome =
    | { kind: 'no_effect'; reason: 'iframe' | 'parried' }
    | { kind: 'downgraded'; from: PlayerColorState; to: PlayerColorState }
    | { kind: 'died'; cause: AttackType };

export interface IPlayerState {
    color: PlayerColorState;
    lives: number;
    /** Whether the ninja sword is currently in its parry-active frame window. */
    swordActive: boolean;
    /** Remaining i-frame time (seconds). */
    iframeSec: number;
    isDead: boolean;
}

export class PlayerStateMachine {
    private state: IPlayerState;

    constructor(initialLives = 3) {
        this.state = {
            color: PlayerColorState.Red,
            lives: initialLives,
            swordActive: false,
            iframeSec: 0,
            isDead: false,
        };
    }

    public get snapshot(): IPlayerState {
        return { ...this.state };
    }

    public get color(): PlayerColorState {
        return this.state.color;
    }

    public get lives(): number {
        return this.state.lives;
    }

    public get isDead(): boolean {
        return this.state.isDead;
    }

    // ---- external events --------------------------------------------------

    /** Player picked up a crystal jade (req 5.1-5.2). */
    public pickupCrystalJade(): PlayerColorState {
        if (this.state.color === PlayerColorState.Red) {
            this.state.color = PlayerColorState.Green;
        } else if (this.state.color === PlayerColorState.Green) {
            this.state.color = PlayerColorState.Yellow;
        }
        return this.state.color;
    }

    /** Player picked up an 增丸 — permanently +1 life (req 7.5). */
    public pickupZengWan(): number {
        this.state.lives += 1;
        return this.state.lives;
    }

    /**
     * Toggle sword active window. Called by `AttackController.tick()` for the
     * duration of a sword swing so the parry window (req 3.7-3.8) stays tight.
     */
    public setSwordActive(active: boolean): void {
        this.state.swordActive = active;
    }

    /** Advance i-frames on every physics tick. */
    public tick(dtSec: number): void {
        if (this.state.iframeSec > 0) {
            this.state.iframeSec = Math.max(0, this.state.iframeSec - dtSec);
        }
    }

    /**
     * Apply incoming damage of `attackType`. Returns the resulting outcome so
     * the caller can render the appropriate FX/HUD change.
     */
    public takeHit(attackType: AttackType): DamageOutcome {
        if (this.state.iframeSec > 0) {
            return { kind: 'no_effect', reason: 'iframe' };
        }
        // Sword-active parry applies to shuriken & sword only (req 3.7-3.8).
        if (this.state.swordActive && (attackType === 'shuriken' || attackType === 'sword')) {
            this.startIFrames();
            return { kind: 'no_effect', reason: 'parried' };
        }
        // Fireball / smoke bomb are always lethal (req 5.5, 10.4-10.5).
        if (attackType === 'fireball' || attackType === 'smoke_bomb') {
            return this.consumeLife(attackType);
        }
        // Ordinary shuriken / sword damage: downgrade by one tier or die.
        if (this.state.color === PlayerColorState.Yellow) {
            this.state.color = PlayerColorState.Red;
            this.startIFrames();
            return { kind: 'downgraded', from: PlayerColorState.Yellow, to: PlayerColorState.Red };
        }
        if (this.state.color === PlayerColorState.Green) {
            this.state.color = PlayerColorState.Red;
            this.startIFrames();
            return { kind: 'downgraded', from: PlayerColorState.Green, to: PlayerColorState.Red };
        }
        // Red → dead
        return this.consumeLife(attackType);
    }

    // ---- helpers ----------------------------------------------------------

    private consumeLife(cause: AttackType): DamageOutcome {
        this.state.lives = Math.max(0, this.state.lives - 1);
        this.state.color = PlayerColorState.Red;
        this.startIFrames();
        if (this.state.lives === 0) {
            this.state.isDead = true;
        }
        return { kind: 'died', cause };
    }

    private startIFrames(): void {
        this.state.iframeSec = PLAYER_IFRAME_SECONDS;
    }
}
