
from files.classes.comment import Comment
from files.classes.volunteer_janitor import VolunteerJanitorRecord, VolunteerJanitorResult
from files.helpers.math import saturate, remap

# Returns (IsBad, Confidence)
def evaluate_badness_of(choice):
    if choice == VolunteerJanitorResult.Warning:
        return True, 1
    if choice == VolunteerJanitorResult.Ban:
        return True, 1
    
    # treating this like a low-weight bad response
    if choice == VolunteerJanitorResult.Bad:
        return True, 0.5
    
    # treating this like a low-weight not-bad response
    if choice == VolunteerJanitorResult.Neutral:
        return False, 0.5
    
    return False, 1

def userweight_from_user_accuracy(accuracy):
    return saturate(remap(accuracy, 0.5, 1, 0, 1))

def calculate_final_comment_badness(comment_total, comment_weight, conservative):
    if not conservative:
        # insert an artificial 50%-badness confident vote, to prevent us from ever reaching 1.0 or 0.0
        return (comment_total + 0.5) / (comment_weight + 1.0)
    
    if comment_weight == 0:
        # INSUFFICIENT DATA FOR A MEANINGFUL ANSWER
        return 0.5
    
    original_badness = calculate_final_comment_badness(comment_total, comment_weight, False)
    if original_badness > 0.5:
        # fake a not-bad vote, with the confidence being the opposite of our confidence in it being bad
        # don't let it invert though
        forged_weight = 1.0 - original_badness
        calculated_badness = max(comment_total / (comment_weight + forged_weight), 0.5)
    else:
        # fake a bad vote, with the confidence being the opposite of our confidence in it being not-bad
        # don't let it invert though
        forged_weight = original_badness
        calculated_badness = min((comment_total + forged_weight) / (comment_weight + forged_weight), 0.5)
    
    return calculated_badness

def update_comment_badness(db, cid, diagnostics: bool = False):
    # Recalculate the comment's confidence values
    # This probably does more SQL queries than it should
    records = db.query(VolunteerJanitorRecord) \
        .where(VolunteerJanitorRecord.comment_id == cid) \
        .order_by(VolunteerJanitorRecord.recorded_datetimez)

    user_has_pending = {}
    earliest_submission = {}

    for rec in records:
        if rec.result == VolunteerJanitorResult.Pending:
            user_has_pending[rec.user_id] = True
        else:
            if rec.user_id in user_has_pending:
                if rec.user_id not in earliest_submission or earliest_submission[rec.user_id].recorded_datetimez > rec.recorded_datetimez:
                    earliest_submission[rec.user_id] = rec
    
    badness = 0
    weight = 0

    for submission in earliest_submission.values():
        userweight_user = userweight_from_user_accuracy(submission.user.volunteer_janitor_correctness);
        submission_bad, submission_weight = evaluate_badness_of(submission.result)

        additive_weight = submission_weight * userweight_user

        weight += additive_weight
        if submission_bad:
            badness += additive_weight

    comment_badness = calculate_final_comment_badness(badness, weight, True)

    db.query(Comment) \
        .where(Comment.id == cid) \
        .update({Comment.volunteer_janitor_badness: comment_badness})

    if diagnostics:
        print(f"Updated comment {cid} to {comment_badness}")
