from datetime import datetime
import json as _json
from app.extensions import db


class Match(db.Model):
    __tablename__ = "matches"
    id = db.Column(db.Integer, primary_key=True)
    game_id = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False)
    mode_id = db.Column(db.Integer, db.ForeignKey("game_modes.id"), nullable=False)
    region = db.Column(db.String(12), default="global")
    title = db.Column(db.String(150), nullable=False)
    starts_at = db.Column(db.DateTime, nullable=False)
    ends_at = db.Column(db.DateTime)
    entry_fee = db.Column(db.Integer, default=0)
    prize_pool = db.Column(db.Integer, default=0)
    capacity = db.Column(db.Integer, default=100)
    team_mode = db.Column(db.Boolean, default=False)
    status = db.Column(db.String(20), default="upcoming")
    room_code = db.Column(db.String(50))
    room_password = db.Column(db.String(50))
    credentials_published = db.Column(db.Boolean, default=False)
    # List of winning MatchParticipant ids stored as JSON: '[1, 5]'
    winner_participant_ids = db.Column(db.Text, default="[]")
    # Slot layout config: {"groups":[{"name":"City","slots":10,"players_per_slot":1},{"name":"Normal","slots":20,"players_per_slot":1}],"auto_fill":false}
    slot_layout = db.Column(db.Text, default="{}")
    # Admin-written match rules (create/edit panel). Shown to players when
    # they want to register — the registration flow displays ONLY these rules.
    rules = db.Column(db.Text)
    # Registration window rule (Task 14 — flipped per user request):
    #   False (default) -> registration OPENS at 12 midnight (00:00) of the match
    #                     day (the midnight of the night before the match day).
    #                     Before that moment registration is NOT open yet.
    #   True  (admin-selected exception) -> no opening gate: registration is open
    #                     immediately, right up to match start.
    # Either way a match whose start time has passed is NEVER registrable.
    reg_open_until_start = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    participants = db.relationship("MatchParticipant", backref="match",
                                    lazy="dynamic", cascade="all,delete-orphan")
    slot_participants = db.relationship("MatchSlotParticipant", backref="match",
                                       lazy="dynamic", cascade="all,delete-orphan")
    game = db.relationship("Game")
    mode = db.relationship("GameMode")

    @property
    def is_special(self):
        return False

    # ── Registration window rules (Task 14: opens at midnight) ─────
    @property
    def registration_opens_at(self):
        """The moment registration OPENS (or None when already open).

        Default rule: opens exactly at 12 midnight (00:00) of the match day —
        i.e. the midnight of the night before the match day. Players cannot
        register before that moment.
        Admin-selected exception (reg_open_until_start): no opening gate —
        registration is open right away, right up to match start.
        """
        if self.reg_open_until_start:
            return None
        day_start = self.starts_at.replace(hour=0, minute=0, second=0, microsecond=0)
        if day_start >= self.starts_at:
            # Match starts at/just after midnight itself — a 00:00 gate would
            # leave a zero-length window, so don't gate it.
            return None
        return day_start

    @property
    def registration_open(self):
        """True while players may join (window open, match not started)."""
        if self.status in ("finished", "cancelled", "live"):
            return False
        now = datetime.utcnow()
        if now >= self.starts_at:      # match time has passed -> never
            return False
        opens = self.registration_opens_at
        return opens is None or now >= opens

    @property
    def registration_closed(self):
        return not self.registration_open

    @property
    def registration_not_yet_open(self):
        """Closed specifically because the midnight opening moment has not
        arrived yet (as opposed to started/finished/cancelled)."""
        if self.status in ("finished", "cancelled", "live"):
            return False
        now = datetime.utcnow()
        if now >= self.starts_at:
            return False
        opens = self.registration_opens_at
        return opens is not None and now < opens

    @property
    def current_players(self):
        return self.participants.filter(MatchParticipant.status.in_(["registered", "confirmed"])).count()

    @property
    def slots_used(self):
        from app.models.match import MatchSlotParticipant
        return MatchSlotParticipant.query.filter_by(match_id=self.id).count()

    @property
    def slot_config(self):
        try:
            return _json.loads(self.slot_layout or "{}")
        except Exception:
            return {}

    @property
    def slot_groups(self):
        cfg = self.slot_config
        return cfg.get("groups", [])

    @property
    def total_slots(self):
        return sum(g.get("slots", 0) for g in self.slot_groups)


class MatchSlotParticipant(db.Model):
    """Tracks which user/team occupies which slot position in a match."""
    __tablename__ = "match_slot_participants"
    id = db.Column(db.Integer, primary_key=True)
    match_id = db.Column(db.Integer, db.ForeignKey("matches.id"), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
    team_id = db.Column(db.Integer, db.ForeignKey("teams.id"), nullable=True)
    group_name = db.Column(db.String(30), default="")  # Which slot group this belongs to
    slot_index = db.Column(db.Integer, nullable=False)  # 0-based within group
    assigned_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship("User", foreign_keys=[user_id])
    team = db.relationship("Team", foreign_keys=[team_id])

    __table_args__ = (db.UniqueConstraint("match_id", "group_name", "slot_index", name="uq_match_slot_group"),)


class MatchParticipant(db.Model):
    __tablename__ = "match_participants"
    id = db.Column(db.Integer, primary_key=True)
    match_id = db.Column(db.Integer, db.ForeignKey("matches.id"), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
    team_id = db.Column(db.Integer, db.ForeignKey("teams.id"), nullable=True)
    status = db.Column(db.String(20), default="registered")
    paid = db.Column(db.Boolean, default=False)
    registered_at = db.Column(db.DateTime, default=datetime.utcnow)
    selected_slot_group = db.Column(db.String(30), nullable=True)
    selected_slot_index = db.Column(db.Integer, nullable=True)

    user = db.relationship("User", foreign_keys=[user_id])
    team = db.relationship("Team", foreign_keys=[team_id])

    __table_args__ = (db.UniqueConstraint("match_id", "user_id", name="uq_match_participant"),)
