import logging
from os import access, R_OK
from os.path import exists as path_exists, getsize

import jwt
import requests
from decouple import config
from fastapi import HTTPException, status

import schemas
from chalicelib.core import projects
from chalicelib.utils.TimeUTC import TimeUTC

logger = logging.getLogger(__name__)

ASSIST_KEY = config("ASSIST_KEY")
ASSIST_URL = config("ASSIST_URL") % ASSIST_KEY


def get_live_sessions_ws_user_id(project_id, user_id):
    data = {
        "filter": {"userId": user_id} if user_id else {}
    }
    return __get_live_sessions_ws(project_id=project_id, data=data)


def get_live_sessions_ws_test_id(project_id, test_id):
    data = {
        "filter": {
            'uxtId': test_id,
            'operator': 'is'
        }
    }
    return __get_live_sessions_ws(project_id=project_id, data=data)


def get_live_sessions_ws(project_id, body: schemas.LiveSessionsSearchPayloadSchema):
    data = {
        "filter": {},
        "pagination": {"limit": body.limit, "page": body.page},
        "sort": {"key": body.sort, "order": body.order}
    }
    for f in body.filters:
        if f.type == schemas.LiveFilterType.METADATA:
            data["filter"][f.source] = {"values": f.value, "operator": f.operator}

        else:
            data["filter"][f.type] = {"values": f.value, "operator": f.operator}
    return __get_live_sessions_ws(project_id=project_id, data=data)


def __get_live_sessions_ws(project_id, data):
    project_key = projects.get_project_key(project_id)
    try:
        results = requests.post(ASSIST_URL + config("assist") + f"/{project_key}",
                                json=data, timeout=config("assistTimeout", cast=int, default=5))
        if results.status_code != 200:
            logger.error(f"!! issue with the peer-server code:{results.status_code} for __get_live_sessions_ws")
            logger.error(results.text)
            return {"total": 0, "sessions": []}
        live_peers = results.json().get("data", [])
    except requests.exceptions.Timeout:
        logger.error("!! Timeout getting Assist response")
        live_peers = {"total": 0, "sessions": []}
    except Exception as e:
        logger.error("!! Issue getting Live-Assist response")
        logger.exception(e)
        logger.error("expected JSON, received:")
        try:
            logger.error(results.text)
        except:
            logger.error("couldn't get response")
        live_peers = {"total": 0, "sessions": []}
    _live_peers = live_peers
    if "sessions" in live_peers:
        _live_peers = live_peers["sessions"]
    for s in _live_peers:
        s["live"] = True
        s["projectId"] = project_id
        if "projectID" in s:
            s.pop("projectID")
    return live_peers


def __get_agent_token(project_id, project_key, session_id):
    iat = TimeUTC.now()
    return jwt.encode(
        payload={
            "projectKey": project_key,
            "projectId": project_id,
            "sessionId": session_id,
            "iat": iat // 1000,
            "exp": iat // 1000 + config("ASSIST_JWT_EXPIRATION", cast=int) + TimeUTC.get_utc_offset() // 1000,
            "iss": config("JWT_ISSUER"),
            "aud": f"openreplay:agent"
        },
        key=config("ASSIST_JWT_SECRET"),
        algorithm=config("JWT_ALGORITHM")
    )


def get_live_session_by_id(project_id, session_id):
    project_key = projects.get_project_key(project_id)
    try:
        results = requests.get(ASSIST_URL + config("assist") + f"/{project_key}/{session_id}",
                               timeout=config("assistTimeout", cast=int, default=5))
        if results.status_code != 200:
            logger.error(f"!! issue with the peer-server code:{results.status_code} for get_live_session_by_id")
            logger.error(results.text)
            return None
        results = results.json().get("data")
        if results is None:
            return None
        results["live"] = True
        results["agentToken"] = __get_agent_token(project_id=project_id, project_key=project_key, session_id=session_id)
    except requests.exceptions.Timeout:
        logger.error("!! Timeout getting Assist response")
        return None
    except Exception as e:
        logger.error("!! Issue getting Assist response")
        logger.exception(e)
        logger.error("expected JSON, received:")
        try:
            logger.error(results.text)
        except:
            logger.error("couldn't get response")
        return None
    return results


def is_live(project_id, session_id, project_key=None):
    if project_key is None:
        project_key = projects.get_project_key(project_id)
    try:
        results = requests.get(ASSIST_URL + config("assistList") + f"/{project_key}/{session_id}",
                               timeout=config("assistTimeout", cast=int, default=5))
        if results.status_code != 200:
            logger.error(f"!! issue with the peer-server code:{results.status_code} for is_live")
            logger.error(results.text)
            return False
        results = results.json().get("data")
    except requests.exceptions.Timeout:
        logger.error("!! Timeout getting Assist response")
        return False
    except Exception as e:
        logger.error("!! Issue getting Assist response")
        logger.exception(e)
        logger.error("expected JSON, received:")
        try:
            logger.error(results.text)
        except:
            logger.error("couldn't get response")
        return False
    return str(session_id) == results


def autocomplete(project_id, q: str, key: str = None):
    project_key = projects.get_project_key(project_id)
    params = {"q": q}
    if key:
        params["key"] = key
    try:
        results = requests.get(
            ASSIST_URL + config("assistList") + f"/{project_key}/autocomplete",
            params=params, timeout=config("assistTimeout", cast=int, default=5))
        if results.status_code != 200:
            logger.error(f"!! issue with the peer-server code:{results.status_code} for autocomplete")
            logger.error(results.text)
            return {"errors": [f"Something went wrong wile calling assist:{results.text}"]}
        results = results.json().get("data", [])
    except requests.exceptions.Timeout:
        logger.error("!! Timeout getting Assist response")
        return {"errors": ["Assist request timeout"]}
    except Exception as e:
        logger.error("!! Issue getting Assist response")
        logger.exception(e)
        logger.error("expected JSON, received:")
        try:
            logger.error(results.text)
        except:
            logger.error("couldn't get response")
        return {"errors": ["Something went wrong wile calling assist"]}
    for r in results:
        r["type"] = __change_keys(r["type"])
    return {"data": results}


def __get_efs_path():
    efs_path = config("FS_DIR")
    if not path_exists(efs_path):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"EFS not found in path: {efs_path}")

    if not access(efs_path, R_OK):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                            detail=f"EFS found under: {efs_path}; but it is not readable, please check permissions")
    return efs_path


def __get_mob_path(project_id, session_id):
    params = {"projectId": project_id, "sessionId": session_id}
    return config("EFS_SESSION_MOB_PATTERN", default="%(sessionId)s") % params


def get_raw_mob_by_id(project_id, session_id):
    efs_path = __get_efs_path()
    path_to_file = efs_path + "/" + __get_mob_path(project_id=project_id, session_id=session_id)
    if path_exists(path_to_file):
        if not access(path_to_file, R_OK):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                                detail=f"Replay file found under: {efs_path};" +
                                       " but it is not readable, please check permissions")
        # getsize return size in bytes, UNPROCESSED_MAX_SIZE is in Kb
        if (getsize(path_to_file) / 1000) >= config("UNPROCESSED_MAX_SIZE", cast=int, default=200 * 1000):
            raise HTTPException(status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail="Replay file too large")
        return path_to_file

    return None


def __get_devtools_path(project_id, session_id):
    params = {"projectId": project_id, "sessionId": session_id}
    return config("EFS_DEVTOOLS_MOB_PATTERN", default="%(sessionId)s") % params


def get_raw_devtools_by_id(project_id, session_id):
    efs_path = __get_efs_path()
    path_to_file = efs_path + "/" + __get_devtools_path(project_id=project_id, session_id=session_id)
    if path_exists(path_to_file):
        if not access(path_to_file, R_OK):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
                                detail=f"Devtools file found under: {efs_path};"
                                       " but it is not readable, please check permissions")

        return path_to_file

    return None


def session_exists(project_id, session_id):
    project_key = projects.get_project_key(project_id)
    try:
        results = requests.get(ASSIST_URL + config("assist") + f"/{project_key}/{session_id}",
                               timeout=config("assistTimeout", cast=int, default=5))
        if results.status_code != 200:
            logger.error(f"!! issue with the peer-server code:{results.status_code} for session_exists")
            logger.error(results.text)
            return None
        results = results.json().get("data")
        if results is None:
            return False
        return True
    except requests.exceptions.Timeout:
        logger.error("!! Timeout getting Assist response")
        return False
    except Exception as e:
        logger.error("!! Issue getting Assist response")
        logger.exception(e)
        logger.error("expected JSON, received:")
        try:
            logger.error(results.text)
        except:
            logger.error("couldn't get response")
        return False


def __change_keys(key):
    return {
        "PAGETITLE": schemas.LiveFilterType.PAGE_TITLE.value,
        "ACTIVE": "active",
        "LIVE": "live",
        "SESSIONID": schemas.LiveFilterType.SESSION_ID.value,
        "METADATA": schemas.LiveFilterType.METADATA.value,
        "USERID": schemas.LiveFilterType.USER_ID.value,
        "USERUUID": schemas.LiveFilterType.USER_UUID.value,
        "PROJECTKEY": "projectKey",
        "REVID": schemas.LiveFilterType.REV_ID.value,
        "TIMESTAMP": "timestamp",
        "TRACKERVERSION": schemas.LiveFilterType.TRACKER_VERSION.value,
        "ISSNIPPET": "isSnippet",
        "USEROS": schemas.LiveFilterType.USER_OS.value,
        "USERBROWSER": schemas.LiveFilterType.USER_BROWSER.value,
        "USERBROWSERVERSION": schemas.LiveFilterType.USER_BROWSER_VERSION.value,
        "USERDEVICE": schemas.LiveFilterType.USER_DEVICE.value,
        "USERDEVICETYPE": schemas.LiveFilterType.USER_DEVICE_TYPE.value,
        "USERCOUNTRY": schemas.LiveFilterType.USER_COUNTRY.value,
        "PROJECTID": "projectId"
    }.get(key.upper(), key)
