import os
import sqlite3
from functools import wraps
from werkzeug.security import check_password_hash, generate_password_hash

from flask import (
    Flask,
    render_template,
    request,
    redirect,
    url_for,
    flash,
    abort
)
from flask_login import (
    LoginManager,
    UserMixin,
    login_user,
    logout_user,
    current_user,
    login_required
)
from werkzeug.security import check_password_hash


BASE_DIR = os.path.abspath(os.path.dirname(__file__))
DB_PATH = os.path.join(BASE_DIR, "users.db")

app = Flask(
    __name__,
    template_folder="templates",
    static_folder="static",
    static_url_path="/static"
)

app.config["SECRET_KEY"] = "SECOS_DATACENTER_2026_super_secret_key_!@#"
app.config["SESSION_COOKIE_HTTPONLY"] = True
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_COOKIE_SECURE"] = True  # activar solo con HTTPS real


login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "index"
login_manager.login_message = "Debes iniciar sesión para acceder a esta sección."
login_manager.login_message_category = "error"


class User(UserMixin):
    def __init__(self, id_, name, email, password_hash, role, status):
        self.id = str(id_)
        self.name = name
        self.email = email
        self.password_hash = password_hash
        self.role = role
        self.status = status


def get_db_connection():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


def get_user_by_email(email):
    conn = get_db_connection()
    row = conn.execute(
        """
        SELECT id, name, email, password_hash, role, status
        FROM users
        WHERE lower(email) = lower(?)
        """,
        (email,)
    ).fetchone()
    conn.close()
    return row


def get_user_by_id(user_id):
    conn = get_db_connection()
    row = conn.execute(
        """
        SELECT id, name, email, password_hash, role, status
        FROM users
        WHERE id = ?
        """,
        (user_id,)
    ).fetchone()
    conn.close()
    return row


@login_manager.user_loader
def load_user(user_id):
    row = get_user_by_id(user_id)
    if not row:
        return None

    return User(
        row["id"],
        row["name"],
        row["email"],
        row["password_hash"],
        row["role"],
        row["status"]
    )


def require_role(*allowed_roles):
    def decorator(view_func):
        @wraps(view_func)
        @login_required
        def wrapper(*args, **kwargs):
            if current_user.role not in allowed_roles:
                abort(403)
            return view_func(*args, **kwargs)
        return wrapper
    return decorator


@app.route("/", methods=["GET"])
def index():
    if current_user.is_authenticated:
        return render_template(
            "datacenter.html",
            user_name=current_user.name,
            user_email=current_user.email,
            user_role=current_user.role
        )

    email = request.args.get("email", "")
    return render_template("index.html", email=email)


@app.route("/login", methods=["POST"])
def login():
    email = request.form.get("email", "").strip()
    password = request.form.get("password", "").strip()

    if not email or not password:
        flash("Debes ingresar correo y contraseña.", "error")
        return redirect(url_for("index", email=email) + "#login")

    row = get_user_by_email(email)

    # Usuario no existe -> 403
    if not row:
        return render_template(
            "403.html",
            allow_back=True
        ), 403

    # Usuario existe pero no está activo -> 403
    if row["status"] != "active":
        return render_template(
            "403.html",
            allow_back=True
        ), 403

    # Contraseña incorrecta -> volver al login con mensaje y conservar email
    if not check_password_hash(row["password_hash"], password):
        flash("Contraseña incorrecta.", "error")
        return redirect(url_for("index", email=email) + "#login")

    user = User(
        row["id"],
        row["name"],
        row["email"],
        row["password_hash"],
        row["role"],
        row["status"]
    )

    login_user(user)
    return redirect(url_for("index"))


@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for("index"))


@app.route("/admin/")
@require_role("admin")
def admin_panel():
    return render_template(
        "403.html",
        allow_back=True
    ), 403


@app.errorhandler(403)
def forbidden(error):
    return render_template(
        "403.html",
        allow_back=True
    ), 403

@app.route("/change_password", methods=["GET", "POST"])
@login_required
def change_password():
    if request.method == "POST":
        current_password = request.form.get("current_password", "").strip()
        new_password = request.form.get("new_password", "").strip()
        confirm_password = request.form.get("confirm_password", "").strip()

        if not current_password or not new_password or not confirm_password:
            flash("Debes completar todos los campos.", "error")
            return redirect("/change_password")

        if not check_password_hash(current_user.password_hash, current_password):
            flash("La contraseña actual es incorrecta.", "error")
            return redirect("/change_password")

        if new_password != confirm_password:
            flash("La nueva contraseña y su confirmación no coinciden.", "error")
            return redirect("/change_password")

        if len(new_password) < 8:
            flash("La nueva contraseña debe tener al menos 8 caracteres.", "error")
            return redirect("/change_password")

        new_hash = generate_password_hash(new_password)

        conn = get_db_connection()
        conn.execute(
            "UPDATE users SET password_hash = ? WHERE id = ?",
            (new_hash, current_user.id)
        )
        conn.commit()
        conn.close()

        current_user.password_hash = new_hash

        flash("Tu contraseña fue actualizada correctamente.", "success")
        return redirect("/change_password")

    return render_template("change_password.html")


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8060))
    app.run(host="0.0.0.0", port=port, debug=False)