import random
import os.path
import math
from io import BytesIO

from PIL import Image, ImageDraw, ImageFont

from rollbot import as_command, Attachment
from rollbot.injection import Arg

# Extracted from https://labyrinthos.co/blogs/tarot-card-meanings-list
# Value and Suit are separated so in theory these could be easily rendered to an SVG at some point
DECK = {
    # Major
    ("0.", "The Fool"): (
        "innocence, new beginnings, free spirit",
        "recklessness, taken advantage of, inconsideration",
    ),
    ("I.", "The Magician"): (
        "willpower, desire, creation, manifestation",
        "trickery, illusions, out of touch",
    ),
    ("II.", "The High Priestess"): (
        "intuitive, unconscious, inner voice",
        "lack of center, lost inner voice, repressed feelings",
    ),
    ("III.", "The Empress"): (
        "motherhood, fertility, nature",
        "dependence, smothering, emptiness, nosiness",
    ),
    ("IV.", "The Emperor"): (
        "authority, structure, control, fatherhood",
        "tyranny, rigidity, coldness",
    ),
    ("V.", "The Hierophant"): (
        "tradition, conformity, morality, ethics",
        "rebellion, subversiveness, new approaches",
    ),
    ("VI.", "The Lovers"): (
        "partnerships, duality, union",
        "loss of balance, one-sidedness, disharmony",
    ),
    ("VII.", "The Chariot"): (
        "direction, control, willpower",
        "lack of control, lack of direction, aggression",
    ),
    ("VIII.", "Strength"): (
        "inner strength, bravery, compassion, focus",
        "self doubt, weakness, insecurity",
    ),
    ("IX.", "The Hermit"): (
        "contemplation, search for truth, inner guidance",
        "loneliness, isolation, lost your way",
    ),
    ("X.", "Wheel of Fortune"): (
        "change, cycles, inevitable fate",
        "no control, clinging to control, bad luck",
    ),
    ("XI.", "Justice"): (
        "cause and effect, clarity, truth",
        "dishonesty, unaccountability, unfairness",
    ),
    ("XII.", "The Hanged Man"): (
        "sacrifice, release, martyrdom",
        "stalling, needless sacrifice, fear of sacrifice",
    ),
    ("XIII.", "Death"): (
        "end of cycle, beginnings, change, metamorphosis",
        "fear of change, holding on, stagnation, decay",
    ),
    ("XIV.", "Temperance"): (
        "middle path, patience, finding meaning",
        "extremes, excess, lack of balance",
    ),
    ("XV.", "The Devil"): (
        "addiction, materialism, playfulness",
        "freedom, release, restoring control",
    ),
    ("XVI.", "The Tower"): (
        "sudden upheaval, broken pride, disaster",
        "disaster avoided, delayed disaster, fear of suffering",
    ),
    ("XVII.", "The Stars"): (
        "hope, faith, rejuvenation",
        "faithlessness, discouragement, insecurity",
    ),
    ("XVIII.", "The Moon"): (
        "unconscious, illusions, intuition",
        "confusion, fear, misinterpretation",
    ),
    ("XIX.", "The Sun"): (
        "joy, success, celebration, positivity",
        "negativity, depression, sadness",
    ),
    ("XX.", "Judgement"): (
        "reflection, reckoning, awakening",
        "lack of self awareness, doubt, self loathing",
    ),
    ("XXI.", "The World"): (
        "fulfillment, harmony, completion",
        "incompletion, no closure",
    ),
    # Wands
    ("King", "Wands"): (
        "big picture, leader, overcoming challenges",
        "impulsive, overbearing, unachievable expectations",
    ),
    ("Queen", "Wands"): (
        "courage, determination, joy",
        "selfishness, jealousy, insecurities",
    ),
    ("Knight", "Wands"): (
        "action, adventure, fearlessness",
        "anger, impulsiveness, recklessness",
    ),
    ("Page", "Wands"): (
        "exploration, excitement, freedom",
        "lack of direction, procrastination, creating conflict",
    ),
    ("Ten", "Wands"): (
        "accomplishment, responsibility, burden",
        "inability to delegate, overstressed, burnt out",
    ),
    ("Nine", "Wands"): (
        "resilience, grit, last stand",
        "exhaustion, fatigue, questioning motivations",
    ),
    ("Eight", "Wands"): (
        "rapid action, movement, quick decisions",
        "panic, waiting, slowdown",
    ),
    ("Seven", "Wands"): (
        "perseverance, defensive, maintaining control",
        "give up, destroyed confidence, overwhelmed",
    ),
    ("Six", "Wands"): (
        "victory, success, public reward",
        "excess pride, lack of recognition, punishment",
    ),
    ("Five", "Wands"): (
        "competition, rivalry, conflict",
        "avoiding conflict, respecting differences",
    ),
    ("Four", "Wands"): (
        "community, home, celebration",
        "lack of support, transience, home conflicts",
    ),
    ("Three", "Wands"): (
        "looking ahead, expansion, rapid growth",
        "obstacles, delays, frustration",
    ),
    ("Two", "Wands"): (
        "planning, making decisions, leaving home",
        "fear of change, playing safe, bad planning",
    ),
    ("Ace", "Wands"): (
        "creation, willpower, inspiration, desire",
        "lack of energy, lack of passion, boredom",
    ),
    # Cups
    ("King", "Cups"): (
        "compassion, control, balance",
        "coldness, moodiness, bad advice",
    ),
    ("Queen", "Cups"): (
        "compassion, calm, comfort",
        "martyrdom, insecurity, dependence",
    ),
    ("Knight", "Cups"): (
        "following the heart, idealist, romantic",
        "moodiness, disappointment",
    ),
    ("Page", "Cups"): (
        "happy surprise, dreamer, sensitivity",
        "emotional immaturity, insecurity, disappointment",
    ),
    ("Ten", "Cups"): (
        "inner happiness, fulfillment, dreams coming true",
        "shattered dreams, broken family, domestic disharmony",
    ),
    ("Nine", "Cups"): (
        "satisfaction, emotional stability, luxury",
        "lack of inner joy, smugness, dissatisfaction",
    ),
    ("Eight", "Cups"): (
        "walking away, disillusionment, leaving behind",
        "avoidance, fear of change, fear of loss",
    ),
    ("Seven", "Cups"): (
        "searching for purpose, choices, daydreaming",
        "lack of purpose, diversion, confusion",
    ),
    ("Six", "Cups"): (
        "familiarity, happy memories, healing",
        "moving forward, leaving home, independence",
    ),
    ("Five", "Cups"): (
        "loss, grief, self-pity",
        "acceptance, moving on, finding peace",
    ),
    ("Four", "Cups"): (
        "apathy, contemplation, disconnectedness",
        "sudden awareness, choosing happiness, acceptance",
    ),
    ("Three", "Cups"): (
        "friendship, community, happiness",
        "overindulgence, gossip, isolation",
    ),
    ("Two", "Cups"): (
        "unity, partnership, connection",
        "imbalance, broken communication, tension",
    ),
    ("Ace", "Cups"): (
        "new feelings, spirituality, intuition",
        "emotional loss, blocked creativity, emptiness",
    ),
    # Swords
    ("King", "Swords"): (
        "head over heart, discipline, truth",
        "manipulative, cruel, weakness",
    ),
    ("Knight", "Swords"): (
        "action, impulsiveness, defending beliefs",
        "no direction, disregard for consequences, unpredictability",
    ),
    ("Queen", "Swords"): (
        "complexity, perceptiveness, clear mindedness",
        "cold hearted, cruel, bitterness",
    ),
    ("Page", "Swords"): (
        "curiosity, restlessness, mental energy",
        "deception, manipulation, all talk",
    ),
    ("Ten", "Swords"): (
        "failure, collapse, defeat",
        "can't get worse, only upwards, inevitable end",
    ),
    ("Nine", "Swords"): (
        "anxiety, hopelessness, trauma",
        "hope, reaching out, despair",
    ),
    ("Eight", "Swords"): (
        "imprisonment, entrapment, self-victimization",
        "self acceptance, new perspective, freedom",
    ),
    ("Seven", "Swords"): (
        "deception, trickery, tactics and strategy",
        "coming clean, rethinking approach, deception",
    ),
    ("Six", "Swords"): (
        "transition, leaving behind, moving on",
        "emotional baggage, unresolved issues, resisting transition",
    ),
    ("Five", "Swords"): (
        "unbridled ambition, win at all costs, sneakiness",
        "lingering resentment, desire to reconcile, forgiveness",
    ),
    ("Three", "Swords"): (
        "heartbreak, suffering, grief",
        "recovery, forgiveness, moving on",
    ),
    ("Four", "Swords"): (
        "rest, restoration, contemplation",
        "restlessness, burnout, stress",
    ),
    ("Two", "Swords"): (
        "difficult choices, indecision, stalemate",
        "lesser of two evils, no right choice, confusion",
    ),
    ("Ace", "Swords"): (
        "breakthrough, clarity, sharp mind",
        "confusion, brutality, chaos",
    ),
    # Pentacles
    ("King", "Pentacles"): (
        "abundance, prosperity, security",
        "greed, indulgence, sensuality",
    ),
    ("Queen", "Pentacles"): (
        "practicality, creature comforts, financial security",
        "self-centeredness, jealousy, smothering",
    ),
    ("Knight", "Pentacles"): (
        "efficiency, hard work, responsibility",
        "laziness, obsessiveness, work without reward",
    ),
    ("Page", "Pentacles"): (
        "ambition, desire, diligence",
        "lack of commitment, greediness, laziness",
    ),
    ("Ten", "Pentacles"): (
        "legacy, culmination, inheritance",
        "fleeting success, lack of stability, lack of resources",
    ),
    ("Nine", "Pentacles"): (
        "fruits of labor, rewards, luxury",
        "reckless spending, living beyond means, false success",
    ),
    ("Eight", "Pentacles"): (
        "apprenticeship, passion, high standards",
        "lack of passion, uninspired, no motivation",
    ),
    ("Seven", "Pentacles"): (
        "hard work, perseverance, diligence",
        "work without results, distractions, lack of rewards",
    ),
    ("Six", "Pentacles"): (
        "charity, generosity, sharing",
        "strings attached, stinginess, power and domination",
    ),
    ("Five", "Pentacles"): (
        "need, poverty, insecurity",
        "recovery, charity, improvement",
    ),
    ("Four", "Pentacles"): (
        "conservation, frugality, security",
        "greediness, stinginess, possessiveness",
    ),
    ("Three", "Pentacles"): (
        "teamwork, collaboration, building",
        "lack of teamwork, disorganized, group conflict",
    ),
    ("Two", "Pentacles"): (
        "balancing decisions, priorities, adapting to change",
        "loss of balance, disorganized, overwhelmed",
    ),
    ("Ace", "Pentacles"): (
        "opportunity, prosperity, new venture",
        "lost opportunity, missed chance, bad investment",
    ),
}

HIGHLIGHT = (244, 170, 9, 255)
SUIT_COLOR = {
    "Pentacles": (0, 130, 3, 255),
    "Swords": (170, 5, 140, 255),
    "Cups": (23, 86, 247, 255),
    "Wands": (249, 2, 1, 255),
}

NUMBER_DISPLAY = {
    "Ace": "A",
    "Two": "2",
    "Three": "3",
    "Four": "4",
    "Five": "5",
    "Six": "6",
    "Seven": "7",
    "Eight": "8",
    "Nine": "9",
    "Ten": "10",
    "Page": "P",
    "Knight": "N",
    "Queen": "Q",
    "King": "K",
}

MAJOR_DISPLAY = {
    "0.": "\U0001f0e0",
    "I.": "\U0001f0e1",
    "II.": "\U0001f0e2",
    "III.": "\U0001f0e3",
    "IV.": "\U0001f0e4",
    "V.": "\U0001f0e5",
    "VI.": "\U0001f0e6",
    "VII.": "\U0001f0e7",
    "VIII.": "\U0001f0e8",
    "IX.": "\U0001f0e9",
    "X.": "\U0001f0ea",
    "XI.": "\U0001f0eb",
    "XII.": "\U0001f0ec",
    "XIII.": "\U0001f0ed",
    "XIV.": "\U0001f0ee",
    "XV.": "\U0001f0ef",
    "XVI.": "\U0001f0f0",
    "XVII.": "\U0001f0f1",
    "XVIII.": "\U0001f0f2",
    "XIX.": "\U0001f0f3",
    "XX.": "\U0001f0f4",
    "XXI.": "\U0001f0f5",
}

CARD_W = 800
# aspect ratio determined by rigorous testing
CARD_H = 31 * CARD_W // 25
# spacing of 1/8 the width
CARD_GAP = CARD_W // 8
CARD_WG = CARD_W + 2 * CARD_GAP
# vertical offset of center card
CARD_OFF = CARD_H // 10

CARD_TEXT_OFF = CARD_W // 10
CARD_RAD = CARD_W // 8
CARD_BORDER = CARD_W // 32

# https://fonts.google.com/specimen/Lumanosimo
FONT_SIZE = CARD_W // 4
TITLE_FONT = ImageFont.truetype(
    os.path.join(os.path.dirname(os.path.realpath(__file__)), "Lumanosimo.ttf"),
    FONT_SIZE,
)
# https://www.fontspace.com/symbola-font-f22021
SYMBOL_FONT = ImageFont.truetype(
    os.path.join(os.path.dirname(os.path.realpath(__file__)), "Symbola.ttf"),
    CARD_H - (CARD_BORDER * 2),
)

SUIT_LINE = {
    k: [(int(x * CARD_W / 10), int(y * CARD_H / 10)) for x, y in v]
    for k, v in {
        "Pentacles": [
            # https://mathworld.wolfram.com/RegularPentagon.html
            (6, 8),
            (6 + 3 * math.sin(4 * math.pi / 5), 6 - 2 * math.cos(math.pi / 5)),
            (6 - 3 * math.sin(2 * math.pi / 5), 6 + 2 * math.cos(2 * math.pi / 5)),
            (6 + 3 * math.sin(2 * math.pi / 5), 6 + 2 * math.cos(2 * math.pi / 5)),
            (6 - 3 * math.sin(4 * math.pi / 5), 6 - 2 * math.cos(math.pi / 5)),
            (6, 8),
        ],
        "Swords": [
            (6.5, 3),
            (7.5, 4),
            (7, 7),
            (8, 7),
            (8, 7.5),
            (7, 7.5),
            (7, 9),
            (6, 9),
            (6, 7.5),
            (5, 7.5),
            (5, 7),
            (6, 7),
            (5.5, 4),
            (6.5, 3),
        ],
        "Cups": [
            (3, 4),
            (9, 4),
            (6.5, 6),
            (6.5, 8),
            (8, 9),
            (4, 9),
            (5.5, 8),
            (5.5, 6),
            (3, 4),
        ],
        "Wands": [
            (5, 9),
            (8, 3),
            (7.5, 2.5),
            (4.5, 8.5),
            (5, 9),
        ],
    }.items()
}


@as_command
def tarot(
    hide: Arg(convert=lambda h: h.lower() == "hide", required=False, default=False),
    seven: Arg(
        convert=lambda h: h.lower() in ("7", "seven"), required=False, default=False
    ),
):
    """
    Get a tarot drawing. Include the argument 'hide' to hide explanations, or the argument 'seven' to get a 7 card draw.
    """
    drawn_cards = []
    message = ""
    for p1, p2 in random.sample(list(DECK.keys()), 7 if seven else 3):
        message += f"{p1} "
        if "." not in p1:  # minor arcana
            message += "of "
        message += p2
        face = random.choice((0, 0, 0, 1))
        if face == 1:
            message += ", Reversed"
        if not hide:
            message += f"\n    {DECK[(p1, p2)][face]}"
        message += "\n"
        drawn_cards.append((p1, p2, face))

    card_images = []
    for p1, p2, face in drawn_cards:
        with Image.new("RGBA", (CARD_W, CARD_H), color=(0, 0, 0, 0)) as image:
            draw = ImageDraw.Draw(image)
            draw.rounded_rectangle(
                xy=(0, 0, CARD_W - 1, CARD_H - 1),
                radius=CARD_RAD,
                outline=HIGHLIGHT,
                width=CARD_BORDER,
                fill=(71, 6, 47, 255),
            )
            if (xy := SUIT_LINE.get(p2)) is not None:
                draw.line(
                    xy=xy,
                    fill=SUIT_COLOR.get(p2, HIGHLIGHT),
                    width=CARD_BORDER,
                    joint="curve",
                )
                draw.text(
                    xy=(CARD_TEXT_OFF, CARD_TEXT_OFF),
                    text=NUMBER_DISPLAY.get(p1, p1),
                    fill=HIGHLIGHT,
                    font=TITLE_FONT,
                    stroke_width=FONT_SIZE // 6,
                )
                draw.text(
                    xy=(CARD_TEXT_OFF, CARD_TEXT_OFF),
                    text=NUMBER_DISPLAY.get(p1, p1),
                    fill=SUIT_COLOR.get(p2, (0, 0, 0, 0)),
                    font=TITLE_FONT,
                )
            else:
                draw.text(
                    xy=(CARD_BORDER, CARD_BORDER),
                    text=MAJOR_DISPLAY.get(p1, p1),
                    fill=HIGHLIGHT,
                    font=SYMBOL_FONT,
                )
            card_images.append(image if face == 0 else image.rotate(180))

    with Image.new(
        "RGBA", (len(card_images) * CARD_WG, CARD_H + CARD_OFF), color=(0, 0, 0, 0)
    ) as image:
        for i, card_img in enumerate(card_images):
            image.paste(
                card_img, (i * (CARD_W + CARD_GAP) + CARD_GAP, ((i + 1) % 2) * CARD_OFF)
            )
        with BytesIO() as buf:
            image.save(buf, "PNG")
            att = Attachment(name="image", body=buf.getvalue())
    return message.strip(), att
