import re
from json import JSONDecodeError
from typing import TypedDict

import orjson
from flask import Blueprint, request, current_app, make_response, jsonify, g, session

from src.lib.account import attempt_login, create_account
from src.lib.api import create_client_error_response
from src.lib.security import is_password_compromised
from src.types.account import Account

from . import v1api_bp

# it is in a separate file because
# all account routes require auth

authentication_bp = Blueprint("authentication", __name__)


class TDRegistrationBody(TypedDict):
    location: str
    username: str
    password: str
    confirm_password: str
    favorites_json: str


USERNAME_REGEX = re.compile(r"^[a-z0-9_@+.\-]{3,15}$")


@authentication_bp.post("/authentication/register")
def post_register():
    body: TDRegistrationBody = request.get_json()
    username = body.get("username", "").replace("\x00", "").strip()
    password = body.get("password", "").strip()
    confirm_password = body.get("confirm_password", "").strip()
    favorites_json = body.get("favorites", "[]")

    favorites = []
    if favorites_json != "":
        try:
            favorites = orjson.loads(favorites_json)
        except JSONDecodeError:
            pass

    if username == "":
        return create_client_error_response("Username cannot be empty")

    if not USERNAME_REGEX.match(username):
        return create_client_error_response("Invalid username")

    if password == "":
        return create_client_error_response("Password cannot be empty")

    if len(password) < 5:
        return create_client_error_response("Password must have at least 5 characters.")

    if password != confirm_password:
        return create_client_error_response("Passwords do not match")

    if current_app.config.get("ENABLE_PASSWORD_VALIDATOR") and is_password_compromised(password):
        return create_client_error_response(
            "We've detected that password was compromised in a data breach on another site. Please choose a different password."
        )

    success = create_account(username, password, favorites)

    if not success:
        return create_client_error_response("Username already taken")

    response = make_response(jsonify(True), 200)

    return response


class TDLoginBody(TypedDict):
    username: str
    password: str


@authentication_bp.post("/authentication/login")
def post_login():
    body: TDLoginBody = request.get_json()

    account: Account | None = g.get("account")

    if account:
        return create_client_error_response("Already logged in", 409)

    username = body.get("username", "").replace("\x00", "")
    password = body.get("password", "")

    if not username:
        return create_client_error_response("Username is required.")

    if not password:
        return create_client_error_response("Password is required.")

    (account, error_message) = attempt_login(username, password)

    if error_message:
        return create_client_error_response(error_message)

    if not account:
        return create_client_error_response("Account doesn't exist")

    response = make_response(jsonify(account), 200)

    return response


@authentication_bp.post("/authentication/logout")
def logout():
    if "account_id" in session:
        session.pop("account_id")
    response = make_response(jsonify(True), 200)

    return response


v1api_bp.register_blueprint(authentication_bp)
