import rules
import datetime

# PERMISSIONS

# USER permissions

"""
USER permissions
- There are 5 types of user: basic, pro, plan, coach, freecoach
- These methods exist on user
    - user.rower
    - user.is_anonymous
    - user.is_staff

- These methods exist on rower
    - rower.rowerplan
    - rower.protrialexpires
    - rower.plantrialexpires
    - rower.mycoachgroup
    - rower.coachinggroups
    - rower.team

- These Rules apply to Rowers
    - Coach can have any number of groups
        - test_permissions.PermissionFreeCoach.test_coach_groupmanager
        - test_permissions.PermissionsBasicsTests.test_coach_groupmanager
        - test_permissions.PermissionsViewTests.test_coach_groups_create
    - Pro and Plan users can have one group and not more
        - test_permissions.PermissionsFreeCoach.test_pro_groupmanager
        - test_permissions.PermissionsViewTests.test_pro_groups_create
    - Free Coach user cannot have workouts
        - test_permissions.PermissionFreeCoach.test_add_workout_freecoach
    - Free coach can create more than one group
        - test_permissions.PermissionsFreeCoach.test_plan_groupmanager
    - Free Coach & Plan (Self Coach) can create planned sessions and team planned sessions
        - test_permissions.PermissionsFreeCoach.test_plan_create_session
        - test_permissions.PermissionsFreeCoach.test_coach_create_session
    - Pro cannot create planned sessions or team planned sessions
        - test_permissions.PermissionsViewTests.test_pro_create_session
    - Basic cannot join groups led by Free Coach
        - test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan
    - Coach can edit athlete settings (if in coachinggroup)
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_prefs_not
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_settings_not
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_account
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_account_not
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_exportsettings_not
    - Coach can upload a workout on behalf of athlete
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not
    - Pro and Plan cannot upload a workout on behalf of athlete
        - test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload
    - Coach can run analytics for athlete
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_analysis_not
        - test_permissions.PermissionsViewTests.test_pro_edit_athlete_analysis
    - Pro and Plan user cannot run analysis for members of their groups
        - test_permissions.PermissionsViewTests.test_plan_edit_athlete_analysis
    - Pro and Plan user cannot edit athlete sessings
        - test_permissions.PermissionsViewTests.test_plan_edit_athlete_settings
        - test_permissions.PermissionsViewTests.test_pro_edit_athlete_settings
    - Self-Coach and Pro cannot create more than one group
        - test_permissions.PermissionsFreeCoach.test_plan_groupmanager
    - Pro users and higher can join group led by other Pro or higher user
        - test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan
    - Pro or basic cannot create planned sessions or team planned sessions
        - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession

"""

# used in can_plan_user
@rules.predicate
def user_is_not_basic(user):
    if user.rower.rowerplan != 'basic':
        return True

    if user.rower.protrialexpires >= datetime.date.today() and user.rower.plantrialexpires >= datetime.date.today():
        return True

    return False

@rules.predicate
def user_is_basic(user):
    return not user_is_not_basic(user)

@rules.predicate
def can_start_trial(user):
    if user.is_anonymous:
        return False


    return user.rower.protrialexpires == datetime.date(1970,1,1)

@rules.predicate
def can_start_plantrial(user):
    if user.is_anonymous:
        return False

    return user.rower.plantrialexpires == datetime.date(1970,1,1)

@rules.predicate
def is_staff(user):
    return user.is_staff

@rules.predicate
def is_coach(user):
    return user.rower.rowerplan in ['coach','freecoach']

def is_paid_coach(user):
    return user.rower.rowerplan == 'coach'

@rules.predicate
def is_planmember(user):
    try:
        r = user.rower
    except AttributeError:
        return False

    return r.rowerplan in ['coach','plan'] # freecoach?

@rules.predicate
def is_promember(user):
    try:
        r = user.rower
    except AttributeError:
        return False

    return r.rowerplan in ['pro','coach','plan'] # freecoach?

@rules.predicate
def is_protrial(user):
    try:
        r = user.rower
    except AttributeError:
        return False

    if r.rowerplan == 'basic':
        return r.protrialexpires >= datetime.date.today()
    if r.rowerplan == 'freecoach':
        if r.mycoachgroup is not None:
            return len(r.mycoachgroup)>=4

    return False


ispromember = is_promember | is_protrial

can_have_teams = ispromember | is_coach

@rules.predicate
def can_add_team(user):
    if is_coach(user):
        return True

    if ispromember(user) or is_planmember(user):
        otherteams = user.rower.get_managed_teams()
        if otherteams.count() == 0:
            return True

    return False

@rules.predicate
def can_add_plan(user):
    return isplanmember(user) or is_coach(user)

@rules.predicate
def can_add_workout(user):
    if user.is_anonymous:
        return False

    return user.rower.rowerplan != 'freecoach'

@rules.predicate
def is_plantrial(user):
    try:
        r = user.rower
    except AttributeError:
        return False

    if r.rowerplan in ['basic','pro']:
        return r.plantrialexpires >= datetime.date.today()
    if r.rowerplan == 'freecoach':
        if r.mycoachgroup is not None:
            return len(r.mycoachgroup)>=4

    return False


isplanmember = is_planmember | is_plantrial

@rules.predicate
def can_add_session(user):
    return isplanmember(user) or is_coach(user)

# User / Coach relationships (Rower object)

@rules.predicate
def can_plan(user):
    if user.is_anonymous:
        return False

    return user.rower.rowerplan in ['plan','coach','freecoach']

# checks if rower is coach of user (or is user himself)
@rules.predicate
def is_coach_user(usercoach,userrower):
    if usercoach == userrower:
        return True

    if not is_coach(usercoach):
        return False

    r = userrower.rower

    coaches = []

    for group in r.coachinggroups.all():
        newcoaches = group.get_coaches()
        for coach in newcoaches:
            coaches.append(coach)

    for coach in coaches:
        if usercoach.rower == coach:
            return True

    return False

# checks if rower is coach of user (or is user himself)
@rules.predicate
def is_anonymous_or_coach(usercoach,userrower):
    print(usercoach,userrower)
    if usercoach == userrower:
        return True

    if userrower.is_anonymous and userrower.is_anonymous:
        return True

    if not is_coach(usercoach):
        return False

    r = userrower.rower

    coaches = []

    for group in r.coachinggroups.all():
        newcoaches = group.get_coaches()
        for coach in newcoaches:
            coaches.append(coach)

    for coach in coaches:
        if usercoach.rower == coach:
            return True

    return False

# check if rower and user are members of the same team
@rules.predicate
def is_rower_team_member(user,rower):
    if user.rower == rower:
        return True

    teams = rower.team.all()

    for team in teams:
        if team.private == 'open':
            if team in user.rower.team.all():
                return True
        if team.manager == user:
            return True

    return False

@rules.predicate
def can_add_workout_member(user,rower):
    if not user:
        return False
    if user.is_anonymous:
        return False
    if user == rower.user:
        return True
    # only below tested - need test user == rower.user
    return is_coach(user) and user.rower in rower.get_coaches()

# check if user can plan for the rower
@rules.predicate
def can_plan_user(user,rower):
    # user must have planning permission
    if not can_plan(user):
        return False

    # if has planning permission, can always plan for himself
    if rower == user.rower:
        return True

    teams = user.rower.get_managed_teams()
    # free coach, plan etc cannot plan for basic
    if not is_paid_coach(user) and user_is_not_basic(user):
        for t in teams:
            if rower in t.rower.all():
                return True

    # paying coach can plan for all kinds of rowers
    if is_paid_coach(user):
        for t in teams:
            if rower in t.rower.all():
                return True

    return False

rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser
rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser
rules.add_perm('rower.is_pro',ispromember)
rules.add_perm('rower.is_staff',is_staff)

# WORKOUT permissions

"""
WORKOUT permissions
- These methods exist on Workout
    - workout.privacy

- These rules apply to workouts
    - User can add, delete and change their own workouts
        - test_aworkouts
    - Coach can add and change workouts for their athletes, but not delete
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_upload_not
    - Basic, Pro and Plan user can view but cannot add or change an athlete's workout
        - test_permissions.PermissionsViewTests.test_coach_edit_athlete_workout
        - test_permissions.PermissionsViewTests.test_plan_edit_athlete_upload
        - test_permissions.PermissionsViewTests.test_plan_edit_athlete_workout
        - test_permissions.PermissionsViewTests.test_basic_edit_athelte_workout
    - Anonymous users can view team members' workout (but not see list of workouts)
        - test_aworkouts

    - Rules for Workouts are transferred to objects related to the Workout,
        - Charts
        - Video Analysis
        - WorkoutComment?

"""

# check if user is owner or coach of owner of workout
@rules.predicate
def is_workout_user(user,workout):
    if user.is_anonymous:
        return False

    try:
        r = user.rower
    except AttributeError:
        return False

    if workout.user == r:
        return True

    return is_coach_user(user,workout.user.user)

# check if user is in same team as owner of workout
@rules.predicate
def is_workout_team(user,workout):
    if user.is_anonymous:
        return False

    try:
        r = user.rower
    except AttributeError:
        return False

    if workout.user == r:
        return True

    return is_rower_team_member(user,workout.user)

# check if user can see workout
@rules.predicate
def can_view_workout(user,workout):
    if workout.privacy != 'private':
        return True
    if user.is_anonymous:
        return False
    return user == workout.user.user

can_change_workout = is_workout_user

rules.add_perm('workout.change_workout',can_change_workout) # replaces checkworkoutuser
rules.add_perm('workout.view_workout',can_view_workout) # replaces checkworkoutuserview



# checkviewworkouts

# PLANNING permissions

"""
- These rules apply to planning
    - Free coach can create planned sessions and team planned sessions
        - test_permissions.PermissionsViewTests.test_coach_create_session
    - Self coach and higher can create planned sessions and team planned sessions
        - test_permissions.PermissionsFreeCoach.test_plan_create_session
        - test_permissions.PermissionsFreeCoach.test_coach_create_session
    - Coach can create planned sessions and team planned sessions
        - test_permissions.PermissionsFreeCoach.test_plan_create_session
        - test_permissions.PermissionsFreeCoach.test_coach_create_session
    - Self Coach and higher can create planned sessions and team planned sessions
        - test_permissions.PermissionsViewTests.test_plan_create_session
    - Pro or Basic cannot create planned sessions or team planned sessions
        - test_permissions.PermissionsFreeCoach.test_pro_create_plannedsession
        - test_permissions.PermissionsFreeCoach.test_basic_create_plannedsession
    - WHO can comment (plannedsession_comment_view)
    - Only Session manager can change session
    - Strict View rules (stricter than workouts)
    - Basic user cannot manage a training plan

    - TrainingTarget
        - rules for view, add, change, delete
    - TrainingPlan
        - rules for view, add, change, delete
    - Cycle
        - inherits rules from TrainingPlan
    - PlannedSession
        - rules for view, add, change, delete, clone, save as template
        - check team

    - Special rules for Race (cannot be edited ex post)

"""

# Training Target rules

# untested can_view_target to can_delete_target
@rules.predicate
def can_view_target(user,target):
    if user.is_anonymous:
        return False
    if user == target.manager.user:
        return True

    # a target's coach can view as well
    if is_coach_user(user,target.manager.user):
        return True

    # the object can view as well
    if user.rower in target.rowers.all():
        return True

@rules.predicate
def can_change_target(user,target):
    if user.is_anonymous:
        return False
    return user == target.manager.user

@rules.predicate
def can_delete_target(user,target):
    if user.is_anonymous:
        return False
    return user == target.manager.user

rules.add_perm('target.view_target',can_view_target)
rules.add_perm('target.change_target',can_change_target)
rules.add_perm('target.delete_target',can_delete_target)

@rules.predicate
def can_view_plan(user,plan):
    if user.is_anonymous:
        return False
    if user == plan.manager.user:
        return True

    # a plan's coach can view as well
    # below untested
    if is_coach_user(user,plan.manager.user):
        return True

    # the object can view as well
    if user.rower in plan.rowers.all():
        return True

@rules.predicate
def can_change_plan(user,plan):
    if user.is_anonymous:
        return False
    return user == plan.manager.user

# below untested
@rules.predicate
def can_delete_plan(user,plan):
    if user.is_anonymous:
        return False
    return user == plan.manager.user



rules.add_perm('plan.view_plan',can_view_plan)
rules.add_perm('plan.change_plan',can_change_plan)
rules.add_perm('plan.delete_plan',can_delete_plan)
rules.add_perm('plan.can_add_plan',can_add_plan)

# untested
@rules.predicate
def can_view_cycle(user,cycle):
    try:
        return can_view_cycle(user,cycle.plan)
    except AttributeError:
        return can_view_plan(user,cycle.plan)

    return False

@rules.predicate
def can_change_cycle(user,cycle):
    try:
        return can_change_cycle(user,cycle.plan)
    except AttributeError:
        return can_change_plan(user,cycle.plan)

    return False

@rules.predicate
def can_delete_cycle(user,cycle):
    try:
        return can_delete_cycle(user,cycle.plan)
    except AttributeError:
        return can_delete_plan(user,cycle.plan)

    return False


rules.add_perm('cycle.view_cycle',can_view_cycle)
rules.add_perm('cycle.change_cycle',can_change_cycle)
rules.add_perm('cycle.delete_cycle',can_delete_cycle)


# check if user has view access to session
@rules.predicate
def can_view_session(user,session):
    if session.sessiontype in ['race','indoorrace']:
        return True
    if user.is_anonymous:
        return False
    # session manager can view session
    if user == session.manager:
        return True
    # if you're a rower in the session you can view it
    # below untested
    if user.rower in session.rower.all():
        return True
    # coach users can view sessions created by their team members
    # below untested
    if is_coach(user):
        teams = user.rower.get_managed_teams()
        teamusers = [member.u for member in t.rower.all()]
        if session.manager in teamusers:
            return True

    return False

@rules.predicate
def can_change_session(user,session):
    if user.is_anonymous:
        return False
    # session part of a race should not be changed through the session interface
    if session.sessiontype in ['race','indoorrace']:
        return False
    if user == session.manager:
        return True

    return False

@rules.predicate
def can_delete_session(user,session):
    if user.is_anonymous:
        return False

    if session.sessiontype in ['race','indoorrace']:
        return False

    if user == session.manager:
        return True

    return False



rules.add_perm('plannedsession.add_session',can_add_session)
rules.add_perm('plannedsession.view_session',can_view_session)
rules.add_perm('plannedsession.change_session',can_change_session)
rules.add_perm('plannedsession.delete_session',can_delete_session)

# TEAM (group) permissions

"""
- These methods exist on team
    - team.manager
    - team.private

- These rules apply to a team
    - A Pro or Plan rower can be manager of only 1 team
        - test_permissions.PermissionsFreeCoach.test_pro_groupmanager
        - test_permissions.PermissionsViewTests.test_pro_groups_create
    - A coach can have any number of teams (as manager)
        - test_permissions.PermissionsBasicTest.test_coach_groupmanager
    - Basic user cannot manage a group
        - test_permissions.PermissionsFreeCoach.test_basic_groupmanager
    - Basic user cannot join groups led by Free Coach or Pro
        - test_permissions.PermissionFreeCoach.test_add_basic_pro_or_plan
    - Basic athletes can be member of Coach led group
        - test_permissions.PermissionsBasicTest.test_add_coach
    - Pro users (and higher) can join team led by other Pro (or higher) user
            - test_permissions.PermissionsFreeCoach.test_add_proplan_pro_or_plan
            - test_permissions.PermissionsViewTests.test_team_member_request_pro_pro
            - test_permissions.PermissionsViewTests.test_team_member_request_basic_pro
    - On downgrade, Coach users lose all but their oldest team (add test!)

"""

# check if user is manager of the team
@rules.predicate
def is_team_manager(user,team):
    return team.manager == user

# check is user is member of team - untested
@rules.predicate
def is_team_member(user,team):
    members = team.rower.all()
    return user in [member.user for member in members]

# check if user can view team
@rules.predicate
def can_view_team(user,team):
    # user based - below untested
    if user.rower.rowerplan == 'basic' and team.manager.rower.rowerplan != 'coach':
        return is_plantrial(user) or is_protrial(user)
    # team is public
    if team.private == 'open':
        return True
    # team is private - below untested
    return is_team_member(user,team) | is_team_manager(user,team)

@rules.predicate
def can_change_team(user,team):
    return is_team_manager(user,team)

@rules.predicate
def can_delete_team(user,team):
    return is_team_manager(user,team)

@rules.predicate
def can_join_team(user,team):
    return is_paid_coach(team.manager) or ispromember(user)

# For Team functionality
rules.add_perm('teams.view_team',can_view_team)
rules.add_perm('teams.add_team',user_is_not_basic)
rules.add_perm('teams.change_team',can_change_team)
rules.add_perm('teams.delete_team',can_delete_team)

# RACING permissions

"""
    - VirtualRace
        - rules to add, view, delete, change
    - GeoCourse
        - rules to add, view, delete, change
    - RaceLogo
"""

@rules.predicate
def can_change_course(user,course):
    if user.is_anonymous:
        return False

    return course.manager == user.rower

# untested
@rules.predicate
def can_delete_course(user,course):
    if user.is_anonymous:
        return False

    return course.manager == user.rower

@rules.predicate
def can_delete_logo(user,logo):
    if user.is_anonymous:
        return False

    return logo.user == user

@rules.predicate
def can_change_race(user,race):
    if user.is_anonymous:
        return False

    return race.manager == user

# everybody can view or add a course
rules.add_perm('course.change_course',can_change_course)
rules.add_perm('course.delete_course',can_delete_course)

rules.add_perm('racelogo.delete_logo',can_delete_logo)

# everybody can view a race
rules.add_perm('virtualevent.change_race',can_change_race)
# can races be deleted?


# ANALYSIS permissions

"""
    - Conditions
    - Alerts
        - rules to add, view, delete, change
    - cpdata
"""
