from ttfrog.db.schema import prototypes
from ttfrog.db.schema.constants import DamageType, Defenses
from ttfrog.db.schema.modifiers import Modifier
from ttfrog.db.schema.prototypes import Weapon


def test_weapons(db):
    with db.transaction():
        longbow = Weapon(
            name="longbow",
            damage_die="1d6",
            damage_type=DamageType.piercing,
            martial=True,
            two_handed=True,
            ammunition=True,
            targets=1,
            attack_range=150,
            attack_range_long=600,
        )

        dagger = Weapon(
            name="Dagger",
            damage_die="1d4",
            damage_type=DamageType.slashing,
            melee=True,
            finesse=True,
            light=True,
            thrown=True,
            attack_range=20,
            attack_range_long=60,
        )
        db.add_or_update([longbow, dagger])

        assert longbow.martial
        assert longbow.ranged
        assert not longbow.melee

        assert not dagger.martial
        assert dagger.ranged
        assert dagger.melee


def test_charges(db, carl):
    with db.transaction():
        for_the_lulz = prototypes.ItemProperty(
            name="For the Lulz",
            description="""
            On a hit against a creature with a mouth, spend one charge to force the target to roll a DC 13 Wisdom
            saving throw. On a failure, the target is forced to grin for one minute. While grinning, the target
            cannot speak. The target can repeat the saving throw at the start of their turn."
            """,
            charge_cost=2,
        )

        dagger_of_lulz = prototypes.Weapon(
            name="Dagger of Lulz",
            description="This magical dagger has 6 charges. It regains 1d6 charges after a short rest.",
            damage_die="1d4",
            damage_type=DamageType.slashing,
            melee=True,
            finesse=True,
            light=True,
            thrown=True,
            attack_range=20,
            attack_range_long=60,
            magical=True,
            charges=6,
            recharge_time=prototypes.RechargeTime.SHORT_REST,
            recharge_amount="1d6",
            rarity=prototypes.Rarity["Very Rare"],
            requires_attunement=True,
            properties=[for_the_lulz],
        )
        db.add_or_update([carl, dagger_of_lulz])

        assert for_the_lulz in dagger_of_lulz.properties

        assert carl.equipment.add(dagger_of_lulz)
        db.add_or_update(carl)

        carls_dagger = carl.equipment.get(dagger_of_lulz)
        assert carls_dagger.equip()
        assert carls_dagger.attune()

        assert len(carls_dagger.charges) == dagger_of_lulz.charges == 6
        assert len(carls_dagger.charges_available) == dagger_of_lulz.charges == 6
        assert carls_dagger.use(for_the_lulz)
        assert len(carls_dagger.charges_available) == 4

        # use the remaining charges
        assert carls_dagger.use(for_the_lulz)
        assert carls_dagger.use(for_the_lulz)

        # all out of charges
        assert len(carls_dagger.charges_available) == 0
        assert not carls_dagger.use(for_the_lulz)


def test_nocharges(db, carl):
    smiles = prototypes.ItemProperty(name="Smile!", description="The target grins for one minute.", charge_cost=None)
    wand_of_unlimited_smiles = prototypes.BaseItem(
        name="Wand of Unlimited Smiles", description="description", properties=[smiles]
    )
    db.add_or_update(wand_of_unlimited_smiles)

    carl.equipment.add(wand_of_unlimited_smiles)
    db.add_or_update(carl)

    # no charges means you can use it at will
    assert carl.equipment.get(wand_of_unlimited_smiles).use(smiles)
    assert carl.equipment.get(wand_of_unlimited_smiles).use(smiles)
    assert carl.equipment.get(wand_of_unlimited_smiles).use(smiles)


def test_attunement(db, carl):
    with db.transaction():
        helm = prototypes.Armor(
            name="Iron Helm",
            rarity=prototypes.Rarity.Common,
        )
        helm.add_modifier(Modifier("+1 AC (helmet)", target="armor_class", relative_value=1, stacks=True))

        shield = prototypes.Shield(
            name="Shield of Missile Attraction",
            description="""
            While holding this shield, you have resistance to damage from ranged weapon attacks.

            **Curse.** This shield is cursed. Attuning to it curses you until you are targeted by the remove curse spell
            or similar magic. Removing the shield fails to end the curse on you. Whenever a ranged weapon attack is made
            against a target within 10 feet of you, the curse causes you to become the target instead.
            """,
            rarity=prototypes.Rarity.Rare,
            requires_attunement=True,
        )

        plus_two_ac = Modifier("+2 AC (Shield", target="armor_class", relative_value=2, stacks=True)
        ranged_resistance = Modifier(
            "Resistance to Damage From Ranged Weapon Attacks (Shield of Missle Attraction)",
            target=DamageType.ranged_weapon_attacks,
            new_value=Defenses.resistant,
            requires_attunement=True,
        )
        shield.add_modifier(plus_two_ac)
        shield.add_modifier(ranged_resistance)

        db.add_or_update([carl, helm, shield])

        assert carl.equipment.add(shield)
        assert carl.equipment.add(helm)
        db.add_or_update(carl)

        carls_shield = carl.equipment.get(shield)

        assert carl.armor_class == 10
        assert len(carl.attuned_items) == 0

        carls_shield.equip()

        assert plus_two_ac in carl.modifiers["armor_class"]
        assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks]
        assert carl.armor_class == 12
        assert carls_shield not in carl.attuned_items

        assert carls_shield.attune()
        assert not carls_shield.attune()
        assert carl.armor_class == 12
        assert plus_two_ac in carl.modifiers["armor_class"]
        assert ranged_resistance in carl.modifiers[DamageType.ranged_weapon_attacks]
        assert carls_shield in carl.attuned_items

        assert carl.equipment.get(helm).equip()
        assert carl.armor_class == 13

        assert carls_shield.unattune()
        assert not carls_shield.unattune()
        assert carl.armor_class == 13
        assert ranged_resistance not in carl.modifiers[DamageType.ranged_weapon_attacks]
        assert carls_shield.unequip()
        assert carl.armor_class == 11

        # can only attune 3 items
        assert carl.equipment.add(shield)
        assert carl.equipment.add(shield)
        assert carl.equipment.add(shield)
        db.add_or_update(carl)

        assert carl.equipment.get_all(shield)[0].attune()
        assert carl.equipment.get_all(shield)[1].attune()
        assert carl.equipment.get_all(shield)[2].attune()
        assert len(carl.attuned_items) == 3
        assert not carl.equipment.get_all(shield)[3].attune()
