import secrets
import string
import uuid
from datetime import datetime, timedelta

from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required, login_user, logout_user
from flask_mailman import EmailMessage
from sqlalchemy import or_, select

from web_application import bcrypt, db
from web_application.constants import roles
from web_application.models import PasswordResetToken, Role, User
from web_application.user.forms import (
    ChangeUserNameDataForm,
    ChangeUserPassword,
    ForgotPasswordForm,
    LoginForm,
    RegisterForm,
    ResetPasswordForm,
)

user = Blueprint(
    "user",
    __name__,
    template_folder="templates",
    static_folder="static",
    url_prefix="/user",
)


def create_password_reset_token():
    # Create a sequence of all uppercase letters and digits
    characters = string.ascii_letters + string.digits + "-_!@#$%^&*()"

    # Generate a 64-character random string
    random_string = "".join(secrets.choice(characters) for _ in range(64))

    return random_string


@user.route("/register", methods=["GET", "POST"])
def register():
    if current_user.is_authenticated:
        return redirect(url_for("main.home"))

    form = RegisterForm()

    if request.method == "POST":
        if form.validate_on_submit():
            user = User.query.filter_by(email=form.email.data).first()
            if user:
                flash("Bu e-posta adresi zaten kayıtlı.", "danger")
                return redirect(url_for("user.register"))

            new_user = User(
                uuid=str(uuid.uuid4()),
                full_name=form.full_name.data,
                email=form.email.data,
                phone=form.phone.data,
                password=bcrypt.generate_password_hash(form.password.data),
                role=roles["Misafir"]["id"],  # Default role for regular users
            )
            db.session.add(new_user)
            try:
                db.session.commit()
                flash("Kayıt işlemi başarılı. Lütfen giriş yapın.", "success")
                return redirect(url_for("user.login"))
            except Exception as e:
                db.session.rollback()
                flash(f"Kayıt sırasında bir hata oluştu: {e}", "danger")

    data = {"title": "Register", "form": form}
    return render_template("user/register.html", **data)


@user.route("/create-super-admin")
def create_super_admin():
    stmt = select(Role)
    user_roles = db.session.execute(stmt).scalars().all()
    if len(user_roles) == 0:
        for _, v in roles.items():
            role_name = v["title"]
            role = Role(name=role_name)
            db.session.add(role)

    users = User.query.all()
    if len(users) == 0:
        new_user = User(
            uuid=str(uuid.uuid4()),
            full_name="System Admin",
            email="testadmin@test.com",
            password=bcrypt.generate_password_hash("Tip-Asu.2520"),
            role=roles["Admin"]["id"],
        )
        db.session.add(new_user)

        try:
            db.session.commit()
            flash(
                "Bir yönetici hesabı oluşturdunuz. Lütfen, giriş yapın ve parolanızı değiştirin.",
                "success",
            )
            return redirect(url_for("main.home"))
        except Exception as e:
            db.session.rollback()
            flash(
                f"Bir hata oluştu. {e}",
                "danger",
            )
            return redirect(url_for("main.home"))
    else:
        flash("Admin User oluşturken hatalı bir işlem gerçekleştirdiniz. ", "danger")
        return redirect(url_for("main.home"))


@user.route("/login", methods=["GET", "POST"])
def login():
    if current_user.is_authenticated:
        return redirect(url_for("main.home"))
    form = LoginForm()
    if request.method == "POST":
        if form.validate_on_submit():
            user = User.query.filter(
                or_(
                    User.email == form.email.data,
                    User.username == form.email.data,
                )
            ).first()
            if user is None or not bcrypt.check_password_hash(
                user.password, form.password.data
            ):
                flash("Hatalı telefon numarası ya da parola girdiniz.", "danger")
                return render_template("user/login.html", title="Login", form=form)

            login_user(user, remember=form.remember.data)

            ## varsa reset token'i sil
            if user.reset_token:
                db.session.delete(user.reset_token)
                db.session.commit()
            flash(
                "Başarılı bir şekilde giriş yaptınız.",
                "success",
            )
            next_page = request.args.get("next")
            return redirect(next_page) if next_page else redirect(url_for("main.home"))
    return render_template("user/login.html", title="Login", form=form)


@user.route("/forgot-password", methods=["GET", "POST"])
def forgot_password():
    if current_user.is_authenticated:
        return redirect(url_for("main.home"))
    form = ForgotPasswordForm()
    if request.method == "POST":
        if form.validate_on_submit():
            user = User.query.filter_by(email=form.email.data).first()
            if user:
                password_reset_token = create_password_reset_token()
                new_token = PasswordResetToken(
                    user_id=user.id,
                    token=password_reset_token,
                )
                db.session.add(new_token)
                db.session.commit()
                # Here you would typically send an email with a reset link
                msg = EmailMessage(
                    subject="Parola Sıfırlama Talebinizi Aldık",
                    body=f"""Parola sıfırlama talebiniz alınmıştır. Lütfen aşağıdaki linke tıklayarak yeni parolanızı oluşturun.
                    <br><br>Link: <a href="{url_for("user.reset_password", token=password_reset_token, _external=True)}">Parola Sıfırlama Linki</a>
                    <br><br>Bu işlemi yapmadıysanız, bu e-postayı dikkate almayabilirsiniz.""",
                    from_email="donotreply@scholars.tr",
                    to=[form.email.data],
                )
                msg.content_subtype = "html"
                res = msg.send()
                if res:
                    flash(
                        "Parola sıfırlama talebiniz alınmıştır. E-posta adresinize bir link gönderilmiştir.",
                        "success",
                    )
                    return redirect(url_for("user.password_reset_mail_sent"))
                else:
                    flash(
                        "Parola sıfırlama talebiniz alınmıştır. Ancak e-posta gönderilememiştir.",
                        "warning",
                    )
                    return redirect(url_for("user.forgot_password"))
            else:
                flash("Bu e-posta adresi kayıtlı değil.", "danger")
                return redirect(url_for("user.forgot_password"))
    data = {
        "title": "Parolamı Unuttum",
        "form": form,
    }
    return render_template("user/forgot_password.html", **data)


@user.route("/password-reset-mail-sent")
def password_reset_mail_sent():
    if current_user.is_authenticated:
        return redirect(url_for("main.home"))

    data = {
        "title": "Parola Sıfırlama Maili Gönderildi",
    }

    return render_template("user/password_reset_mail_sent.html", **data)


@user.route("/reset-password/<token>", methods=["GET", "POST"])
def reset_password(token):
    if current_user.is_authenticated:
        return redirect(url_for("main.home"))

    existing_token = PasswordResetToken.query.filter_by(token=token).first()

    if not existing_token:
        flash("Geçersiz veya süresi dolmuş parola sıfırlama talebi.", "danger")
        return redirect(url_for("user.forgot_password"))

    if datetime.now() > existing_token.created_at + timedelta(minutes=30):
        flash(
            "Parola sıfırlama talebinizin süresi dolmuştur. Lütfen tekrar deneyin.",
            "danger",
        )
        db.session.delete(existing_token)
        db.session.commit()
        return redirect(url_for("user.forgot_password"))

    form = ResetPasswordForm()

    if request.method == "POST":
        if form.validate_on_submit():
            new_password = form.password.data

            user = User.query.get(existing_token.user_id)
            user.password = bcrypt.generate_password_hash(new_password)
            db.session.commit()

            # Delete the token after successful password reset
            db.session.delete(existing_token)
            db.session.commit()

            flash("Parolanız başarıyla değiştirildi. Lütfen giriş yapın.", "success")
            return redirect(url_for("user.login"))

    data = {
        "title": "Parola Sıfırlama",
        "form": form,
    }
    return render_template("user/reset_password.html", **data)


@user.route("/logout")
def logout():
    logout_user()
    return redirect(url_for("user.login"))


# Profile routes such as profile, change change password, etc.
@user.route("/profile")
@login_required
def profile():
    data = {
        "title": "profil",
        "user": current_user,
        "roles": roles,
    }
    return render_template("user/profile.html", **data)


@user.route("/change-user-name", methods=["GET", "POST"])
@login_required
def change_username():
    if current_user.get_role() == "Admin":
        data = {"form": ChangeUserNameDataForm(), "title": "Kullanıcı Adı Değiştir"}
        data["form"].full_name.data = current_user.full_name

        if request.method == "POST":
            # get fresh data
            full_name = request.form.get("full_name")
            # update existing data
            current_user.full_name = full_name
            db.session.commit()
            flash("Kullanıcı adınız başarıyla değiştirildi.", "success")
            return redirect(url_for("user.profile"))
        return render_template("user/change_username.html", **data)
    else:
        flash("Bu sayfaya erişim izniniz yok.", "danger")
        return redirect(url_for("main.home"))


@user.route("/change-password", methods=["GET", "POST"])
@login_required
def change_password():
    data = {"form": ChangeUserPassword(), "title": "Kullanıcı Parolası Değiştir"}
    if request.method == "POST":
        new_pwd = request.form.get("password")
        confirm_pwd = request.form.get("confirm_password")

        if new_pwd != confirm_pwd or new_pwd == "":
            flash("Parolalar eşleşmiyor veya boş bırakılmış.", "danger")
            return redirect(url_for("user.change_password"))
        current_user.password = bcrypt.generate_password_hash(new_pwd)
        db.session.commit()
        flash("Parolanız başarıyla değiştirildi.", "success")
        return redirect(url_for("user.profile"))

    return render_template("user/change_password.html", **data)
