import datetime
import enum
import uuid

import sqlalchemy
from sqlalchemy import CheckConstraint, DateTime, ForeignKey, UniqueConstraint, create_engine, event, func, select
from sqlalchemy import Enum as SAEnum
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, Session, mapped_column, relationship

from idlemaxx.models.activities import (
    Activity,
    ActivityCategory,
    ActivityData,
    ActivityItemReward,
    ActivityMaterial,
    ActivitySkillRequirement,
    ActivitySkillReward,
)
from idlemaxx.models.items import Item
from idlemaxx.models.skills import Skill


class TradeStatus(enum.Enum):
    PENDING = "pending"
    ACCEPTED = "accepted"
    REJECTED = "rejected"


def now_utc() -> datetime.datetime:
    return datetime.datetime.now(tz=datetime.UTC).replace(tzinfo=None)


def xp_for_level(level: int) -> int:
    total = 0
    for i in range(1, level):
        total += int(i + 300 * 2 ** (i / 7.0))
    return total // 4


def level_from_xp(xp: int) -> int:
    level = 1
    while xp_for_level(level + 1) <= xp:
        level += 1
    return level


class IdlemaxBase(MappedAsDataclass, DeclarativeBase):
    __abstract__ = True
    id: Mapped[uuid.UUID] = mapped_column(default_factory=uuid.uuid4, primary_key=True, init=False)
    created_at: Mapped[datetime.datetime] = mapped_column(default_factory=now_utc, init=False)
    updated_at: Mapped[datetime.datetime] = mapped_column(default_factory=now_utc, onupdate=now_utc, init=False)


class Character(IdlemaxBase):
    __tablename__ = "characters"
    name: Mapped[str] = mapped_column(unique=True)
    discord_user_id: Mapped[str | None] = mapped_column(default=None)

    skills: Mapped[list["CharacterSkill"]] = relationship(default_factory=list, cascade="all, delete-orphan")
    activities: Mapped[list["CharacterActivity"]] = relationship(default_factory=list, cascade="all, delete-orphan")
    items: Mapped[list["CharacterItem"]] = relationship(default_factory=list, cascade="all, delete-orphan")
    activity_queue: Mapped[list["CharacterActivityQueue"]] = relationship(
        default_factory=list, cascade="all, delete-orphan", order_by="CharacterActivityQueue.created_at"
    )

    @property
    def current_activity(self) -> "CharacterActivity | None":
        for activity in self.activities:
            if activity.ended_at is None:
                return activity
        return None


class CharacterSkill(IdlemaxBase):
    __tablename__ = "character_skills"
    character_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    skill: Mapped[Skill] = mapped_column(SAEnum(Skill))
    experience: Mapped[int] = mapped_column(default=0)

    __table_args__ = (
        UniqueConstraint("character_id", "skill", name="character_id_skill_uc"),
        CheckConstraint("experience >= 0", name="experience_non_negative"),
    )


class CharacterItem(IdlemaxBase):
    __tablename__ = "character_items"
    character_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    item: Mapped[Item] = mapped_column(SAEnum(Item))
    quantity: Mapped[int] = mapped_column(default=0)

    __table_args__ = (
        UniqueConstraint("character_id", "item", name="character_id_item_uc"),
        CheckConstraint("quantity >= 0", name="quantity_non_negative"),
    )


class CharacterActivity(IdlemaxBase):
    __tablename__ = "character_activities"
    character_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    activity: Mapped[Activity] = mapped_column(SAEnum(Activity))
    action_limit: Mapped[int | None] = mapped_column(default=None)
    started_at: Mapped[datetime.datetime] = mapped_column(default_factory=now_utc, init=False)
    ended_at: Mapped[datetime.datetime | None] = mapped_column(default=None, init=False)

    xp_rewards: Mapped[list["CharacterActivityXpReward"]] = relationship(
        default_factory=list, cascade="all, delete-orphan"
    )
    item_rewards: Mapped[list["CharacterActivityItemReward"]] = relationship(
        default_factory=list, cascade="all, delete-orphan"
    )
    materials_consumed: Mapped[list["CharacterActivityMaterialConsumed"]] = relationship(
        default_factory=list, cascade="all, delete-orphan"
    )

    __table_args__ = (CheckConstraint("ended_at is null or started_at <= ended_at", name="started_before_end"),)


class CharacterActivityXpReward(IdlemaxBase):
    __tablename__ = "character_activity_xp_rewards"
    character_activity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("character_activities.id"))

    skill: Mapped[Skill] = mapped_column(SAEnum(Skill))
    xp_gained: Mapped[int]


class CharacterActivityItemReward(IdlemaxBase):
    __tablename__ = "character_activity_item_rewards"
    character_activity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("character_activities.id"))

    item: Mapped[Item] = mapped_column(SAEnum(Item))
    quantity: Mapped[int]


class CharacterActivityMaterialConsumed(IdlemaxBase):
    __tablename__ = "character_activity_materials_consumed"

    character_activity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("character_activities.id"))
    item: Mapped[Item] = mapped_column(SAEnum(Item))
    quantity: Mapped[int]


class CharacterActivityQueue(IdlemaxBase):
    __tablename__ = "character_activity_queue"
    character_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    activity: Mapped[Activity] = mapped_column(SAEnum(Activity))
    action_limit: Mapped[int | None] = mapped_column(default=None)
    started_at: Mapped[datetime.datetime | None] = mapped_column(default=None, init=False)
    # created_at (from IdlemaxBase) = queued_at; ordered by created_at ASC
    # started_at IS NULL  → pending; IS NOT NULL → picked up


class Trade(IdlemaxBase):
    __tablename__ = "trades"
    initiator_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    counterparty_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    current_turn_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    status: Mapped[TradeStatus] = mapped_column(SAEnum(TradeStatus), default=TradeStatus.PENDING)

    offers: Mapped[list["TradeOffer"]] = relationship(
        default_factory=list,
        cascade="all, delete-orphan",
        order_by="TradeOffer.created_at",
    )


class TradeOffer(IdlemaxBase):
    __tablename__ = "trade_offers"
    trade_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("trades.id"))
    proposed_by_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))

    items: Mapped[list["TradeOfferItem"]] = relationship(
        default_factory=list,
        cascade="all, delete-orphan",
    )


class TradeOfferItem(IdlemaxBase):
    __tablename__ = "trade_offer_items"
    trade_offer_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("trade_offers.id"))
    offered_by_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("characters.id"))
    item: Mapped[Item] = mapped_column(SAEnum(Item))
    quantity: Mapped[int]

    __table_args__ = (CheckConstraint("quantity > 0", name="trade_offer_item_quantity_positive"),)
