import uuid
from datetime import datetime, timezone
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, Float, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.database import Base


def new_uuid() -> str:
    return str(uuid.uuid4())

def now_utc() -> datetime:
    return datetime.now(timezone.utc)


# ── User ──────────────────────────────────────────────────────────────────────

class User(Base):
    __tablename__ = "users"

    id:               Mapped[str]       = mapped_column(String(36), primary_key=True, default=new_uuid)
    username:         Mapped[str]       = mapped_column(String(64), unique=True, nullable=False)
    email:            Mapped[str]       = mapped_column(String(255), unique=True, nullable=False)
    full_name:        Mapped[str]       = mapped_column(String(128), default="")
    password_hash:    Mapped[str]       = mapped_column(String(255), nullable=False)
    is_verified:      Mapped[bool]      = mapped_column(Boolean, default=False)
    verify_token:     Mapped[str|None]  = mapped_column(String(64), nullable=True)
    created_at:       Mapped[datetime]  = mapped_column(DateTime, default=now_utc)

    projects:        Mapped[list["Project"]]        = relationship("Project", back_populates="owner")
    memberships:     Mapped[list["ProjectMember"]]  = relationship("ProjectMember", back_populates="user")
    alt_ratings:     Mapped[list["DanceAltRating"]] = relationship("DanceAltRating", back_populates="user")
    playlist_shares: Mapped[list["PlaylistShare"]]  = relationship("PlaylistShare", foreign_keys="PlaylistShare.shared_with_id", back_populates="shared_with")


# ── Song (global — ikke knyttet til en bruger) ────────────────────────────────

class Song(Base):
    __tablename__ = "songs"

    id:           Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    title:        Mapped[str]      = mapped_column(String(255), nullable=False)
    artist:       Mapped[str]      = mapped_column(String(255), default="")
    album:        Mapped[str]      = mapped_column(String(255), default="")
    bpm:          Mapped[int]      = mapped_column(Integer, default=0)
    duration_sec: Mapped[int]      = mapped_column(Integer, default=0)
    mbid:         Mapped[str|None] = mapped_column(String(36), nullable=True, unique=True)
    acoustid:     Mapped[str|None] = mapped_column(String(64), nullable=True)
    synced_at:    Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    project_songs:   Mapped[list["ProjectSong"]]   = relationship("ProjectSong", back_populates="song")
    song_dances:     Mapped[list["SongDance"]]     = relationship("SongDance", back_populates="song", cascade="all, delete-orphan")
    song_alt_dances: Mapped[list["SongAltDance"]]  = relationship("SongAltDance", back_populates="song", cascade="all, delete-orphan")


# ── Dans-entitet ──────────────────────────────────────────────────────────────

class DanceLevel(Base):
    __tablename__ = "dance_levels"

    id:          Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    sort_order:  Mapped[int] = mapped_column(Integer, nullable=False)
    name:        Mapped[str] = mapped_column(String(64), unique=True, nullable=False)
    description: Mapped[str] = mapped_column(String(255), default="")


class Dance(Base):
    __tablename__ = "dances"
    __table_args__ = (UniqueConstraint("name", "level_id", name="uq_dance_name_level"),)

    id:            Mapped[int]       = mapped_column(Integer, primary_key=True, autoincrement=True)
    name:          Mapped[str]       = mapped_column(String(128), nullable=False)
    level_id:      Mapped[int|None]  = mapped_column(Integer, ForeignKey("dance_levels.id"), nullable=True)
    choreographer: Mapped[str]       = mapped_column(String(128), default="")
    video_url:     Mapped[str]       = mapped_column(String(512), default="")
    stepsheet_url: Mapped[str]       = mapped_column(String(512), default="")
    notes:         Mapped[str]       = mapped_column(Text, default="")
    use_count:     Mapped[int]       = mapped_column(Integer, default=1)
    synced_at:     Mapped[datetime|None] = mapped_column(DateTime, nullable=True)

    level: Mapped["DanceLevel|None"] = relationship("DanceLevel")


# ── Project / Playlist ────────────────────────────────────────────────────────

class Project(Base):
    __tablename__ = "projects"

    id:          Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    owner_id:    Mapped[str]      = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
    name:        Mapped[str]      = mapped_column(String(128), nullable=False)
    description: Mapped[str]      = mapped_column(Text, default="")
    visibility:  Mapped[str]      = mapped_column(String(16), default="private")
    updated_at:  Mapped[datetime] = mapped_column(DateTime, default=now_utc, onupdate=now_utc)
    created_at:  Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    owner:         Mapped["User"]               = relationship("User", back_populates="projects")
    members:       Mapped[list["ProjectMember"]] = relationship("ProjectMember", back_populates="project", cascade="all, delete-orphan")
    project_songs: Mapped[list["ProjectSong"]]  = relationship("ProjectSong", back_populates="project", order_by="ProjectSong.position", cascade="all, delete-orphan")
    shares:        Mapped[list["PlaylistShare"]] = relationship("PlaylistShare", back_populates="project", cascade="all, delete-orphan")


class ProjectMember(Base):
    __tablename__ = "project_members"

    id:         Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    project_id: Mapped[str]      = mapped_column(String(36), ForeignKey("projects.id"), nullable=False)
    user_id:    Mapped[str]      = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
    role:       Mapped[str]      = mapped_column(String(16), default="viewer")
    status:     Mapped[str]      = mapped_column(String(16), default="pending")
    invited_at: Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    project: Mapped["Project"] = relationship("Project", back_populates="members")
    user:    Mapped["User"]    = relationship("User", back_populates="memberships")


class ProjectSong(Base):
    __tablename__ = "project_songs"

    id:             Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    project_id:     Mapped[str]      = mapped_column(String(36), ForeignKey("projects.id"), nullable=False)
    song_id:        Mapped[str]      = mapped_column(String(36), ForeignKey("songs.id"), nullable=False)
    position:       Mapped[int]      = mapped_column(Integer, nullable=False)
    status:         Mapped[str]      = mapped_column(String(16), default="pending")
    is_workshop:    Mapped[bool]     = mapped_column(Boolean, default=False)
    dance_override: Mapped[str]      = mapped_column(String(128), default="")

    project: Mapped["Project"] = relationship("Project", back_populates="project_songs")
    song:    Mapped["Song"]    = relationship("Song", back_populates="project_songs")


class PlaylistShare(Base):
    __tablename__ = "playlist_shares"
    __table_args__ = (UniqueConstraint("project_id", "shared_with_id", name="uq_share"),)

    id:             Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    project_id:     Mapped[str]      = mapped_column(String(36), ForeignKey("projects.id"), nullable=False)
    shared_with_id: Mapped[str|None] = mapped_column(String(36), ForeignKey("users.id"), nullable=True)
    invited_email:  Mapped[str]      = mapped_column(String(255), default="")
    permission:     Mapped[str]      = mapped_column(String(16), default="view")
    accepted_at:    Mapped[datetime|None] = mapped_column(DateTime, nullable=True)
    created_at:     Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    project:      Mapped["Project"]   = relationship("Project", back_populates="shares")
    shared_with:  Mapped["User|None"] = relationship("User", foreign_keys=[shared_with_id], back_populates="playlist_shares")


# ── Sang-dans tags ────────────────────────────────────────────────────────────

class SongDance(Base):
    __tablename__ = "song_dances"
    __table_args__ = (UniqueConstraint("song_id", "dance_id", name="uq_song_dance"),)

    id:          Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
    song_id:     Mapped[str] = mapped_column(String(36), ForeignKey("songs.id"), nullable=False)
    dance_id:    Mapped[int] = mapped_column(Integer, ForeignKey("dances.id"), nullable=False)
    dance_order: Mapped[int] = mapped_column(Integer, default=1)

    song:  Mapped["Song"]  = relationship("Song", back_populates="song_dances")
    dance: Mapped["Dance"] = relationship("Dance")


class SongAltDance(Base):
    __tablename__ = "song_alt_dances"
    __table_args__ = (UniqueConstraint("song_id", "dance_id", name="uq_song_alt_dance"),)

    id:       Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
    song_id:  Mapped[str] = mapped_column(String(36), ForeignKey("songs.id"), nullable=False)
    dance_id: Mapped[int] = mapped_column(Integer, ForeignKey("dances.id"), nullable=False)
    note:     Mapped[str] = mapped_column(String(255), default="")

    song:  Mapped["Song"]  = relationship("Song", back_populates="song_alt_dances")
    dance: Mapped["Dance"] = relationship("Dance")


# ── Community dans-tags ───────────────────────────────────────────────────────

class CommunityDance(Base):
    __tablename__ = "community_dances"
    __table_args__ = (UniqueConstraint("song_mbid", "song_title", "song_artist", "dance_id", name="uq_comm_dance"),)

    id:           Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    song_mbid:    Mapped[str|None] = mapped_column(String(36), nullable=True)
    song_title:   Mapped[str]      = mapped_column(String(255), default="")
    song_artist:  Mapped[str]      = mapped_column(String(255), default="")
    dance_id:     Mapped[int]      = mapped_column(Integer, ForeignKey("dances.id"), nullable=False)
    submitted_by: Mapped[str]      = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
    created_at:   Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    dance: Mapped["Dance"] = relationship("Dance")


class CommunityDanceAlt(Base):
    __tablename__ = "community_dance_alts"
    __table_args__ = (UniqueConstraint("song_mbid", "song_title", "song_artist", "alt_dance_id", name="uq_comm_alt"),)

    id:           Mapped[str]      = mapped_column(String(36), primary_key=True, default=new_uuid)
    song_mbid:    Mapped[str|None] = mapped_column(String(36), nullable=True)
    song_title:   Mapped[str]      = mapped_column(String(255), default="")
    song_artist:  Mapped[str]      = mapped_column(String(255), default="")
    alt_dance_id: Mapped[int]      = mapped_column(Integer, ForeignKey("dances.id"), nullable=False)
    note:         Mapped[str]      = mapped_column(Text, default="")
    submitted_by: Mapped[str]      = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
    avg_rating:   Mapped[float]    = mapped_column(Float, default=0.0)
    rating_count: Mapped[int]      = mapped_column(Integer, default=0)
    created_at:   Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    alt_dance: Mapped["Dance"]               = relationship("Dance")
    ratings:   Mapped[list["DanceAltRating"]] = relationship("DanceAltRating", back_populates="alternative", cascade="all, delete-orphan")


class DanceAltRating(Base):
    __tablename__ = "dance_alt_ratings"
    __table_args__ = (UniqueConstraint("alternative_id", "user_id", name="uq_rating"),)

    id:             Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid)
    alternative_id: Mapped[str] = mapped_column(String(36), ForeignKey("community_dance_alts.id"), nullable=False)
    user_id:        Mapped[str] = mapped_column(String(36), ForeignKey("users.id"), nullable=False)
    score:          Mapped[int] = mapped_column(Integer, nullable=False)
    created_at:     Mapped[datetime] = mapped_column(DateTime, default=now_utc)

    alternative: Mapped["CommunityDanceAlt"] = relationship("CommunityDanceAlt", back_populates="ratings")
    user:        Mapped["User"]              = relationship("User", back_populates="alt_ratings")