from src.utils.humanize import pause as sleep
from typing import Union
from src.shared.typings import BBox, GrayImage
from src.utils.core import cacheObjectPosition, locate, getScreenshot
from src.utils.image import crop
from src.utils.keyboard import hotkey, press, write
from src.utils.mouse import leftClick, moveTo
from .config import images, npcTradeBarImage, npcTradeOkImage


# TODO: add unit tests
# TODO: add perf
@cacheObjectPosition
def getTradeTopPosition(screenshot: GrayImage) -> Union[BBox, None]:
    return locate(screenshot, npcTradeBarImage)


# TODO: add unit tests
# TODO: add perf
@cacheObjectPosition
def getTradeBottomPos(screenshot: GrayImage) -> Union[BBox, None]:
    tradeTopPos = getTradeTopPosition(screenshot)
    if tradeTopPos is None:
        return None
    (x, y, _, _) = tradeTopPos
    croppedImage = crop(
        screenshot, x, y, 174, len(screenshot) - y)
    bottom = locate(croppedImage, npcTradeOkImage)
    if bottom is None:
        return None
    (_, botY, _, _) = bottom
    return x, y + botY + 26, 174, 2


# TODO: add unit tests
# TODO: add perf
def findItem(screenshot: GrayImage, itemName: str):
    bottom = getTradeBottomPos(screenshot)
    if bottom is None:
        return False
    (bx, by, _, _) = bottom
    leftClick((bx + 160, by - 75))
    sleep(0.2)
    leftClick((bx + 16, by - 75))
    sleep(0.2)
    write(itemName)
    sleep(2)
    screenshotAfterFind = getScreenshot()
    itemImg = images[itemName]
    itemPos = locate(screenshotAfterFind, itemImg)
    if itemPos is None:
        return False
    # TODO: improve it, click should be done in a handle coordinate inside the box
    x = itemPos[0] + 10
    y = itemPos[1] + 10
    leftClick((x, y))
    return True


# TODO: add unit tests
# TODO: add perf
def setAmount(screenshot: GrayImage, amount: int):
    bottom = getTradeBottomPos(screenshot)
    if bottom is None:
        return False
    (bx, by, _, _) = bottom
    leftClick((bx + 115, by - 42))
    sleep(0.2)
    hotkey('ctrl', 'a')
    sleep(0.2)
    press('backspace')
    write(str(amount))
    return True


# TODO: add unit tests
# TODO: add perf
def confirmBuyItem(screenshot: GrayImage):
    bottom = getTradeBottomPos(screenshot)
    if bottom is None:
        return False
    (bx, by, _, _) = bottom
    leftClick((bx + 150, by - 18))
    return True


# TODO: add unit tests
# TODO: add perf
def clearSearchBox(screenshot: GrayImage):
    bottom = getTradeBottomPos(screenshot)
    if bottom is None:
        return False
    (bx, by, _, _) = bottom
    x = bx + 115 + 45
    y = by - 42 - 35
    moveTo((x, y))
    leftClick((x, y))
    moveTo((x, y + 20))
    return True


# TODO: add unit tests
# TODO: add perf
def buyItem(screenshot: GrayImage, itemName: str, itemQuantity: int):
    if not findItem(screenshot, itemName):
        return False
    sleep(1)
    if not setAmount(screenshot, itemQuantity):
        return False
    sleep(1)
    if not confirmBuyItem(screenshot):
        return False
    sleep(1)
    if not clearSearchBox(screenshot):
        return False


def sellItem(screenshot: GrayImage, itemName: str):
    """Verkauft ein Item im NPC-Trade (Sell-Tab, Suche, OK).

    ponytail: Die Sell-Tab-Position relativ zur Trade-Titelleiste
    (x+44, y+34) ist nicht live vermessen. Schutz: Es wird nur
    bestaetigt, wenn das Item in der Liste gefunden wird — sonst
    Abbruch ohne Aktion (Upgrade-Trigger: Trade-Fenster live
    vermessen und Offset bestaetigen).
    """
    top = getTradeTopPosition(screenshot)
    if top is None:
        return False
    leftClick((top[0] + 44, top[1] + 34))
    sleep(1)
    if not findItem(screenshot, itemName):
        return False
    sleep(1)
    return confirmBuyItem(screenshot)
    sleep(1)
    return True
