
from typing import Optional
from files.__main__ import app
from files.classes.comment import Comment
from files.classes.flags import CommentFlag
from files.classes.user import User
from files.classes.visstate import StateReport, StateMod
from files.classes.volunteer_janitor import VolunteerJanitorRecord, VolunteerJanitorResult
from files.helpers.volunteer_janitor import update_comment_badness
from files.routes.volunteer_common import VolunteerDuty
from flask import g
import pprint
import random
import sqlalchemy
from sqlalchemy.orm import aliased

class VolunteerDutyJanitor(VolunteerDuty):

    def __init__(self, choices):
        self.choices = choices

    def accept(self, v) -> None:
        for item in self.choices:
            record = VolunteerJanitorRecord()
            record.user_id = v.id
            record.comment_id = item
            record.result = VolunteerJanitorResult.Pending
            g.db.add(record)
        
        g.db.commit()

    def embed_template(self) -> str:
        return "volunteer_janitor.html"

    def comments(self) -> list[Comment]:
        return g.db.query(Comment).where(Comment.id.in_(self.choices))


def get_duty(u: User) -> Optional[VolunteerDutyJanitor]:
    if not app.config['VOLUNTEER_JANITOR_ENABLE']:
        return None

    # these could probably be combined into one query somehow

    # find reported visible comments not made by the current user or in reply to the current user
    ParentComment = aliased(Comment)
    reported_comments = g.db.query(Comment) \
        .where(Comment.state_report == StateReport.REPORTED) \
        .where(Comment.state_mod == StateMod.VISIBLE) \
        .where(Comment.state_user_deleted_utc == None) \
        .where(Comment.author_id != u.id) \
        .outerjoin(ParentComment, ParentComment.id == Comment.parent_comment_id) \
        .where(sqlalchemy.or_(ParentComment.author_id != u.id, ParentComment.author_id == None)) \
        .with_entities(Comment.id)

    reported_ids = [reported.id for reported in reported_comments]
    
    if not app.config['DBG_VOLUNTEER_PERMISSIVE']:
        # find distinguished children of those reported comments
        # this is a ghastly query and should be fixed when we're doing some kind of cleanup and mod-action formalization
        distinguished_children = g.db.query(Comment) \
            .where(Comment.parent_comment_id.in_(reported_ids)) \
            .where(Comment.distinguish_level > 0) \
            .with_entities(Comment.parent_comment_id)

        distinguished_children_ids = [child.parent_comment_id for child in distinguished_children]

        # filter
        # we're doing this because we don't want to give people hints as to the "right" result
        # once a modhat hits, that's it, doesn't show up in the volunteer system anymore
        clean_reported = set(reported_ids) - set(distinguished_children_ids)

        # also, let's make sure it has a report that isn't made by this user
        nonuser_reports = g.db.query(CommentFlag) \
            .where(CommentFlag.comment_id.in_(clean_reported)) \
            .where(CommentFlag.user_id != u.id) \
            .with_entities(CommentFlag.comment_id)

        # also, let's make sure it hasn't already been looked at by this user
        seen_records = g.db.query(VolunteerJanitorRecord) \
            .where(VolunteerJanitorRecord.comment_id.in_(nonuser_reports)) \
            .where(VolunteerJanitorRecord.user_id == u.id) \
            .with_entities(VolunteerJanitorRecord.comment_id)

        final_reported = list({report.comment_id for report in nonuser_reports} - {record.comment_id for record in seen_records})
    else:
        final_reported = reported_ids

    if len(final_reported) <= 0:
        return None
    
    return VolunteerDutyJanitor(random.sample(final_reported, k = min(3, len(final_reported))))

def submitted(v: User, key: str, val: str) -> None:
    record = VolunteerJanitorRecord()
    record.user_id = v.id
    record.comment_id = key
    record.result = VolunteerJanitorResult(int(val))
    g.db.add(record)

    update_comment_badness(g.db, key)

    g.db.commit()
