from src.importers.bbot import convert_bbot, parse_bbot


def test_imports_bbot_profile_without_executing_macro_code():
    text = """BBOT
1399504867=@M:variables@
minhp=5
hpid=266
minstamina=900
@M:variables@
-1028332407=@M:macros@
0 {Logout} Self.StaminaMins()<=!minstamina! Self.Logout() Exit()
@M:macros@
20504739=@M:targets@
Troll:4:1:0::::Atk@
@M:targets@
1534554678=@M:waypoints@
Label (100 200 7:Hunt)
Point (101 200 7)
Fixed (102 200 7)
FullCheck (101 200 7:Full Refill Else Hunt Code !hpid!<!minhp!)
Macro (101 200 7:Logout)
ResetBackpacks (101 200 7)
@M:waypoints@
664726982=-1
-1257914102=35
-1169183731=1
1705233088=-1
"""
    config = {
        'ng_cave': {'enabled': False, 'runToCreatures': False, 'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {
                'enabled': False, 'hotkey': 'F1', 'slot': 1,
                'hpPercentageLessThanOrEqual': 0,
            }},
            'spells': {'criticalHealing': {}, 'lightHealing': {}},
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config, 'Carlin test.bbot', text)

    assert report['waypoints'] == 6
    assert imported['ng_cave']['waypoints']['items'][1]['options'] == {'exact': False}
    assert imported['ng_cave']['waypoints']['items'][2]['type'] == 'walk'
    assert imported['ng_cave']['waypoints']['items'][2]['options'] == {'exact': True}
    assert imported['ng_cave']['waypoints']['items'][3]['options']['checks'][0] == {
        'source': 'item', 'value': 266, 'operator': '<', 'target': 5,
    }
    assert imported['ng_cave']['waypoints']['items'][4]['options']['trueAction'] == 'logout'
    assert imported['ng_cave']['waypoints']['items'][5]['type'] == 'bbotResetBackpacks'
    assert imported['ng_targeting']['creatures']['Troll']['priority'] == 4
    assert imported['healing']['potions']['firstHealthPotion']['hpPercentageLessThanOrEqual'] == 35
    assert imported['ng_bbot']['itemSlots'] == {'266': 1}


def test_imports_absolute_health_potion_threshold():
    text = """BBOT
1534554678=@M:waypoints@
Point (100 200 7)
@M:waypoints@
664726982=-1
-1257914102=150
-1169183731=1
1705233088=0
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {'criticalHealing': {}, 'lightHealing': {}},
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config)

    potion = imported['healing']['potions']['firstHealthPotion']
    assert potion['enabled'] is True
    assert potion['hpLessThanOrEqual'] == 150
    assert potion['item'] == 266
    assert report['unsupported'] == []


def test_converts_floor_changes_to_directional_transitions():
    text = """BBOT
1534554678=@M:waypoints@
Point (32428 32313 7)
Point (32428 32314 8)
Point (32430 32303 8)
Point (32430 32302 9)
Point (32349 32231 8)
Point (32348 32230 7)
@M:waypoints@
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {'criticalHealing': {}, 'lightHealing': {}},
        },
    }

    imported, _ = convert_bbot(parse_bbot(text), config)

    waypoints = imported['ng_cave']['waypoints']['items']
    assert len(waypoints) == 10
    assert waypoints[0]['type'] == 'walk'
    assert waypoints[1]['type'] == 'moveDown'
    assert waypoints[1]['coordinate'] == [32428, 32313, 7]
    assert waypoints[1]['options'] == {'direction': 'south'}
    assert waypoints[4]['type'] == 'moveDown'
    assert waypoints[4]['coordinate'] == [32430, 32303, 8]
    assert waypoints[4]['options'] == {'direction': 'north'}
    assert waypoints[6]['type'] == 'moveUp'
    assert waypoints[6]['coordinate'] == [32430, 32302, 9]
    assert waypoints[6]['options'] == {'direction': 'west'}
    # Depot Thais: Aus den Koordinaten allein waere hier 'north'
    # herausgekommen (-1/-1 sieht diagonal aus, die Regel bevorzugt die
    # Senkrechte). Tatsaechlich geht es nach WESTEN hoch - nachgemessen im
    # Spiel: von (32349, 32231, 8) nach links landet man auf
    # (32348, 32230, 7). Die Karte weiss das, weil sie das Treppenfeld als
    # 'accessPoint' fuehrt, und wird darum bevorzugt.
    assert waypoints[8]['type'] == 'moveUp'
    assert waypoints[8]['coordinate'] == [32349, 32231, 8]
    assert waypoints[8]['options'] == {'direction': 'west'}


def test_imports_heal_spells_without_enable_flags():
    # BBot-Scripts (z. B. Wasps Thais) speichern Heilzauber mit Spell/HP/Mana,
    # aber ohne gesetzte Enable-Flags — sie müssen trotzdem übernommen werden.
    text = """BBOT
1534554678=@M:waypoints@
Point (100 200 7)
@M:waypoints@
-2143817111=0
-1399506541=Exura
-112139308=80
267068155=20
-1958017710=-1
1393655302=0
-2035450878=Exura Gran
-12875631=60
1485121280=70
-1426447618=-1
-1230307632=0
1362722921=Exura Vita
347220298=30
-1684520157=160
-1246590074=-1
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {
                'criticalHealing': {'enabled': False, 'hotkey': '5'},
                'lightHealing': {'enabled': False, 'hotkey': '7'},
                'mediumHealing': {'enabled': False, 'hotkey': '6'},
            },
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config)

    critical = imported['healing']['spells']['criticalHealing']
    light = imported['healing']['spells']['lightHealing']
    medium = imported['healing']['spells']['mediumHealing']
    assert critical['enabled'] is True
    assert critical['spell'] == 'exura vita'
    assert critical['hpPercentageLessThanOrEqual'] == 30
    assert light['enabled'] is True
    assert light['spell'] == 'exura gran'
    assert light['hpPercentageLessThanOrEqual'] == 60
    assert medium['enabled'] is True
    assert medium['spell'] == 'exura'
    assert medium['hpPercentageLessThanOrEqual'] == 80
    assert report['unsupported'] == []
    assert any('ohne Enable-Flag' in warning for warning in report['warnings'])


def test_absolute_potion_clears_percentage_threshold():
    text = """BBOT
1534554678=@M:waypoints@
Point (100 200 7)
@M:waypoints@
664726982=-1
-1257914102=150
-1169183731=1
1705233088=0
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {
                'enabled': False, 'slot': 1, 'hpPercentageLessThanOrEqual': 40,
            }},
            'spells': {'criticalHealing': {}, 'lightHealing': {}},
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config)

    potion = imported['healing']['potions']['firstHealthPotion']
    assert potion['hpLessThanOrEqual'] == 150
    assert potion['hpPercentageLessThanOrEqual'] is None
    assert any('absolute HP-Schwelle' in warning for warning in report['warnings'])


def test_parses_macro_steps():
    text = """BBOT
-1028332407=@M:123@
0 {crow} X:=Self.X() Y:=Self.Y() Z:=Self.Z() Map.UseOn(3304, 8435, !X!, !Y!, !Z!, 1)
1 {door} Map.Use(1676, 32801, 31220, 7, 1)
2 {talk} NPC.Say(hi) NPC.Say(mission)
3 {seq} Map.PickUpEx(8826, 1, 1, 32776, 31062, 7, 7) Macro.Wait(2000) Self.MoveE() Cavebot.Stop()
@M:123@
1534554678=@M:456@
Label (100 200 7:Start)
Macro (100 200 7:crow)
Macro (100 200 7:door)
Macro (100 200 7:talk)
Macro (100 200 7:seq)
Delay (100 200 7:5)
Sell (100 200 7:3357)
@M:456@
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {'criticalHealing': {}, 'lightHealing': {}},
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config)

    waypoints = imported['ng_cave']['waypoints']['items']
    crow = waypoints[1]['options']['steps']
    assert crow == [{
        'action': 'useItemOn', 'itemId': 3304, 'coordinate': None,
    }]
    door = waypoints[2]['options']['steps']
    assert door == [{'action': 'useMap', 'coordinate': (32801, 31220, 7)}]
    talk = waypoints[3]['options']['steps']
    assert talk == [
        {'action': 'say', 'text': 'hi'},
        {'action': 'say', 'text': 'mission'},
    ]
    seq = waypoints[4]['options']['steps']
    assert seq == [
        {'action': 'pickup', 'itemId': 8826, 'coordinate': (32776, 31062, 7)},
        {'action': 'wait', 'ms': 2000},
        {'action': 'move', 'direction': 'E'},
        {'action': 'pause'},
    ]
    assert waypoints[5]['type'] == 'bbotDelay'
    assert waypoints[5]['options']['seconds'] == 5
    assert waypoints[6]['type'] == 'bbotSell'
    assert waypoints[6]['options'] == {'id': 3357, 'item': 'Plate Armor'}
    assert report['unsupported'] == []


def test_parses_golabel_turn_exit_close():
    text = """BBOT
-1028332407=@M:123@
0 {Turn West} Self.TurnW()
0 {Turn East} Self.TurnE()
0 {Exit} Tibia.Close() Exit()
0 {out} Self.Logout()
@M:123@
1534554678=@M:456@
Label (100 200 7:Start)
Macro (100 200 7:Turn West)
Macro (100 200 7:Turn East)
Macro (100 200 7:Exit)
Macro (100 200 7:out)
GoLabel (100 200 7:Hunt)
@M:456@
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {'criticalHealing': {}, 'lightHealing': {}},
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config)

    waypoints = imported['ng_cave']['waypoints']['items']
    assert waypoints[1]['options']['steps'] == [{'action': 'turn', 'direction': 'W'}]
    assert waypoints[2]['options']['steps'] == [{'action': 'turn', 'direction': 'E'}]
    assert waypoints[3]['options']['steps'] == [{'action': 'closeClient'}]
    assert waypoints[4]['options']['steps'] == [{'action': 'logout'}]
    assert waypoints[5]['options'] == {'action': 'goLabel', 'label': 'Hunt'}
    assert report['unsupported'] == []


def test_imports_absolute_spell_threshold():
    text = """BBOT
1534554678=@M:456@
Point (100 200 7)
@M:456@
-2143817111=-1
-1399506541=Exura Vita
-112139308=300
267068155=160
-1958017710=0
"""
    config = {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {
                'criticalHealing': {'enabled': False, 'hotkey': '5'},
                'lightHealing': {'enabled': False, 'hotkey': '7'},
            },
        },
    }

    imported, report = convert_bbot(parse_bbot(text), config)

    light = imported['healing']['spells']['lightHealing']
    assert light['enabled'] is True
    assert light['spell'] == 'exura vita'
    assert light['hpLessThanOrEqual'] == 300
    assert light['hpPercentageLessThanOrEqual'] is None
    assert report['unsupported'] == []
    assert any('absolute HP-Schwelle' in warning for warning in report['warnings'])


def _bg_config():
    return {
        'ng_cave': {'waypoints': {'items': []}},
        'healing': {
            'potions': {'firstHealthPotion': {'enabled': False, 'slot': 1}},
            'spells': {
                'criticalHealing': {'enabled': False, 'hotkey': '5'},
                'lightHealing': {'enabled': False, 'hotkey': '7'},
                'utura': {'enabled': False, 'hotkey': '8'},
                'uturaGran': {'enabled': False, 'hotkey': '9'},
            },
        },
    }


def test_background_utura_enables_upkeep():
    text = """BBOT
-1028332407=@M:123@
3000 {Auto utura} Status.Buff<>1 Self.Health%<=75 Self.Mana>75 Self.Say(utura)
@M:123@
1534554678=@M:456@
Point (100 200 7)
@M:456@
"""
    imported, report = convert_bbot(parse_bbot(text), _bg_config())

    utura = imported['healing']['spells']['utura']
    assert utura['enabled'] is True
    assert utura['hpPercentageLessThanOrEqual'] == 75
    assert any('Utura-Upkeep' in warning for warning in report['warnings'])


def test_background_utura_absolute_hp():
    text = """BBOT
-1028332407=@M:123@
3000 {Utura} Status.Buff<>1 Self.Health<=1300 Self.Mana>75 Self.Say(utura)
@M:123@
1534554678=@M:456@
Point (100 200 7)
@M:456@
"""
    imported, _ = convert_bbot(parse_bbot(text), _bg_config())

    utura = imported['healing']['spells']['utura']
    assert utura['hpLessThanOrEqual'] == 1300


def test_background_ue_creates_combo():
    text = """BBOT
-1028332407=@M:123@
1000 {UE} COUNT:=4 mp:=700 Creatures.OnScreen()>=4 Creatures.PlayersOnScreen()==1 Self.Mana()>=700 Self.Say(exevo gran mas vis)
@M:123@
1534554678=@M:456@
Point (100 200 7)
@M:456@
"""
    imported, report = convert_bbot(parse_bbot(text), _bg_config())

    combos = imported['ng_comboSpells']
    assert combos['enabled'] is True
    assert len(combos['items']) == 1
    entry = combos['items'][0]
    assert entry['creatures'] == {'compare': 'greaterThanOrEqual', 'value': 4}
    assert entry['spells'] == [{'name': 'exevo gran mas vis', 'hotkey': None}]
    assert any('Kombo' in warning for warning in report['warnings'])


def test_mount_and_ring_report_warnings():
    text = """BBOT
-1028332407=@M:123@
3000 {Auto Mount} Status.InPZ()==0 Self.Mount()==0 Self.ToggleMount()
500 {Life Ring} Self.Equip.Ring(3052)
@M:123@
1534554678=@M:456@
Point (100 200 7)
@M:456@
"""
    _, report = convert_bbot(parse_bbot(text), _bg_config())

    assert any('Mount-Status' in warning for warning in report['warnings'])
    assert any('Equipment-Slot' in warning for warning in report['warnings'])


def test_macro_conditions_item_and_creatures():
    text = """BBOT
-1028332407=@M:123@
0 {check} Misc.ItemCountEx(3031)<100 Creatures.OnScreen()>=3 CaveBot.GoLabel(Deposit)
@M:123@
1534554678=@M:456@
Label (100 200 7:Start)
Macro (100 200 7:check)
@M:456@
"""
    imported, report = convert_bbot(parse_bbot(text), _bg_config())

    options = imported['ng_cave']['waypoints']['items'][1]['options']
    assert options['action'] == 'condition'
    assert options['checks'] == [
        {'source': 'item', 'value': 3031, 'operator': '<', 'target': 100},
        {'source': 'creatures', 'value': None, 'operator': '>=', 'target': 3},
    ]
    assert options['trueLabel'] == 'Deposit'
    assert report['unsupported'] == []


def test_self_say_becomes_say_step():
    text = """BBOT
-1028332407=@M:123@
0 {talk} Self.Say(exori)
@M:123@
1534554678=@M:456@
Label (100 200 7:Start)
Macro (100 200 7:talk)
@M:456@
"""
    imported, report = convert_bbot(parse_bbot(text), _bg_config())

    steps = imported['ng_cave']['waypoints']['items'][1]['options']['steps']
    assert steps == [{'action': 'say', 'text': 'exori'}]
    assert report['unsupported'] == []


def test_import_creates_loot_rules(mocker):
    from src.importers.bbot import _apply_loot_rules
    mocker.patch(
        'src.repositories.actionBar.itemIcons.ensureItemIcon',
        side_effect=lambda item_id: '/icons/x.png' if item_id != 10452 else None)
    config = {'ng_backpacks': {'main': 'Fur Backpack', 'loot': 'Demon Backpack'}}
    report = {'warnings': []}

    _apply_loot_rules(
        config,
        [{'id': 3031, 'name': 'gold coin'}, {'id': 9692, 'name': 'lump of dirt'}, {'id': 10452, 'name': 'terramite shell'}],
        report)

    assert config['ng_lootRules'] == [
        {'itemId': 3031, 'to': 'Demon Backpack'},
        {'itemId': 9692, 'to': 'Demon Backpack'},
    ]
    assert any('2 Loot-Regeln' in warning for warning in report['warnings'])
    assert any('terramite shell' in warning for warning in report['warnings'])

    # Dedup beim zweiten Lauf
    _apply_loot_rules(config, [{'id': 3031, 'name': 'gold coin'}], {'warnings': []})
    assert len(config['ng_lootRules']) == 2


def test_import_loot_rules_need_loot_backpack():
    from src.importers.bbot import _apply_loot_rules
    config = {'ng_backpacks': {'main': 'Fur Backpack', 'loot': None}}
    report = {'warnings': []}

    _apply_loot_rules(config, [{'id': 3031, 'name': 'gold coin'}], report)

    assert 'ng_lootRules' not in config
    assert any('kein Loot-Backpack' in warning for warning in report['warnings'])
