from flask import Flask, render_template, request, redirect, url_for, flash, session
from flask_sqlalchemy import SQLAlchemy
from datetime import date
import gnupg
import secrets
import os
from werkzeug.utils import secure_filename


UPLOAD_FOLDER = "static/uploads"
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

COUNTRIES = [
    "Afghanistan","Albania","Algeria","Andorra","Angola","Antigua and Barbuda","Argentina","Armenia","Australia","Austria",
    "Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bhutan",
    "Bolivia","Bosnia and Herzegovina","Botswana","Brazil","Brunei","Bulgaria","Burkina Faso","Burundi","Cabo Verde","Cambodia",
    "Cameroon","Canada","Central African Republic","Chad","Chile","China","Colombia","Comoros","Congo (Congo-Brazzaville)","Costa Rica",
    "Croatia","Cuba","Cyprus","Czechia (Czech Republic)","Democratic Republic of the Congo","Denmark","Djibouti","Dominica","Dominican Republic","Ecuador",
    "Egypt","El Salvador","Equatorial Guinea","Eritrea","Estonia","Eswatini (fmr. Swaziland)","Ethiopia","Fiji","Finland","France",
    "Gabon","Gambia","Georgia","Germany","Ghana","Greece","Grenada","Guatemala","Guinea","Guinea-Bissau",
    "Guyana","Haiti","Holy See","Honduras","Hungary","Iceland","India","Indonesia","Iran","Iraq",
    "Ireland","Israel","Italy","Jamaica","Japan","Jordan","Kazakhstan","Kenya","Kiribati","Kuwait",
    "Kyrgyzstan","Laos","Latvia","Lebanon","Lesotho","Liberia","Libya","Liechtenstein","Lithuania","Luxembourg",
    "Madagascar","Malawi","Malaysia","Maldives","Mali","Malta","Marshall Islands","Mauritania","Mauritius","Mexico",
    "Micronesia","Moldova","Monaco","Mongolia","Montenegro","Morocco","Mozambique","Myanmar (Burma)","Namibia","Nauru",
    "Nepal","Netherlands","New Zealand","Nicaragua","Niger","Nigeria","North Korea","North Macedonia","Norway","Oman",
    "Pakistan","Palau","Palestine State","Panama","Papua New Guinea","Paraguay","Peru","Philippines","Poland","Portugal",
    "Qatar","Romania","Russia","Rwanda","Saint Kitts and Nevis","Saint Lucia","Saint Vincent and the Grenadines","Samoa","San Marino","Sao Tome and Principe",
    "Saudi Arabia","Senegal","Serbia","Seychelles","Sierra Leone","Singapore","Slovakia","Slovenia","Solomon Islands","Somalia",
    "South Africa","South Korea","South Sudan","Spain","Sri Lanka","Sudan","Suriname","Sweden","Switzerland","Syria",
    "Tajikistan","Tanzania","Thailand","Timor-Leste","Togo","Tonga","Trinidad and Tobago","Tunisia","Turkey","Turkmenistan",
    "Tuvalu","Uganda","Ukraine","United Arab Emirates","United Kingdom","United States","Uruguay","Uzbekistan","Vanuatu","Venezuela",
    "Vietnam","Yemen","Zambia","Zimbabwe"
]

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://love:love@localhost:3309/lovedb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'random'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

db = SQLAlchemy(app)
gpg = gnupg.GPG()

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)

    username = db.Column(db.String(128), unique=True, nullable=False)
    pgp = db.Column(db.String(4096), nullable=False)

    firstname = db.Column(db.String(128), nullable=False)
    lastname = db.Column(db.String(128), nullable=False)

    sex = db.Column(db.Enum('male', 'female'), nullable=False)
    date_of_birth = db.Column(db.Date, nullable=False)

    profile_picture = db.Column(db.String(200), nullable=False)

    pictures = db.Column(db.JSON, nullable=True)

    country = db.Column(db.String(128), nullable=False)
    city = db.Column(db.String(128), nullable=True)

    height = db.Column(db.Float, nullable=True)
    weight = db.Column(db.Integer, nullable=True)
    race = db.Column(db.String(20), nullable=True)

    prefered_age_range = db.Column(db.String(20), nullable=True)

    likes = db.Column(db.JSON, nullable=True)
    dislikes = db.Column(db.JSON, nullable=True)

    xmpp = db.Column(db.String(128), unique=True, nullable=False)
    email = db.Column(db.String(128), unique=True, nullable=True)
    phone = db.Column(db.String(20), unique=True, nullable=True)

    is_verified = db.Column(db.Boolean, default=False)

@app.route("/")
def home():
    return render_template("index.html")

@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "POST":
        username = request.form.get("username")
        pgp = request.form.get("pgp")
        firstname = request.form.get("firstname")
        lastname = request.form.get("lastname")
        sex = request.form.get("sex")
        date_of_birth = request.form.get("date_of_birth")
        country = request.form.get("country")
        xmpp = request.form.get("xmpp")

        email = request.form.get("email")
        phone = request.form.get("phone")
        city = request.form.get("city")
        height = request.form.get("height")
        weight = request.form.get("weight")
        race = request.form.get("race")
        prefered_age_range = request.form.get("prefered_age_range")

        if not all([username, pgp, firstname, lastname, sex, date_of_birth, country, xmpp]):
            flash("Please fill all required fields.")
            return redirect(url_for("register"))

        if User.query.filter_by(username=username).first():
            flash("Username already exists.")
            return redirect(url_for("register"))

        if User.query.filter_by(xmpp=xmpp).first():
            flash("XMPP already exists.")
            return redirect(url_for("register"))

        if email and User.query.filter_by(email=email).first():
            flash("Email already exists.")
            return redirect(url_for("register"))

        if phone and User.query.filter_by(phone=phone).first():
            flash("Phone already exists.")
            return redirect(url_for("register"))

        try:
            dob = date.fromisoformat(date_of_birth)
        except ValueError:
            flash("Invalid date format.")
            return redirect(url_for("register"))

        today = date.today()
        age = today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
        if age < 18:
            flash("You must be at least 18 years old to register.")
            return redirect(url_for("register"))

        profile_file = request.files.get("profile_picture")
        pictures_files = request.files.getlist("pictures")

        if not profile_file:
            flash("Profile picture is required.")
            return redirect(url_for("register"))

        user_folder = os.path.join(app.root_path, "static/uploads", username)
        os.makedirs(user_folder, exist_ok=True)

        from werkzeug.utils import secure_filename
        profile_filename = secure_filename(profile_file.filename)
        profile_path = os.path.join(user_folder, profile_filename)
        profile_file.save(profile_path)
        profile_url = f"/static/uploads/{username}/{profile_filename}"

        pictures_urls = []
        for pic in pictures_files:
            if pic.filename:
                filename = secure_filename(pic.filename)
                path = os.path.join(user_folder, filename)
                pic.save(path)
                pictures_urls.append(f"/static/uploads/{username}/{filename}")

        session["pending_user"] = {
            "username": username,
            "pgp": pgp,
            "firstname": firstname,
            "lastname": lastname,
            "sex": sex,
            "date_of_birth": date_of_birth,
            "profile_url": profile_url,
            "pictures_urls": pictures_urls,
            "country": country,
            "xmpp": xmpp,
            "email": email,
            "phone": phone,
            "city": city,
            "height": height,
            "weight": weight,
            "race": race,
            "prefered_age_range": prefered_age_range
        }

        import_result = gpg.import_keys(pgp)
        if not import_result.fingerprints:
            flash("Invalid PGP key.")
            return redirect(url_for("register"))

        fingerprint = import_result.fingerprints[0]
        random_string = secrets.token_hex(16)
        challenge_phrase = f"this is the unencrypted string: {random_string}"
        encrypted_data = gpg.encrypt(challenge_phrase, recipients=[fingerprint])
        if not encrypted_data.ok:
            flash("Failed to encrypt challenge.")
            return redirect(url_for("register"))

        session["pgp_expected_phrase"] = challenge_phrase
        return render_template("verify.html", encrypted_message=str(encrypted_data))

    return render_template("register.html", countries=COUNTRIES)

@app.route("/verify", methods=["POST"])
def verify():
    expected_phrase = session.get("pgp_expected_phrase")
    data = session.get("pending_user")

    if not data or not expected_phrase:
        flash("Session expired.")
        return redirect(url_for("register"))

    submitted = request.form.get("decrypted_message")
    if not submitted:
        flash("You must paste the decrypted message.")
        return redirect(url_for("register"))

    if submitted.strip() == expected_phrase:
        dob = date.fromisoformat(data["date_of_birth"])

        new_user = User(
            username=data["username"],
            pgp=data["pgp"],
            firstname=data["firstname"],
            lastname=data["lastname"],
            sex=data["sex"],
            date_of_birth=dob,
            profile_picture=data["profile_url"],
            pictures=data["pictures_urls"],
            country=data["country"],
            xmpp=data["xmpp"],
            email=data["email"] or None,
            phone=data["phone"] or None,
            city=data["city"] or None,
            height=float(data["height"]) if data["height"] else None,
            weight=int(data["weight"]) if data["weight"] else None,
            race=data["race"] or None,
            prefered_age_range=data["prefered_age_range"] or None,
            is_verified=True
        )

        db.session.add(new_user)
        db.session.commit()

        session['user_id'] = new_user.id
        session['username'] = new_user.username

        # limpar dados temporários
        session.pop("pending_user", None)
        session.pop("pgp_expected_phrase", None)

        flash("PGP verification successful! Account created.")
        return redirect(url_for("home"))

    else:
        flash("Verification failed. Account not created.")
        return redirect(url_for("register"))

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username")
        pgp = request.form.get("pgp")

        if not username or not pgp:
            flash("Please enter both username and PGP key.")
            return redirect(url_for("login"))

        user = User.query.filter_by(username=username).first()
        if not user:
            flash("User not found.")
            return redirect(url_for("login"))

        import_result = gpg.import_keys(pgp)
        if not import_result.fingerprints:
            flash("Invalid PGP key.")
            return redirect(url_for("login"))

        fingerprint = import_result.fingerprints[0]

        random_string = secrets.token_hex(16)
        challenge_phrase = f"this is the unencrypted string: {random_string}"

        encrypted_data = gpg.encrypt(
            challenge_phrase,
            recipients=[fingerprint]
        )

        if not encrypted_data.ok:
            flash("Failed to encrypt challenge.")
            return redirect(url_for("login"))

        session["login_user_id"] = user.id
        session["login_expected_phrase"] = challenge_phrase

        return render_template(
            "login_verify.html",
            encrypted_message=str(encrypted_data)
        )

    return render_template("login.html")

@app.route("/login_verify", methods=["POST"])
def login_verify():
    user_id = session.get("login_user_id")
    expected_phrase = session.get("login_expected_phrase")

    if not user_id or not expected_phrase:
        flash("Login session expired")
        return redirect(url_for("login"))

    submitted = request.form.get("decrypted_message")
    if not submitted:
        flash("You must paste the decrypted message")
        return redirect(url_for("login"))

    if submitted.strip() == expected_phrase:
        user = User.query.get(user_id)
        session['user_id'] = user.id
        session['username'] = user.username

        session.pop("login_user_id", None)
        session.pop("login_expected_phrase", None)

        flash("Logged in successfully")
        return redirect(url_for("home"))
    else:
        flash("Verification failed")
        return redirect(url_for("login"))

@app.route("/logout")
def logout():
    session.pop('user_id', None)
    session.pop('username', None)
    flash("Logged out successfully")
    return redirect(url_for("home"))

@app.route("/user/<username>")
def user_profile(username):
    user = User.query.filter_by(username=username).first_or_404()

    return render_template("user.html", user=user, date=date)

if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)
