"""Loot-Router: Items regelbasiert zwischen Backpacks verschieben.

Regeln (ng_lootRules):
- {'itemId': 3031, 'to': 'Gold Backpack'} — "alles Gold in ein Backpack"
- {'itemId': 3577, 'drop': True} — Muell (Fleisch, Wuermer) wegwerfen
- ng_lootDropUnwanted (Schalter): alles ERKANNTE, was nicht konfiguriert
  ist, wird weggeworfen. Niemals weggeworfen werden Regel-Items,
  Traenke, Coins und Magic-Shield-Potions; was der Bot nicht erkennt,
  bleibt aus Sicherheitsgruenden liegen.

Sicherheit vor Verwechslungen: Wegwerfen und Verschieben brauchen
Score >= 0.85 UND einen Vorsprung >= 0.08 gegenueber dem
zweitbesten Icon — aehnliche Items (Edelsteine, Stoffe, Traenke) sind
damit ambiguitaetssicher; im Zweifel bleibt das Item liegen.

Alle 15s werden die offenen Container gescannt. Erst wird Muell auf
den eigenen SQM geworfen (macht Cap frei), dann werden Wertitems ins
Ziel-Backpack gezogen. Die Ziel-Backpacks kommen aus der
Auto-Erkennung (detectOpenBackpacks) und sind in der Inventar-Seite
direkt auswaehlbar; "wegwerfen" ist dort als Pseudo-Ziel waehlbar.

ponytail: Verschiebt/wirft den ersten gefundenen Stapel pro Runde und
Regel (schont die Loop; der Rest folgt in den naechsten Runden).
"""
from time import monotonic

from src.repositories.actionBar.slotIcons import (
    HEALTH_POTION_IDS, MANA_POTION_IDS, SLOT_ITEMS, getItemIcon,
    matchScore,
)
from src.repositories.gameWindow.slot import getSlotPosition
from src.repositories.inventory.core import detectOpenBackpacks
from src.utils.core import locate
from src.utils.mouse import drag

CHECK_INTERVAL_SECONDS = 15
_STRICT_THRESHOLD = 0.85
_STRICT_MARGIN = 0.08


def _regionOf(screenshot, bar):
    x0, y0 = bar[0], bar[1] + 18
    return x0, y0, screenshot[y0:, x0:x0 + 176]


def _findInContainer(screenshot, bar, icon, others=(), threshold=_STRICT_THRESHOLD):
    """Icon-Position im Container oder None.

    Strenger Match: `icon` muss mit >= threshold gefunden werden UND
    mindestens _STRICT_MARGIN besser sein als jedes Icon aus `others`
    (Schutz gegen Verwechslung aehnlicher Items).
    """
    x0, y0, region = _regionOf(screenshot, bar)
    position = locate(region, icon, confidence=threshold)
    if position is None:
        return None
    if others:
        ownScore = matchScore(region, icon)
        for other in others:
            if ownScore - matchScore(region, other) < _STRICT_MARGIN:
                return None
    return (x0 + position[0] + 16, y0 + position[1] + 16)


def _keepItemIds(rules):
    """Niemals wegwerfen: Regel-Items, Traenke, Coins, Ausruestung."""
    keep = {rule.get('itemId') for rule in rules}
    keep.update(HEALTH_POTION_IDS)
    keep.update(MANA_POTION_IDS)
    keep.update((3031, 3035, 3360, 35563))
    return keep


def _dropUnwanted(context, screenshot, containers, rules):
    """Wirft erkannte, nicht konfigurierte Items aus Nicht-Ziel-Containern.

    Erkannt = Icon liegt vor (Item-DB oder per Import geladen). Was der
    Bot nicht erkennt oder nicht eindeutig zuordnet, bleibt aus
    Sicherheitsgruenden liegen — ebenso alles in Regel-Zielen.
    """
    from src.repositories.actionBar.slotIcons import _itemsPath
    keep = _keepItemIds(rules)
    targetNames = {rule.get('to') for rule in rules if rule.get('to')}
    scanBars = [
        bar for name, bar in containers.items() if name not in targetNames
    ]
    candidates = {}
    for path in sorted(_itemsPath.glob('*.png')):
        try:
            item_id = int(path.stem)
        except ValueError:
            continue
        if item_id in keep:
            continue
        icon = getItemIcon(item_id)
        if icon is not None:
            candidates[item_id] = icon
    for sourceBar in scanBars:
        _x0, _y0, region = _regionOf(screenshot, sourceBar)
        scored = sorted(
            ((matchScore(region, icon), item_id)
             for item_id, icon in candidates.items()),
            reverse=True,
        )
        if not scored or scored[0][0] < _STRICT_THRESHOLD:
            continue
        if len(scored) > 1 and scored[0][0] - scored[1][0] < _STRICT_MARGIN:
            # uneindeutig: zwei Icons matchen aehnlich gut — liegen lassen
            continue
        position = _findInContainer(
            screenshot, sourceBar, candidates[scored[0][1]],
            others=[icon for item_id, icon in candidates.items()
                    if item_id != scored[0][1]])
        if position is not None:
            drag(position, getSlotPosition(
                (7, 5), context['gameWindow']['coordinate']))


def routeLoot(context):
    """Loop-Hook: verschiebt Items gemaess ng_lootRules in Ziel-Backpacks."""
    rules = context.get('ng_lootRules', [])
    if not rules and not context.get('ng_lootDropUnwanted'):
        return context
    state = context.setdefault('ng_lootRouter', {})
    now = monotonic()
    if now - state.get('lastCheckAt', 0) < CHECK_INTERVAL_SECONDS:
        return context
    state['lastCheckAt'] = now
    screenshot = context['ng_screenshot']
    if screenshot is None:
        return context
    containers = detectOpenBackpacks(screenshot)
    allIcons = [icon for icon in (
        getItemIcon(item_id) for item_id in SLOT_ITEMS) if icon is not None]
    # Erst Muell wegwerfen (macht Cap frei), dann Wertitems umsortieren
    dropRules = [rule for rule in rules if rule.get('drop')]
    moveRules = [rule for rule in rules if not rule.get('drop')]
    for rule in dropRules:
        icon = getItemIcon(rule.get('itemId'))
        if icon is None:
            continue
        others = [other for other in allIcons if other is not icon]
        for sourceBar in containers.values():
            position = _findInContainer(
                screenshot, sourceBar, icon, others=others)
            if position is None:
                continue
            drag(position, getSlotPosition(
                (7, 5), context['gameWindow']['coordinate']))
            break
    if context.get('ng_lootDropUnwanted'):
        _dropUnwanted(context, screenshot, containers, rules)
    for rule in moveRules:
        targetName = rule.get('to')
        targetBar = containers.get(targetName)
        if targetBar is None:
            continue
        icon = getItemIcon(rule.get('itemId'))
        if icon is None:
            continue
        others = [other for other in allIcons if other is not icon]
        for sourceName, sourceBar in containers.items():
            if sourceName == targetName:
                continue
            position = _findInContainer(
                screenshot, sourceBar, icon, others=others)
            if position is None:
                continue
            drag(position, (targetBar[0] + 16, targetBar[1] + 34))
            break
    return context
