import { Assets, Ticker } from 'pixi.js';
import HoldAndWinMachine from 'src/slotMachines/HoldAndWinMachine';
import { argTypes, getDefaultArgs } from 'src/stories/utils/argTypes';
import { createScreenInputMap } from 'src/stories/utils/createScreenInputMap';
import MachineContainer from 'src/stories/utils/MachineContainer';
import { centerView } from 'src/stories/utils/resize';
import { SlotMachineConfig } from 'src/types/SlotMachineConfig';
import { Meta, PixiStory, StoryFn } from '@pixi/storybook-renderer';
import IdleSystem from '../systems/IdleSystem';
import StickySymbolsSystem from 'src/systems/StickySymbolsSystem';
import { STATES } from 'src/states/BasicStateMachine';

const args = {
    accelerationTime: 1,
    backCoef: 1.1,
    maxSpeed: 15,
    reelStartDelay: 0.2,
    reelStopDelay: 0.5,
    slotStopDelay: 0,
    spinningDuration: 2,
    blur: false,
    bounce: true
};

/**
 * Creates argTypes with fractional range steps for finer Hold & Win tuning.
 */
const createHoldAndWinArgTypes = (initialArgs: typeof args): ReturnType<typeof argTypes> => {
    const baseArgTypes = argTypes(initialArgs);

    Object.keys(initialArgs).forEach((key) => {

        const descriptor = baseArgTypes[key];
        const control = descriptor?.control;

        if (!control || control.type !== 'range') return;

        baseArgTypes[key] = {
            ...descriptor,
            control: {
                ...control,
                step: control.step && control.step < 1 ? control.step : 0.1,
            },
        };
    });

    return baseArgTypes;
};

const meta: Meta<typeof IdleSystem> = {
    title: 'features/HoldAndWin', // Путь для отображения в Storybook
    tags: ['autodocs'], // Включает автоматическую генерацию документации
    parameters: {
        docs: {
            story: {
                height: 600
            },
            description: {
                component: 'Manages idle animations for random symbols in the slot machine. When the system is idle, it triggers an idle animation on a randomly chosen symbol.'
            },
        }
    },
    argTypes: createHoldAndWinArgTypes(args),
    args: getDefaultArgs(args),
};

export default meta;

export const SingleSlotReelsStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
    context,
    init: async (view) => {
        Assets.reset();
        await Assets.init({ manifest: 'hold-and-win/manifest.json' });
        await Assets.loadBundle('configs');
        await Assets.loadBundle('symbols');
        const config: SlotMachineConfig = Assets.get('slot_machine_config');
        const machine = new HoldAndWinMachine(config);
        const container = new MachineContainer(machine);
        container.scale.set(0.5);
        view.addChild(container);
        context.machine = machine;
        // Prepare input maps for screen and initialization
        machine.inputs.initMap = createScreenInputMap(config);
        machine.inputs.screenMap = createScreenInputMap(config);
        // Apply story args to spin system and trigger spin
        machine.systems.spin.setSettings(_params as any);
        machine.inputs.spin = true;
        centerView(view);
    },
    resize: centerView,
    update: () => {
        const dt = Ticker.shared.deltaMS;
        if (context.machine) context.machine.update(dt);
    }
});

export const StickSystemStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
    context,
    init: async (view) => {
        Assets.reset();
        await Assets.init({ manifest: 'hold-and-win/manifest.json' });
        await Assets.loadBundle('configs');
        await Assets.loadBundle('symbols');
        const config: SlotMachineConfig = Assets.get('slot_machine_config');
        const machine = new HoldAndWinMachine(config);
        const container = new MachineContainer(machine);
        container.scale.set(0.5);
        view.addChild(container);
        context.machine = machine;

        // Attach StickySymbolsSystem to HoldAndWinMachine
        machine.systems.stick = new StickySymbolsSystem(machine as any);
        // Ensure stick system runs during all states where needed
        machine.stateMachine.states[STATES.SPINNING].systems = ['spin', 'layering'];
        machine.stateMachine.states.forEach((state) => {
            if (!state.systems.includes('stick')) state.systems.push('stick');
        });

        // Prepare input maps for screen and initialization
        machine.inputs.initMap = createScreenInputMap(config);
        machine.inputs.screenMap = machine.inputs.initMap;

        // Mark which symbols should become sticky; example: stick wilds L/M/N
        // This ensures only those cells are hidden behind stickies; others remain visible during spin
        machine.maps.stickySymbols = machine.inputs.screenMap.map((s: any) => ['L', 'M', 'N'].includes(s.key));

        // Configure systems
        if ((machine.systems as any).stick?.setSettings) (machine.systems as any).stick.setSettings({ zIndex: 5 });
        machine.systems.spin.setSettings(_params as any);

        // Trigger spin
        machine.inputs.spin = true;
        centerView(view);
    },
    resize: centerView,
    update: () => {
        const dt = Ticker.shared.deltaMS;
        if (context.machine) context.machine.update(dt);
    }
});

export const SingleSlotReelsPreventSpinStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
    context,
    init: async (view) => {
        Assets.reset();
        await Assets.init({ manifest: 'hold-and-win/manifest.json' });
        await Assets.loadBundle('configs');
        await Assets.loadBundle('symbols');
        const config: SlotMachineConfig = Assets.get('slot_machine_config');
        const machine = new HoldAndWinMachine(config);
        const container = new MachineContainer(machine);
        container.scale.set(0.5);
        view.addChild(container);
        context.machine = machine;
        // Prepare input maps for screen and initialization
        machine.inputs.initMap = createScreenInputMap(config);
        machine.maps.preventReelsSpin = machine.inputs.initMap.map((s: any) => s?.key === 'K');
        machine.inputs.screenMap = machine.inputs.initMap
        // Apply story args to spin system and trigger spin
        machine.systems.spin.setSettings(_params as any);
        machine.inputs.spin = true;
        centerView(view);
    },
    resize: centerView,
    update: () => {
        const dt = Ticker.shared.deltaMS;
        if (context.machine) context.machine.update(dt);
    }
});

export const DoubleHoldAndWinStory: StoryFn<typeof args> = (_params, context) => new PixiStory({
    context,
    init: async (view) => {
        Assets.reset();
        await Assets.init({ manifest: 'hold-and-win/manifest.json' });
        await Assets.loadBundle('configs');
        await Assets.loadBundle('symbols');
        const config: SlotMachineConfig = Assets.get('slot_machine_config');

        // Create two machines
        const machineTop = new HoldAndWinMachine(config);
        const containerTop = new MachineContainer(machineTop);
        const machineBottom = new HoldAndWinMachine(config);
        const containerBottom = new MachineContainer(machineBottom);

        // Scale both machines to half size
        containerTop.scale.set(0.5);
        containerBottom.scale.set(0.5);

        // Add to view
        view.addChild(containerTop);
        view.addChild(containerBottom);

        // Keep references for update loop
        (context as any).machines = [machineTop, machineBottom];

        // Prepare input maps (different for each machine)
        const initMapTop = createScreenInputMap(config);
        const initMapBottom = [...createScreenInputMap(config)].reverse(); // make it different

        machineTop.inputs.initMap = initMapTop;
        machineTop.inputs.screenMap = initMapTop;

        machineBottom.inputs.initMap = initMapBottom;
        machineBottom.inputs.screenMap = initMapBottom;

        // Apply story args to both spin systems and trigger spin
        machineTop.systems.spin.setSettings(_params as any);
        machineBottom.systems.spin.setSettings(_params as any);

        machineTop.inputs.spin = true;
        machineBottom.inputs.spin = true;

        // Layout: one above the other
        containerBottom.y = containerTop.height + 50; // spacing between machines

        centerView(view);
    },
    resize: centerView,
    update: () => {
        const dt = Ticker.shared.deltaMS;
        const machines = (context as any).machines as any[] | undefined;
        if (machines) machines.forEach((m: any) => m.update(dt));
    }
});
