const assert = require('assert');
const sinon = require('sinon');

const PhaseRule = require('../src/mode/basic/PhaseRule');
const Phase = require('../src/core/Player/Phase');

describe('Phase Rule', function () {
	const rule = new PhaseRule();

	it('checks driver and target', function () {
		rule.setDriver(null);
		assert.strictEqual(rule.isTriggerable(null), false);
		assert.strictEqual(rule.isTriggerable({}), false);

		rule.setDriver({});
		assert.strictEqual(rule.isTriggerable(null), false);
		assert.strictEqual(rule.isTriggerable({}), true);
	});

	it('draws 2 cards', async function () {
		const player = {};
		const driver = {
			trigger() {},
			drawCards: sinon.spy(),
		};
		rule.setDriver(driver);

		await rule.effect(player, {
			to: Phase.Draw,
		});

		sinon.assert.calledOnceWithExactly(driver.drawCards, player, 2);
	});

	it('activates a player', async function () {
		let count = 0;
		const player = {
			async play() {
				count++;
				return count < 3;
			},
		};

		await rule.effect(player, {
			to: Phase.Play,
		});

		assert.strictEqual(count, 3);
	});

	it('discards overflow hand cards', async function () {
		const selected = [1, 2, 3];
		const player = {
			handArea: {
				size: 10,
			},
			getHp() {
				return 3;
			},
			askForCards: sinon.stub().returns(selected),
		};
		const driver = {
			discardPile: {},
			trigger: sinon.fake(),
			moveCards: sinon.spy(),
		};
		rule.setDriver(driver);

		await rule.effect(player, {
			to: Phase.Discard,
		});

		sinon.assert.calledOnceWithExactly(driver.moveCards, selected, player.handArea, driver.discardPile, { open: true });
	});

	it('does nothing by default', async function () {
		await rule.effect({}, {});
	});
});

import PhaseRule from '../src/mode/basic/PhaseRule';
import { PlayerPhase as Phase } from '@karuta/sanguosha-core';

describe('Phase Rule', () => {
	const rule = new PhaseRule();

	it('checks driver and target', () => {
		rule.setDriver(null);
		expect(rule.isTriggerable(null)).toBe(false);
		expect(rule.isTriggerable({})).toBe(false);

		rule.setDriver({});
		expect(rule.isTriggerable(null)).toBe(false);
		expect(rule.isTriggerable({ player: 1 })).toBe(true);
	});

	it('draws 2 cards', async () => {
		const player = {};
		const driver = {
			trigger() {},
			drawCards: jest.fn(),
		};
		rule.setDriver(driver);

		await rule.effect({
			to: Phase.Draw,
			player,
		});

		expect(driver.drawCards).toBeCalledWith(player, 2);
	});

	it('activates a player', async () => {
		const play = jest.fn().mockResolvedValue(false);
		for (let i = 0; i < 2; i++) {
			play.mockResolvedValueOnce(true);
		}

		const handArea = {
			getCards() {
				return [];
			},
		};

		const player = {
			play,
			getHandArea() {
				return handArea;
			},
		};

		await rule.effect({
			to: Phase.Play,
			player,
		});

		expect(play).toBeCalledTimes(3);
	});

	it('discards overflow hand cards', async () => {
		const handArea = {
			size: 10,
		};
		const selected = [1, 2, 3];
		const player = {
			getHandArea: jest.fn().mockReturnValue(handArea),
			getHp() {
				return 3;
			},
			askForCards: jest.fn().mockResolvedValue(selected),
		};
		const discardPile = {};
		const driver = {
			getDiscardPile: () => discardPile,
			trigger: jest.fn(),
			moveCards: jest.fn(),
		};
		rule.setDriver(driver);

		await rule.effect({
			player,
			to: Phase.Discard,
		});

		expect(driver.moveCards).toBeCalledWith(selected, handArea, discardPile, { open: true });
	});

	it('does nothing by default', async () => {
		await rule.effect({}, {});
	});
});

Add tests of PhaseRule