import sys
import os

if __package__ is None or __package__ == "":
    # Running as __main__ (e.g. python web/app.py)
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
    from utils.sql_commands import DatabaseManager, initialize_database
else:
    # Imported as a module (e.g. from bot.py)
    from utils.sql_commands import DatabaseManager, initialize_database

import os
import requests
from flask import Flask, redirect, url_for, session, request, render_template, flash
from dotenv import load_dotenv
from collections import defaultdict
import datetime
import json

load_dotenv()

app = Flask(__name__)
app.config.update(
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SECURE=os.getenv("FLASK_ENV") == "production",
    SESSION_COOKIE_SAMESITE="Lax",
    PERMANENT_SESSION_LIFETIME=datetime.timedelta(hours=1),
)

# Ensure a secret key is set for session security. In production this MUST be
# provided via the SECRET_KEY environment variable. In development we generate
# a temporary one (not suitable for production).
secret = os.getenv("SECRET_KEY")
if not secret:
    if os.getenv("FLASK_ENV") == "production":
        raise EnvironmentError("SECRET_KEY must be set in production environment")
    print("Warning: SECRET_KEY not set; generating a temporary key for development.")
    secret = os.urandom(32)
app.secret_key = secret
db = initialize_database()

# Ensure required environment variables are loaded
DISCORD_CLIENT_ID = os.getenv("DISCORD_CLIENT_ID")
DISCORD_CLIENT_SECRET = os.getenv("DISCORD_CLIENT_SECRET")
DISCORD_REDIRECT_URI = os.getenv("DISCORD_REDIRECT_URI")
DISCORD_API_BASE_URL = "https://discord.com/api"
DISCORD_BOT_TOKEN = os.getenv("TOKEN")

http = requests.Session()
http.headers.update({"User-Agent": "DiscordBotWeb/1.0"})

def discord_request(method, endpoint, headers=None, **kwargs):
    url = f"{DISCORD_API_BASE_URL}{endpoint}"
    try:
        response = http.request(method, url, headers=headers, timeout=REQUEST_TIMEOUT, **kwargs)
        response.raise_for_status()
        return response
    except requests.RequestException as exc:
        app.logger.warning("Discord API request failed: %s %s %s", method, url, exc)
        raise

# Default request timeout (seconds) for external HTTP calls
REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "10"))

# Check for missing environment variables
if not all([DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_REDIRECT_URI]):
    raise EnvironmentError("One or more required environment variables are missing.")


# OAuth2 login route
@app.route("/login")
def login():
    discord_auth_url = (
        f"{DISCORD_API_BASE_URL}/oauth2/authorize"
        f"?client_id={DISCORD_CLIENT_ID}&redirect_uri={DISCORD_REDIRECT_URI}"
        f"&response_type=code&scope=identify%20guilds%20applications.commands%20bot"
    )
    return redirect(discord_auth_url)


def can_add_bot(guild):
    # Check if the user has permission to manage the bot
    permissions = guild.get(
        "permissions", 0
    )  # Ensure permissions are included in the guild data
    # Bitwise operation for managing server (assuming manage_server is 0x20)
    return (permissions & 0x20) != 0


# OAuth2 callback route
@app.route("/callback")
def callback():
    code = request.args.get("code")
    if not code:
        flash("Authorization code was not provided.")
        return redirect(url_for("home"))

    data = {
        "client_id": DISCORD_CLIENT_ID,
        "client_secret": DISCORD_CLIENT_SECRET,
        "grant_type": "authorization_code",
        "code": code,
        "redirect_uri": DISCORD_REDIRECT_URI,
    }
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    try:
        response = discord_request(
            "POST",
            "/oauth2/token",
            data=data,
            headers=headers,
        )
    except requests.RequestException:
        flash("Failed to retrieve access token from Discord.")
        return redirect(url_for("home"))

    access_token = response.json().get("access_token")
    if not access_token:
        flash("Access token missing in the response.")
        return redirect(url_for("home"))

    session.permanent = True

    # Fetch user info
    try:
        user_resp = discord_request(
            "GET",
            "/users/@me",
            headers={"Authorization": f"Bearer {access_token}"},
        )
        user_data = user_resp.json()
    except requests.RequestException:
        flash("Failed to fetch user info from Discord.")
        return redirect(url_for("home"))

    session["user"] = user_data

    # Fetch guilds the user is in
    try:
        guilds_response = discord_request(
            "GET",
            "/users/@me/guilds",
            headers={"Authorization": f"Bearer {access_token}"},
        )
        guilds = guilds_response.json()
        session["guilds"] = guilds

        # Fetch member count and permissions for each guild
        for guild in guilds:
            guild_id = guild["id"]
            try:
                guild_info_response = discord_request(
                    "GET",
                    f"/guilds/{guild_id}",
                    headers={"Authorization": f"Bearer {access_token}"},
                )
                guild_info = guild_info_response.json()
                guild["approx_member_count"] = guild_info.get("member_count", "N/A")
                guild["permissions"] = guild_info.get("permissions", 0)
            except requests.RequestException:
                guild["approx_member_count"] = "N/A"

        guilds_with_bot_permission = [guild for guild in guilds if can_add_bot(guild)]
        session["guilds_with_bot_permission"] = guilds_with_bot_permission
    except requests.RequestException:
        flash("Failed to retrieve guilds from Discord.")
        return redirect(url_for("home"))

    return redirect(url_for("home"))


# Logout route
@app.route("/logout")
def logout():
    session.pop("user", None)
    session.pop("guilds", None)
    session.pop("access_token", None)  # Ensure access token is also cleared
    session.pop("guilds_with_bot_permission", None)
    flash("You have been logged out.")
    return redirect(url_for("home"))


@app.route("/main")
def main_page():
    return render_template("main.html")


# Update home route to display user info
@app.route("/")
def home():
    user = session.get("user")
    if not user:
        return redirect(
            url_for("main_page")
        )  # Redirect to the main page if not logged in

    guilds_with_permission = session.get("guilds_with_bot_permission", [])
    user_info = {
        "id": user.get("id", "N/A"),
        "username": user.get("username", "Unknown"),
        "discriminator": user.get("discriminator", "0000"),
        "avatar": user.get("avatar", None),
        "servers": len(guilds_with_permission),
    }

    return render_template(
        "home.html", user=user_info, stats=user_info, guilds=guilds_with_permission
    )


def get_user_balance(user_id):
    result = db.fetch_one("SELECT BANK, WALLET FROM economy WHERE ID = %s", (user_id,))
    print(result)
    bank_balance = result.get("BANK", 0) if result else 0
    wallet_balance = result.get("WALLET", 0) if result else 0

    return {
        "bank": bank_balance,
        "wallet": wallet_balance,
    }


@app.route("/wallet")
def wallet():
    user = session.get("user")
    if not user:
        flash("You are not logged in.")
        return redirect(url_for("login"))

    user_balance = get_user_balance(user["id"])

    return render_template("wallet.html", user=user, balance=user_balance)


def get_user_transactions(user_id, sort_by="date", order="asc"):
    valid_sort_fields = {
        "date": "TIME",
        "amount": "amount",
    }

    sort_column = valid_sort_fields.get(sort_by, "TIME")
    sort_order = "ASC" if order == "asc" else "DESC"

    query = f"SELECT * FROM transactions WHERE USERID = %s ORDER BY {sort_column} {sort_order}"
    transactions = db.fetch_all(query, (user_id,))
    return transactions if transactions else []


@app.route("/transactions")
def transactions():
    user = session.get("user")
    if not user:
        flash("You are not logged in.")
        return redirect(url_for("login"))

    sort_by = request.args.get("sort", "date")
    order = request.args.get("order", "asc")

    user_transactions = get_user_transactions(str(user["id"]), sort_by, order)
    daily_totals = defaultdict(float)

    for transaction in user_transactions:
        time_value = transaction["TIME"]
        if isinstance(time_value, str):
            try:
                time_value = datetime.datetime.fromisoformat(time_value)
            except Exception:
                try:
                    time_value = datetime.datetime.strptime(
                        time_value, "%Y-%m-%d %H:%M:%S"
                    )
                except Exception:
                    continue  # skip if can't parse
        date = time_value.date()
        amount = transaction["amount"]
        daily_totals[date] += float(amount)

    dates = list(daily_totals.keys())
    totals = list(daily_totals.values())

    return render_template(
        "transactions.html",
        user=user,
        transactions=user_transactions,
        dates=dates,
        totals=totals,
        sort_by=sort_by,
        order=order,
    )


@app.route("/add_command", methods=["GET", "POST"])
def add_command():
    user = session.get("user")
    if not user:
        flash("You are not logged in.")
        return redirect(url_for("login"))

    if request.method == "POST":
        guild_id = request.form.get("guild_id")
        command_name = request.form.get("command_name")
        response_text = request.form.get("response")

        # Save command to the database
        db.execute_query(
            "INSERT INTO custom_commands (GUILDID, COMMANDNAME, RESPONSE) VALUES (%s, %s, %s)",
            (guild_id, command_name, response_text),
        )
        flash("Custom command created successfully!")
        return redirect(url_for("home"))

    return render_template("add_command.html")


@app.route("/commands")
def list_commands():
    user = session.get("user")
    if not user:
        flash("You are not logged in.")
        return redirect(url_for("login"))

    guild_id = request.args.get("guild_id")
    commands = []
    if guild_id:
        commands = db.fetch_all(
            "SELECT * FROM custom_commands WHERE GUILDID = %s", (guild_id,)
        )
    return render_template("list_commands.html", commands=commands)


@app.route("/delete_command/<int:command_id>", methods=["POST"])
def delete_command(command_id):
    user = session.get("user")
    if not user:
        flash("You are not logged in.")
        return redirect(url_for("login"))

    db.execute_query("DELETE FROM custom_commands WHERE ID = %s", (command_id,))
    flash("Custom command deleted successfully!")
    return redirect(url_for("list_commands"))


@app.route("/guild/<guild_id>/settings", methods=["GET", "POST"])
def guild_settings(guild_id):
    user = session.get("user")
    if not user:
        flash("You are not logged in.")
        return redirect(url_for("login"))

    if not DISCORD_BOT_TOKEN:
        flash("Bot token not configured on server; cannot fetch guild info.")
        return redirect(url_for("home"))

    bot_headers = {"Authorization": f"Bot {DISCORD_BOT_TOKEN}"}
    try:
        guild_info_response = discord_request(
            "GET",
            f"/guilds/{guild_id}?with_counts=true",
            headers=bot_headers,
        )
        guild_info = guild_info_response.json()
    except requests.RequestException:
        flash("Failed to retrieve guild information (bot may not be in this guild or network error).")
        return redirect(url_for("home"))
    owner_id = guild_info.get("owner_id", "N/A")
    member_count = guild_info.get("approximate_member_count", "N/A")

    # Fetch owner's username using the bot token
    owner_name = "Unknown"
    if owner_id != "N/A":
        try:
            owner_response = discord_request(
                "GET",
                f"/users/{owner_id}",
                headers=bot_headers,
            )
            owner_data = owner_response.json()
            owner_name = f"{owner_data.get('username', 'Unknown')}#{owner_data.get('discriminator', '0000')}"
        except requests.RequestException:
            owner_name = owner_id  # fallback to ID if fetch fails
    json_formatted_str = json.dumps(guild_info, indent=2)

    print(json_formatted_str)

    return render_template(
        "guild_settings.html",
        user=user,
        guild=guild_info,
        owner_name=owner_name,
        member_count=member_count,
    )


@app.route("/profile")
def profile():
    user = session.get("user")
    if not user:
        flash("Please log in to view your profile.")
        return redirect(url_for("login"))
    # Example: Fetch more user data if needed
    # user_data = db.fetch_one("SELECT ... FROM ... WHERE ID = %s", (user["id"],))
    return render_template("profile.html", user=user)


if __name__ == "__main__":
    # Running via the Flask development server for local testing.
    # For production, run under a WSGI server (gunicorn, waitress, etc.).
    if os.getenv("FLASK_ENV") == "production":
        raise RuntimeError(
            "Do not use the Flask development server in production. "
            "Please serve this app with a WSGI server like gunicorn or waitress."
        )
    app.run(
        debug=True,
        port=int(os.getenv("PORT", "5000")),
        host="0.0.0.0",
        use_reloader=False,
    )
