import json

from flask import (
    abort,
    current_app,
    flash,
    redirect,
    render_template,
    request,
    session,
    url_for,
)
from flask_login import current_user
from notifications_utils.url_safe_token import check_token

from app import user_api_client
from app.event_handlers import (
    create_email_change_event,
    create_mobile_number_change_event,
)
from app.main import main
from app.main.forms import (
    ChangeEmailForm,
    ChangeMobileNumberForm,
    ChangeNameForm,
    ChangePasswordForm,
    ChangePreferredTimezoneForm,
    ConfirmPasswordForm,
    ServiceOnOffSettingForm,
    TwoFactorForm,
)
from app.models.user import User
from app.utils.user import user_is_gov_user, user_is_logged_in

NEW_EMAIL = "new-email"
NEW_MOBILE = "new-mob"
NEW_MOBILE_PASSWORD_CONFIRMED = (
    "new-mob-password-confirmed"  # nosec B105 - this is not a password
)


@main.route("/user-profile")
@user_is_logged_in
def user_profile():
    return render_template(
        "views/user-profile.html",
        can_see_edit=current_user.is_gov_user,
    )


@main.route("/user-profile/name", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_name():
    form = ChangeNameForm(new_name=current_user.name)

    if form.validate_on_submit():
        current_user.update(name=form.new_name.data)
        return redirect(url_for(".user_profile"))

    return render_template(
        "views/user-profile/change.html", thing="name", form_field=form.new_name
    )


@main.route("/user-profile/preferred_timezone", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_preferred_timezone():
    form = ChangePreferredTimezoneForm(new_name=current_user.preferred_timezone)

    if form.validate_on_submit():
        current_user.update(preferred_timezone=form.new_preferred_timezone.data)
        current_user.preferred_timezone = form.new_preferred_timezone.data
        return redirect(url_for(".user_profile"))
    return render_template(
        "views/user-profile/change.html",
        thing="preferred timezone",
        form_field=form.new_preferred_timezone,
    )


@main.route("/user-profile/email", methods=["GET", "POST"])
@user_is_logged_in
@user_is_gov_user
def user_profile_email():
    form = ChangeEmailForm(
        User.already_registered, email_address=current_user.email_address
    )

    if form.validate_on_submit():
        session[NEW_EMAIL] = form.email_address.data
        return redirect(url_for(".user_profile_email_authenticate"))
    return render_template(
        "views/user-profile/change.html",
        thing="email address",
        form_field=form.email_address,
    )


@main.route("/user-profile/email/authenticate", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_email_authenticate():
    # Validate password for form
    def _check_password(pwd):
        return user_api_client.verify_password(current_user.id, pwd)

    form = ConfirmPasswordForm(_check_password)

    if NEW_EMAIL not in session:
        return redirect("main.user_profile_email")

    if form.validate_on_submit():
        user_api_client.send_change_email_verification(
            current_user.id, session[NEW_EMAIL]
        )
        create_email_change_event(
            user_id=current_user.id,
            updated_by_id=current_user.id,
            original_email_address=current_user.email_address,
            new_email_address=session[NEW_EMAIL],
        )
        return render_template(
            "views/change-email-continue.html", new_email=session[NEW_EMAIL]
        )

    return render_template(
        "views/user-profile/authenticate.html",
        thing="email address",
        form=form,
        back_link=url_for(".user_profile_email"),
    )


@main.route("/user-profile/email/confirm/<token>", methods=["GET"])
@user_is_logged_in
def user_profile_email_confirm(token):
    token_data = check_token(
        token,
        current_app.config["SECRET_KEY"],
        current_app.config["DANGEROUS_SALT"],
        current_app.config["EMAIL_EXPIRY_SECONDS"],
    )
    token_data = json.loads(token_data)
    user = User.from_id(token_data["user_id"])
    user.update(email_address=token_data["email"])
    session.pop(NEW_EMAIL, None)

    return redirect(url_for(".user_profile"))


@main.route("/user-profile/mobile-number", methods=["GET", "POST"])
@main.route(
    "/user-profile/mobile-number/delete",
    methods=["GET"],
    endpoint="user_profile_confirm_delete_mobile_number",
)
@user_is_logged_in
def user_profile_mobile_number():
    user = User.from_id(current_user.id)
    form = ChangeMobileNumberForm(mobile_number=current_user.mobile_number)

    if form.validate_on_submit():
        session[NEW_MOBILE] = form.mobile_number.data
        return redirect(url_for(".user_profile_mobile_number_authenticate"))

    if request.endpoint == "main.user_profile_confirm_delete_mobile_number":
        flash(
            "Are you sure you want to delete your mobile number from Notify?", "delete"
        )

    return render_template(
        "views/user-profile/change.html",
        thing="mobile number",
        form_field=form.mobile_number,
        user_auth=user.auth_type,
    )


@main.route("/user-profile/mobile-number/delete", methods=["POST"])
@user_is_logged_in
def user_profile_mobile_number_delete():
    if current_user.auth_type != "email_auth":
        abort(403)

    current_user.update(mobile_number=None)

    return redirect(url_for(".user_profile"))


@main.route("/user-profile/mobile-number/authenticate", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_mobile_number_authenticate():
    # Validate password for form
    def _check_password(pwd):
        return user_api_client.verify_password(current_user.id, pwd)

    form = ConfirmPasswordForm(_check_password)

    if NEW_MOBILE not in session:
        return redirect(url_for(".user_profile_mobile_number"))

    if form.validate_on_submit():
        session[NEW_MOBILE_PASSWORD_CONFIRMED] = True
        current_user.send_verify_code(to=session[NEW_MOBILE])
        create_mobile_number_change_event(
            user_id=current_user.id,
            updated_by_id=current_user.id,
            original_mobile_number=current_user.mobile_number,
            new_mobile_number=session[NEW_MOBILE],
        )
        return redirect(url_for(".user_profile_mobile_number_confirm"))

    return render_template(
        "views/user-profile/authenticate.html",
        thing="mobile number",
        form=form,
        back_link=url_for(".user_profile_mobile_number_confirm"),
    )


@main.route("/user-profile/mobile-number/confirm", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_mobile_number_confirm():
    # Validate verify code for form
    def _check_code(cde):
        return user_api_client.check_verify_code(current_user.id, cde, "sms")

    if NEW_MOBILE_PASSWORD_CONFIRMED not in session:
        return redirect(url_for(".user_profile_mobile_number"))

    form = TwoFactorForm(_check_code)

    if form.validate_on_submit():
        current_user.refresh_session_id()
        mobile_number = session[NEW_MOBILE]
        del session[NEW_MOBILE]
        del session[NEW_MOBILE_PASSWORD_CONFIRMED]
        current_user.update(mobile_number=mobile_number)
        return redirect(url_for(".user_profile"))

    return render_template(
        "views/user-profile/confirm.html",
        form_field=form.sms_code,
        thing="mobile number",
    )


@main.route("/user-profile/password", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_password():
    # Validate password for form
    def _check_password(pwd):
        return user_api_client.verify_password(current_user.id, pwd)

    form = ChangePasswordForm(_check_password)

    if form.validate_on_submit():
        user_api_client.update_password(
            current_user.id, password=form.new_password.data
        )
        return redirect(url_for(".user_profile"))

    return render_template("views/user-profile/change-password.html", form=form)


@main.route("/user-profile/disable-platform-admin-view", methods=["GET", "POST"])
@user_is_logged_in
def user_profile_disable_platform_admin_view():
    if not current_user.platform_admin and not session.get(
        "disable_platform_admin_view"
    ):
        abort(403)

    form = ServiceOnOffSettingForm(
        name="Use platform admin view",
        enabled=not session.get("disable_platform_admin_view"),
        truthy="Yes",
        falsey="No",
    )

    form.enabled.param_extensions = {
        "hint": {"text": "Signing in again clears this setting"}
    }

    if form.validate_on_submit():
        session["disable_platform_admin_view"] = not form.enabled.data
        return redirect(url_for(".user_profile"))

    return render_template(
        "views/user-profile/disable-platform-admin-view.html", form=form
    )
