# models.py
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import json

db = SQLAlchemy()

# 用户表
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=True)
    phone = db.Column(db.String(20), unique=True, nullable=True)
    password = db.Column(db.String(128), nullable=False)
    role = db.Column(db.String(20), default='student')  # admin, teacher, student
    avatar = db.Column(db.String(200), default='')  # 头像URL
    is_banned = db.Column(db.Boolean, default=False)
    completed_exams = db.Column(db.Integer, default=0)  # 已完成并批改的考试次数
    name_changed_at = db.Column(db.DateTime, nullable=True)  # 上次修改用户名时间
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # 关系
    exams_created = db.relationship('Exam', back_populates='creator', lazy=True)
    submissions = db.relationship('Submission', back_populates='user', lazy=True)
    drafts = db.relationship('Draft', back_populates='user', lazy=True)
    posts = db.relationship('Post', back_populates='author', lazy=True)
    replies = db.relationship('Reply', back_populates='author', lazy=True)
    reactions = db.relationship('Reaction', back_populates='user', lazy=True)
    bookmarks = db.relationship('Bookmark', back_populates='user', lazy=True)
    reports = db.relationship('Report', backref='reporter', lazy=True)
    notifications = db.relationship('Notification', backref='user', lazy=True)
    contest_memberships = db.relationship('ContestMembership', back_populates='user', lazy=True)
    contest_registrations = db.relationship('ContestRegistration', backref='user', lazy=True)
    # teacher_applications 将通过 TeacherApplication 的 back_populates 自动创建

# 杯赛成员表（记录用户在特定杯赛中的角色）
class ContestMembership(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
    role = db.Column(db.String(20), nullable=False)  # 'owner' 或 'teacher'
    joined_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', back_populates='contest_memberships')
    contest = db.relationship('Contest', back_populates='members')

    __table_args__ = (db.UniqueConstraint('user_id', 'contest_id', name='uq_user_contest'),)

# 杯赛申请表（用于用户申请举办新杯赛）
class ContestApplication(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    name = db.Column(db.String(100), nullable=False)
    organizer = db.Column(db.String(100))
    description = db.Column(db.Text)
    contact = db.Column(db.String(100))
    status = db.Column(db.String(20), default='pending')  # pending, approved, rejected
    applied_at = db.Column(db.DateTime, default=datetime.utcnow)
    reviewed_at = db.Column(db.DateTime)
    total_score = db.Column(db.Integer, default=150)  # 杯赛默认满分
    start_date = db.Column(db.String(20))  # 申请时填写的开始日期
    end_date = db.Column(db.String(20))    # 申请时填写的结束日期
    # 报备信息
    responsible_person = db.Column(db.String(80))   # 责任人姓名
    responsible_phone = db.Column(db.String(20))    # 责任人电话
    responsible_email = db.Column(db.String(120))   # 责任人邮箱
    organization = db.Column(db.String(100))        # 所属机构/学校
    # 拒绝次数控制
    rejection_count = db.Column(db.Integer, default=0)  # 被拒绝次数
    last_rejected_at = db.Column(db.DateTime)  # 最后一次被拒绝的时间

    user = db.relationship('User', backref='contest_applications')

# 杯赛报名表（用户报名参加杯赛）
class ContestRegistration(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
    registered_at = db.Column(db.DateTime, default=datetime.utcnow)

    __table_args__ = (db.UniqueConstraint('user_id', 'contest_id', name='uq_user_contest_reg'),)

# 杯赛表
class Contest(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    organizer = db.Column(db.String(100))
    description = db.Column(db.Text)
    start_date = db.Column(db.String(20))
    end_date = db.Column(db.String(20))
    status = db.Column(db.String(20), default='upcoming')
    participants = db.Column(db.Integer, default=0)
    created_by = db.Column(db.Integer, db.ForeignKey('user.id'))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    past_papers = db.Column(db.Text)  # JSON 存储历年真题 [{year, title, file}]
    total_score = db.Column(db.Integer, default=150)  # 杯赛默认考试满分
    visible = db.Column(db.Boolean, default=True)  # 是否对普通用户可见
    # 报备信息
    responsible_person = db.Column(db.String(80))   # 责任人姓名
    responsible_phone = db.Column(db.String(20))    # 责任人电话
    responsible_email = db.Column(db.String(120))   # 责任人邮箱
    organization = db.Column(db.String(100))        # 所属机构/学校

    creator = db.relationship('User', backref='contests_created')
    members = db.relationship('ContestMembership', back_populates='contest', lazy=True, cascade='all, delete-orphan')
    exams = db.relationship('Exam', backref='contest', lazy=True)
    posts = db.relationship('Post', back_populates='contest', lazy=True, cascade='all, delete-orphan')
    teacher_applications = db.relationship('TeacherApplication', back_populates='contest', lazy=True, cascade='all, delete-orphan')

    def get_past_papers(self):
        return json.loads(self.past_papers) if self.past_papers else []

    def set_past_papers(self, papers):
        self.past_papers = json.dumps(papers)

# 题库表（杯赛老师和负责人往题库添加题目，负责人选题组卷）
class QuestionBankItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
    contributor_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    type = db.Column(db.String(20), nullable=False)  # 'choice', 'fill', 'essay'
    content = db.Column(db.Text, nullable=False)      # 题目内容
    options = db.Column(db.Text)                       # JSON，选择题选项
    answer = db.Column(db.Text)                        # 答案
    score = db.Column(db.Integer, default=10)          # 建议分值
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    contest = db.relationship('Contest', backref='question_bank_items')
    contributor = db.relationship('User', backref='contributed_questions')

# 考试表
class Exam(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=True)
    title = db.Column(db.String(200), nullable=False)
    subject = db.Column(db.String(50))
    duration = db.Column(db.Integer)
    total_score = db.Column(db.Integer)
    creator_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    questions = db.Column(db.Text)  # JSON
    encrypted_questions = db.Column(db.Text)  # 加密后的试卷内容
    is_encrypted = db.Column(db.Boolean, default=False)  # 是否加密
    access_password = db.Column(db.String(128))  # 考试访问密码
    scheduled_start = db.Column(db.DateTime, nullable=True)  # 预定开始时间
    scheduled_end = db.Column(db.DateTime, nullable=True)    # 预定结束时间
    score_release_time = db.Column(db.DateTime, nullable=True)  # 成绩公布时间
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    status = db.Column(db.String(20), default='available')

    creator = db.relationship('User', back_populates='exams_created')
    submissions = db.relationship('Submission', back_populates='exam', lazy=True, cascade='all, delete-orphan')
    drafts = db.relationship('Draft', back_populates='exam', lazy=True, cascade='all, delete-orphan')

    def get_questions(self):
        return json.loads(self.questions) if self.questions else []

    def set_questions(self, questions):
        self.questions = json.dumps(questions)

# 提交记录表
class Submission(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    exam_id = db.Column(db.Integer, db.ForeignKey('exam.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    answers = db.Column(db.Text)  # JSON
    score = db.Column(db.Integer, default=0)
    question_scores = db.Column(db.Text)  # JSON
    graded = db.Column(db.Boolean, default=False)
    graded_by = db.Column(db.String(80))
    submitted_at = db.Column(db.DateTime, default=datetime.utcnow)

    exam = db.relationship('Exam', back_populates='submissions')
    user = db.relationship('User', back_populates='submissions')

    def get_answers(self):
        return json.loads(self.answers) if self.answers else {}

    def set_answers(self, answers):
        self.answers = json.dumps(answers)

    def get_question_scores(self):
        return json.loads(self.question_scores) if self.question_scores else {}

    def set_question_scores(self, scores):
        self.question_scores = json.dumps(scores)

# 草稿表
class Draft(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    exam_id = db.Column(db.Integer, db.ForeignKey('exam.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    answers = db.Column(db.Text)  # JSON
    saved_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    exam = db.relationship('Exam', back_populates='drafts')
    user = db.relationship('User', back_populates='drafts')

    def get_answers(self):
        return json.loads(self.answers) if self.answers else {}

    def set_answers(self, answers):
        self.answers = json.dumps(answers)

# 论坛帖子表
class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    tag = db.Column(db.String(50), default='全部')
    is_official = db.Column(db.Boolean, default=False)
    pinned = db.Column(db.Boolean, default=False)
    likes = db.Column(db.Integer, default=0)
    replies_count = db.Column(db.Integer, default=0)
    views = db.Column(db.Integer, default=0)
    has_poll = db.Column(db.Boolean, default=False)
    images = db.Column(db.Text)  # JSON
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    author = db.relationship('User', back_populates='posts')
    replies = db.relationship('Reply', back_populates='post', lazy=True, cascade='all, delete-orphan')
    poll = db.relationship('Poll', back_populates='post', uselist=False, cascade='all, delete-orphan')
    reactions = db.relationship('Reaction', back_populates='post', lazy=True, cascade='all, delete-orphan')
    bookmarks = db.relationship('Bookmark', back_populates='post', lazy=True, cascade='all, delete-orphan')
    contest = db.relationship('Contest', back_populates='posts')

    def get_images(self):
        return json.loads(self.images) if self.images else []

    def set_images(self, images):
        self.images = json.dumps(images)

# 回复表
class Reply(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    content = db.Column(db.Text, nullable=False)
    likes = db.Column(db.Integer, default=0)
    reply_to = db.Column(db.String(80))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    post = db.relationship('Post', back_populates='replies')
    author = db.relationship('User', back_populates='replies')
    reactions = db.relationship('Reaction', back_populates='reply', lazy=True, cascade='all, delete-orphan')

# 投票表
class Poll(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    question = db.Column(db.String(200), nullable=False)
    options = db.Column(db.Text)  # JSON
    voters = db.Column(db.Text)   # JSON
    multi = db.Column(db.Boolean, default=False)
    total_votes = db.Column(db.Integer, default=0)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    post = db.relationship('Post', back_populates='poll')

    def get_options(self):
        return json.loads(self.options) if self.options else []

    def set_options(self, options):
        self.options = json.dumps(options)

    def get_voters(self):
        return json.loads(self.voters) if self.voters else {}

    def set_voters(self, voters):
        self.voters = json.dumps(voters)

# 表情反应表
class Reaction(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=True)
    reply_id = db.Column(db.Integer, db.ForeignKey('reply.id'), nullable=True)
    reaction = db.Column(db.String(20))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', back_populates='reactions')
    post = db.relationship('Post', back_populates='reactions')
    reply = db.relationship('Reply', back_populates='reactions')

    __table_args__ = (
        db.UniqueConstraint('user_id', 'post_id', name='uq_user_post_reaction'),
        db.UniqueConstraint('user_id', 'reply_id', name='uq_user_reply_reaction'),
    )

# 收藏表
class Bookmark(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', back_populates='bookmarks')
    post = db.relationship('Post', back_populates='bookmarks')

    __table_args__ = (db.UniqueConstraint('user_id', 'post_id', name='uq_user_post_bookmark'),)

# 举报表
class Report(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String(20))
    target_id = db.Column(db.Integer)
    reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    reason = db.Column(db.String(100))
    detail = db.Column(db.Text)
    status = db.Column(db.String(20), default='pending')
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # reporter 已在 User 中通过 backref 定义

# 通知表
class Notification(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    type = db.Column(db.String(50))
    content = db.Column(db.Text)
    from_user = db.Column(db.String(80))
    post_id = db.Column(db.Integer, nullable=True)
    read = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # user 已在 User 中通过 backref 定义

# 系统公告表（管理员发布的全站通知）
class SystemNotification(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    pinned = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    author = db.relationship('User', backref='system_notifications')

# 编辑历史表（可选）
class EditHistory(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
    title = db.Column(db.String(200))
    content = db.Column(db.Text)
    edited_by = db.Column(db.String(80))
    edited_at = db.Column(db.DateTime, default=datetime.utcnow)

    post = db.relationship('Post')

# 教师申请表（针对特定杯赛）
class TeacherApplication(db.Model):
    """教师申请记录（针对特定杯赛）"""
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
    name = db.Column(db.String(80), nullable=False)          # 申请人姓名
    email = db.Column(db.String(120), nullable=False)       # 申请人邮箱
    reason = db.Column(db.Text, nullable=False)             # 申请理由
    status = db.Column(db.String(20), default='pending')    # pending, approved, rejected
    applied_at = db.Column(db.DateTime, default=datetime.utcnow)
    reviewed_at = db.Column(db.DateTime)
    reviewed_by = db.Column(db.Integer, db.ForeignKey('user.id'))
    # 拒绝次数控制
    rejection_count = db.Column(db.Integer, default=0)  # 被拒绝次数
    last_rejected_at = db.Column(db.DateTime)  # 最后一次被拒绝的时间

    # 关系
    user = db.relationship('User', foreign_keys=[user_id], backref='teacher_applications')
    contest = db.relationship('Contest', back_populates='teacher_applications')
    reviewer = db.relationship('User', foreign_keys=[reviewed_by])

    __table_args__ = (db.UniqueConstraint('user_id', 'contest_id', name='uq_user_contest_application'),)

# 好友表
class Friend(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    friend_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    status = db.Column(db.String(20), default='pending')  # pending, accepted
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('friends_initiated', lazy='dynamic'))
    friend = db.relationship('User', foreign_keys=[friend_id], backref=db.backref('friends_received', lazy='dynamic'))

# 试卷收藏表
class ExamBookmark(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    exam_id = db.Column(db.Integer, db.ForeignKey('exam.id'), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', backref=db.backref('exam_bookmarks', lazy='dynamic', cascade='all, delete-orphan'))
    exam = db.relationship('Exam', backref=db.backref('bookmarked_by', lazy='dynamic', cascade='all, delete-orphan'))

    __table_args__ = (db.UniqueConstraint('user_id', 'exam_id', name='uq_user_exam_bookmark'),)

# 聊天室表
class ChatRoom(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String(20), nullable=False)  # 'private' | 'group' | 'contest'
    name = db.Column(db.String(100), nullable=True)
    avatar = db.Column(db.String(200), default='')
    creator_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
    contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), unique=True, nullable=True)
    announcement = db.Column(db.Text, default='')
    announcement_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
    announcement_at = db.Column(db.DateTime, nullable=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    creator = db.relationship('User', foreign_keys=[creator_id], backref='chatrooms_created')
    announcement_user = db.relationship('User', foreign_keys=[announcement_by])
    contest = db.relationship('Contest', backref=db.backref('chatroom', uselist=False))
    members = db.relationship('ChatRoomMember', backref='room', lazy=True, cascade='all, delete-orphan')
    messages = db.relationship('Message', backref='room', lazy=True, cascade='all, delete-orphan')

# 聊天室成员表
class ChatRoomMember(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    room_id = db.Column(db.Integer, db.ForeignKey('chat_room.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    role = db.Column(db.String(20), default='member')  # 'admin' | 'member'
    nickname = db.Column(db.String(50), default='')
    muted = db.Column(db.Boolean, default=False)
    last_read_at = db.Column(db.DateTime, default=datetime.utcnow)
    joined_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', backref='chat_memberships')

    __table_args__ = (db.UniqueConstraint('room_id', 'user_id', name='uq_room_user'),)

# 消息表
class Message(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    room_id = db.Column(db.Integer, db.ForeignKey('chat_room.id'), nullable=False)
    sender_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    type = db.Column(db.String(20), default='text')  # 'text' | 'image' | 'file' | 'system' | 'voice'
    content = db.Column(db.Text, default='')
    file_url = db.Column(db.String(300), nullable=True)
    file_name = db.Column(db.String(200), nullable=True)
    recalled = db.Column(db.Boolean, default=False)
    reply_to_id = db.Column(db.Integer, db.ForeignKey('message.id'), nullable=True)
    mentions = db.Column(db.Text, default='')  # JSON array of user IDs, e.g. "[1,2,3]" or "all"
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    sender = db.relationship('User', backref='messages_sent')
    reply_to = db.relationship('Message', remote_side='Message.id', backref='replies')
    reactions = db.relationship('MessageReaction', backref='message', lazy=True, cascade='all, delete-orphan')

# 消息表情回应表
class MessageReaction(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    message_id = db.Column(db.Integer, db.ForeignKey('message.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    emoji = db.Column(db.String(10), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    user = db.relationship('User', backref='message_reactions')

    __table_args__ = (db.UniqueConstraint('message_id', 'user_id', 'emoji', name='uq_msg_user_emoji'),)

# 邀请码表（教师申请审批通过后生成，老师输入邀请码激活身份）
class InviteCode(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.String(32), unique=True, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    application_id = db.Column(db.Integer, db.ForeignKey('teacher_application.id'), nullable=False)
    used = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    used_at = db.Column(db.DateTime, nullable=True)

    user = db.relationship('User', backref='invite_codes')
    application = db.relationship('TeacherApplication', backref='invite_code')