def create_user(user_name, password_first, email, dob: datetime.date, user_type: str):
    session = db.create_session()
    new_user: User = User()
    new_user.user_name = user_name
    new_user.password = hash_text(password_first)  # Password is encrypted
    new_user.email = email
    new_user.dob = dob
    user_type = user_type.lower()
    if user_type == 'child':  # Sets appropriate user types to match the table
        new_user.user_type = False
    else:
        new_user.user_type = True
    session.add(new_user)
    session.commit()

    # Returns ID and adds child or parent data
    added_user: User = session.query(User).filter(User.email == email).first()
    if user_type == 'child':
        new_child: Child_Data = Child_Data()
        added_user.child_data.append(new_child)
    else:
        new_parent: Parent_Data = Parent_Data()
        added_user.parent_data.append(new_parent)
    session.commit()
    return added_user
def create_connection(child: User, parent: User):
    session = db.create_session()
    new_connection: User_Connection = User_Connection()
    new_connection.child_id = child.id
    new_connection.parent_id = parent.id
    session.add(new_connection)
    session.commit()
    session.close()
def check_if_email_exists(email: str) -> bool:
    session = db.create_session()
    returned_user = session.query(User).filter(User.email == email).first()  # User of that email is searched
    session.close()
    if returned_user is None:
        return False
    else:
        return True
def return_pending_connections(child_id):
    session = db.create_session()
    returned_pending_connections = session.query(User_Connection).filter(User_Connection.child_id == child_id).all()
    true_connections = []
    for connection in returned_pending_connections:
        if connection.pending:
            true_connections.append(connection)
    session.close()
    return true_connections
def create_report(child_id, title, description):
    session = db.create_session()
    new_report = Report()
    new_report.child_id = child_id
    new_report.title = title
    new_report.description = description
    session.add(new_report)
    session.commit()
    session.close()
def check_connection_already_pending_or_exists(child: User, parent: User):
    session = db.create_session()
    existing_connection: User_Connection = session.query(User_Connection).filter(User_Connection.child_id == child.id
                                                                                 and User_Connection.parent_id == parent.id).first()
    if not existing_connection:
        session.close()
        return False

    session.close()
    return True
def delete_connection(child: User, parent: User):
    session = db.create_session()
    connection_to_delete: User_Connection = session.query(User_Connection).filter(
        User_Connection.child_id == child.id and User_Connection.parent_id == parent.id).first()
    if not connection_to_delete:
        return False

    session.delete(connection_to_delete)
    session.commit()
    session.close()
    return True
def update_connection(child: User, parent: User):
    session = db.create_session()
    existing_connection: User_Connection = session.query(User_Connection).filter(User_Connection.child_id == child.id
                                                                                 and User_Connection.parent_id == parent.id).first()
    if not existing_connection:
        session.close()
        return False

    existing_connection.pending = False  # Pending set to false, meaning connection is now valid
    session.commit()
    session.close()
    return True
def check_user_is_in_database_and_password_valid(email: str, password: str):
    session = db.create_session()
    returned_user = session.query(User).filter(User.email == email).first()

    if not returned_user:
        return False

    if not verify_hash(returned_user.password, password):  # Password does not match encryped password
        return False

    session.close()
    return returned_user
def return_connected_children(user_id):
    session = db.create_session()
    returned_parent: Parent_Data = session.query(Parent_Data).filter(Parent_Data.user_id == user_id).first()
    if not returned_parent:
        return None
    children = returned_parent.child_connections
    proper_children = []
    for child in children:
        connection: User_Connection = session.query(User_Connection).filter(User_Connection.parent_id == user_id and
                                                                            User_Connection.child_id == child.user_id).first()
        if not connection.pending:
            proper_children.append(child.user)

    return proper_children
def return_connected_children_user(user_id):
    session = db.create_session()
    returned_parent: Parent_Data = session.query(Parent_Data).filter(Parent_Data.user_id == user_id).first()
    if not returned_parent:
        return None

    children = returned_parent.child_connections
    proper_children = []
    for child in children:  # Traverses a parents children
        user_child = CombinedUserAndChild()
        connection: User_Connection = session.query(User_Connection).filter(User_Connection.parent_id == user_id and
                                                                            User_Connection.child_id == child.user_id).first()
        if not connection.pending:  # Returns connection between child and user and checks if the connection is pending
            user_child.location = child.current_location  # If the connection is not pending then this is a proper child
            user_child.last_time = child.last_location_update
            user_child.user_id = child.user_id
            user_child.user_name = child.user.user_name
            if child.reports:
                user_child.last_report_time = child.reports[0].date_created  # Most recent report is returned
            proper_children.append(user_child)

    return proper_children
def update_location(user_ip, child_id):
    # user_ip = "81.104.157.39"
    geo = pygeoip.GeoIP("static/GeoLiteCity.dat", pygeoip.MEMORY_CACHE)
    user_location_data = geo.record_by_addr(user_ip)  # User location is returned based on their IP

    session = db.create_session()
    child: Child_Data = session.query(Child_Data).filter(Child_Data.user_id == child_id).first()
    if not child:  # User must be child and not parent
        return False

    try:
        metro_code = user_location_data['metro_code']  # Metro code is returned
        lat = user_location_data['latitude']  # Latitude coordinates are returned
        long = user_location_data['longitude']  # Longitude coordinates are returned
        location_to_add = f"{metro_code}:{lat}:{long}"  # Locations string is derived
    except TypeError:
        child.current_location = "None"  # If the data cannot be returned then the location is returned as None
        return False

    child.current_location = location_to_add  # Location is added
    child.last_location_update = datetime.datetime.now()  # Last location update is set
    session.commit()
    session.close()
def return_connected_parents(user_id):
    session = db.create_session()
    returned_child: Child_Data = session.query(Child_Data).filter(Child_Data.user_id == user_id).first()
    if not returned_child:
        return None
    return returned_child.parent_connections
def return_user(user_id):
    session = db.create_session()
    returned_user: User = session.query(User).filter(User.id == user_id).first()
    return returned_user