from datetime import datetime, timedelta

from pyramid.httpexceptions import HTTPFound
from pyramid.response import Response

from pyramid.security import remember, forget
from pyramid.view import view_config
from sqlalchemy import and_, or_
import transaction

from brewman import groupfinder
from brewman.models import DBSession
from brewman.models.auth import User, Client, LoginHistory
from brewman.models.validation_exception import TryCatchFunction


@view_config(route_name='logout')
def logout(request):
    request.session.invalidate()
    headers = forget(request)
    return HTTPFound(location=request.route_url('home'), headers=headers)


@view_config(request_method='POST', route_name='api_login', renderer='json')
@TryCatchFunction
def login(request):
    username = request.json_body.get('username', None)
    password = request.json_body.get('password', None)
    found, user = User.auth(username, password)

    client = Client.by_code(request.cookies.get('ClientID', None))
    otp = request.json_body.get('otp', None)
    client_name = request.json_body.get('ClientName', None)
    allowed, client, response = check_client(client, otp, client_name, user if found else None)

    if found and allowed:
        headers = remember(request, str(user.id))
        request.response.headers = headers
        request.response.set_cookie('ClientID', value=str(client.code), max_age=365 * 24 * 60 * 60)
        record_login(user.id, client)
        transaction.commit()
        return request.response
    elif not found:
        response = Response("Login failed")
        response.status_int = 403
        transaction.commit()
        return response
    else:
        transaction.commit()
        return response


def check_client(client, otp, client_name, user):
    outside_login_allowed = False if user is None else True if 'Clients' in groupfinder(user.id, None) else False

    if len(Client.enabled_list()) == 0 and outside_login_allowed:
        client = Client.create()
        client.otp = None
        client.name = 'Created on login by ' + user.name
        client.enabled = True
        return True, client, None

    if client is None:
        if outside_login_allowed:
            client = Client.create()
            return True, client, None
        else:
            client = Client.create()
            response = Response("Unknown Client")
            response.status_int = 403
            response.set_cookie('ClientID', value=str(client.code), max_age=10 * 365 * 24 * 60 * 60)
            return False, None, response

    if client.enabled or outside_login_allowed:
        return True, client, None

    if client.otp is None:
        response = Response("Client is Forbidden")
        response.status_int = 403
        return False, None, response

    if otp is None:
        response = Response("OTP not supplied")
        response.status_int = 403
        return False, None, response
    elif client.otp != int(otp):
        response = Response("OTP is wrong")
        response.status_int = 403
        return False, None, response
    else:
        client.otp = None
        client.enabled = True
        client.name = client_name
        return True, client, None


def record_login(user_id, client):
    history = LoginHistory(user_id)
    history.client = client
    DBSession.add(history)

    recent_logins = DBSession.query(LoginHistory.client_id.distinct()) \
        .filter(LoginHistory.date > datetime.utcnow() - timedelta(days=90)).subquery()

    deletable_clients = DBSession.query(Client.id) \
        .filter(Client.creation_date < datetime.utcnow() - timedelta(days=3)) \
        .filter(Client.enabled == False).subquery()

    LoginHistory.__table__.delete(
        and_(
            ~LoginHistory.client_id.in_(recent_logins),
            LoginHistory.client_id.in_(deletable_clients)
        )
    ).execute()

    Client.__table__.delete(
        and_(
            Client.creation_date < datetime.utcnow() - timedelta(days=3),
            Client.enabled == False,
            ~Client.id.in_(recent_logins)
        )
    ).execute()

