import secrets
import uuid

from sqlalchemy import Column, BigInteger, Boolean, Unicode
from sqlalchemy.orm import relationship

from .meta import (
    Base,
    RBase,
    UUIDType,
    foreign_key,
    now_timestamp,
    get_object_by_id,
)

from ..lib.currency import cents_to_dollars


class GiftCard(RBase, Base):
    """
    A gift card is scoped to a single shop. The code IS the value —
    no account required to redeem. Balance decrements across purchases.
    Gift cards never expire (permacomputer rules).
    """

    id = Column(UUIDType, primary_key=True, index=True)
    shop_id = Column(UUIDType, foreign_key("Shop", "id"), nullable=False)
    code = Column(Unicode(64), nullable=False, unique=True, index=True)
    initial_amount_in_cents = Column(BigInteger, nullable=False)
    balance_in_cents = Column(BigInteger, nullable=False)
    purchaser_email = Column(Unicode(256), nullable=True)
    gift_email = Column(Unicode(256), nullable=True)
    gift_message = Column(Unicode(512), nullable=True)
    invoice_id = Column(UUIDType, foreign_key("Invoice", "id"), nullable=True)
    created_timestamp = Column(BigInteger, nullable=False)
    disabled = Column(Boolean, default=False, nullable=False)

    shop = relationship(argument="Shop", uselist=False, lazy="joined")
    invoice = relationship(argument="Invoice", uselist=False, lazy="joined")

    transactions = relationship(
        argument="GiftCardTransaction",
        lazy="dynamic",
        back_populates="gift_card",
    )

    def __init__(self, shop, amount_in_cents, purchaser_email=None,
                 gift_email=None, gift_message=None, invoice=None):
        self.id = uuid.uuid1()
        self.shop = shop
        self.code = generate_gift_card_code()
        self.initial_amount_in_cents = amount_in_cents
        self.balance_in_cents = amount_in_cents
        self.purchaser_email = purchaser_email
        self.gift_email = gift_email
        self.gift_message = gift_message
        self.invoice = invoice
        self.created_timestamp = now_timestamp()

    @property
    def is_valid(self):
        return not self.disabled and self.balance_in_cents > 0

    @property
    def balance(self):
        return cents_to_dollars(self.balance_in_cents)

    @property
    def initial_amount(self):
        return cents_to_dollars(self.initial_amount_in_cents)

    @property
    def shop_uuid_str(self):
        return self.id_to_uuid_str(self.shop_id)

    @property
    def is_fully_redeemed(self):
        return self.balance_in_cents <= 0

    def deduct(self, amount_in_cents):
        """Deduct amount from balance. Returns actual amount deducted."""
        deduction = min(amount_in_cents, self.balance_in_cents)
        self.balance_in_cents -= deduction
        return deduction


def generate_gift_card_code():
    """Generate a unique gift card code: GC- prefix + 16 hex chars uppercase."""
    return "GC-" + secrets.token_hex(8).upper()


def get_gift_card_by_id(dbsession, gift_card_id):
    return get_object_by_id(dbsession, gift_card_id, GiftCard)


def get_gift_card_by_code(dbsession, code, shop=None):
    query = dbsession.query(GiftCard).filter(GiftCard.code == code.strip().upper())
    if shop is not None:
        query = query.filter(GiftCard.shop_id == shop.id)
    return query.one_or_none()


def get_gift_cards_by_shop(dbsession, shop):
    return (
        dbsession.query(GiftCard)
        .filter(GiftCard.shop_id == shop.id)
        .order_by(GiftCard.created_timestamp.desc())
    )
