from functools import cache
import json
from flask import (
    Flask,
    make_response,
    redirect,
    request,
    jsonify,
    render_template,
    send_from_directory,
    send_file,
    Response
)
import os
import json
import requests
from datetime import datetime, timedelta
import dotenv
from pytz import timezone
import pytz
import re
from PIL import Image, ImageDraw, ImageFont
import io
import urllib.parse


dotenv.load_dotenv()

app = Flask(__name__)


def find(name, path):
    for root, dirs, files in os.walk(path):
        if name in files:
            return os.path.join(root, name)

# Assets routes
@app.route("/assets/<path:path>")
def send_assets(path):
    if path.endswith(".json"):
        return send_from_directory(
            "templates/assets", path, mimetype="application/json"
        )

    if os.path.isfile("templates/assets/" + path):
        return send_from_directory("templates/assets", path)

    # Try looking in one of the directories
    filename: str = path.split("/")[-1]
    if (
        filename.endswith(".png")
        or filename.endswith(".jpg")
        or filename.endswith(".jpeg")
        or filename.endswith(".svg")
    ):
        if os.path.isfile("templates/assets/img/" + filename):
            return send_from_directory("templates/assets/img", filename)
        if os.path.isfile("templates/assets/img/favicon/" + filename):
            return send_from_directory("templates/assets/img/favicon", filename)

    return render_template("404.html"), 404


# region Special routes
@app.route("/favicon.png")
def faviconPNG():
    return send_from_directory("templates/assets/img", "favicon.png")


@app.route("/.well-known/<path:path>")
def wellknown(path):
    # Try to proxy to https://nathan.woodburn.au/.well-known/
    req = requests.get(f"https://nathan.woodburn.au/.well-known/{path}")
    return make_response(
        req.content, 200, {"Content-Type": req.headers["Content-Type"]}
    )


# endregion


# region Main routes
@app.route("/")
def index():
    from_tz = request.args.get('from_tz', '')
    time = request.args.get('time', '')
    to_tzs = request.args.get('to_tzs', '')
    
    # Generate the OG image URL based on the parameters
    # Make sure to encode the parameters
    from_tz = urllib.parse.quote(from_tz)
    time = urllib.parse.quote(time)
    to_tzs = urllib.parse.quote(to_tzs)


    og_image = f"og_image?from_tz={from_tz}&time={time}&to_tzs={to_tzs}"
    
    return render_template("index.html", og_image=og_image)

# Mapping of short timezone names to full names
SHORT_TZ_MAP = {
    "GMT": "Etc/GMT",
    "PST": "America/Los_Angeles",
    "PDT": "America/Los_Angeles",
    "MST": "America/Denver",
    "MDT": "America/Denver",
    "CST": "America/Chicago",
    "CDT": "America/Chicago",
    "EST": "America/New_York",
    "EDT": "America/New_York",
    "AKST": "America/Anchorage",
    "AKDT": "America/Anchorage",
    "HST": "Pacific/Honolulu",
    "HDT": "Pacific/Honolulu",
    "AST": "America/Puerto_Rico",
    "NST": "America/St_Johns",
    "BST": "Europe/London",
    "CET": "Europe/Paris",
    "CEST": "Europe/Paris",
    "EET": "Europe/Athens",
    "EEST": "Europe/Athens",
    "MSK": "Europe/Moscow",
    "MSD": "Europe/Moscow",
    "IST": "Asia/Kolkata",
    "PKT": "Asia/Karachi",
    "WIB": "Asia/Jakarta",
    "WITA": "Asia/Makassar",
    "WIT": "Asia/Jayapura",
    "CST": "Asia/Shanghai",
    "JST": "Asia/Tokyo",
    "KST": "Asia/Seoul",
    "AEDT": "Australia/Sydney",
    "AEST": "Australia/Sydney",
    "ACST": "Australia/Adelaide",
    "ACDT": "Australia/Adelaide",
    "AWST": "Australia/Perth",
}

def get_full_timezone(tz):
    match = re.match(r"^UTC([+-]\d{1,2}):?(\d{2})?$", tz)
    if match:
        hours = int(match.group(1))
        minutes = int(match.group(2)) if match.group(2) else 0
        return pytz.FixedOffset(hours * 60 + minutes)
    full_tz = SHORT_TZ_MAP.get(tz, tz)
    try:
        return pytz.timezone(full_tz) if isinstance(full_tz, str) else None
    except pytz.UnknownTimeZoneError:
        return None

@app.route("/api/v1/convert", methods=["POST"])
def convert():
    data = request.json
    from_tz = get_full_timezone(data.get("from_tz").replace("_", "/"))
    to_tz = get_full_timezone(data.get("to_tz").replace("_", "/"))
    time = data.get("time")

    if from_tz is None or to_tz is None:
        return jsonify({"error": "Invalid timezone"}), 400

    # Parse the input time
    try:
        input_time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S")
    except ValueError:
        input_time = datetime.strptime(time, "%Y-%m-%dT%H:%M")

    try:
        # Get the timezone
        from_tz_time = from_tz.localize(input_time)

        # Convert the time
        to_tz_time = from_tz_time.astimezone(to_tz)
    except Exception as e:
        return jsonify({"error": str(e)}), 400

    return jsonify(
        {
            "from": from_tz_time.strftime("%Y-%m-%d %H:%M:%S %Z"),
            "to": to_tz_time.strftime("%Y-%m-%d %H:%M:%S"),
        }
    )

@app.route("/api/v1/convert_multiple", methods=["POST"])
def convert_multiple():
    data = request.json
    from_tz = get_full_timezone(data.get("from_tz").replace("_", "/"))
    time = data.get("time")
    to_tz_list = [(tz, get_full_timezone(tz.replace("_", "/"))) for tz in data.get("to_tzs") if len(tz) > 0]

    if from_tz is None or any(tz[1] is None for tz in to_tz_list):
        return jsonify({"error": "Invalid timezone"}), 400

    # Parse the input time
    try:
        input_time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S")
    except ValueError:
        input_time = datetime.strptime(time, "%Y-%m-%dT%H:%M")

    try:
        # Get the timezone
        from_tz_time = from_tz.localize(input_time)

        # Convert the time to each timezone in the list
        results = {}
        for original_tz, tz in to_tz_list:
            to_tz_time = from_tz_time.astimezone(tz)
            results[original_tz] = f"{to_tz_time.strftime('%Y-%m-%d %H:%M:%S')}"
    except Exception as e:
        return jsonify({"error": str(e)}), 400

    return jsonify(results)

@app.route("/og_image")
def og_image():
    from_tz = request.args.get('from_tz', 'Unknown')
    time = request.args.get('time', 'Unknown')
    to_tzs = request.args.get('to_tzs', 'Unknown')

    # Load the background image
    img_path = "templates/assets/img/og_image.webp"
    img = Image.open(img_path).convert("RGBA")
    d = ImageDraw.Draw(img)

    # Convert time format to 12-hour format and add AM/PM
    try:
        input_time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S")
    except ValueError:
        input_time = datetime.strptime(time, "%Y-%m-%dT%H:%M")
    time = input_time.strftime("%I:%M %p")



    # Prepare text
    text = f"{time} {from_tz.replace("_","/")}"

    from_tz = get_full_timezone(from_tz.replace("_", "/"))

    # Check if multiple timezones are provided
    if "," in to_tzs:
        to_tzs = to_tzs.split(",")
        to_tz_list = [(tz, get_full_timezone(tz.replace("_", "/"))) for tz in to_tzs if len(tz) > 0]
    else:
        to_tz_list = [(to_tzs, get_full_timezone(to_tzs.replace("_", "/")))]

    if from_tz is None or any(tz[1] is None for tz in to_tz_list):
        return send_from_directory("templates/assets/img", "og_image.webp")

    

    for tz in to_tz_list:
        to_tz = tz[1]
        to_tz_time = from_tz.localize(input_time).astimezone(to_tz)
        text += f"\n{to_tz_time.strftime('%I:%M %p')} {tz[0].replace('_','/')}"
        
        
    # Use font from assets
    font_path = "templates/assets/fonts/Orbitron-VariableFont_wght.ttf"
    font_size = 180
    # Shrink font size if text is too long
    if (max(len(line) for line in text.split("\n")) > 13):
        font_size = 150
    
    font_size -= (len(text.split("\n")) - 4) * 10
    


    font = ImageFont.truetype(font_path, font_size)

    text_bbox = d.textbbox((0, 0), text, font=font)
    text_width = text_bbox[2] - text_bbox[0]
    text_height = text_bbox[3] - text_bbox[1]

    # Calculate text position
    x = (img.width - text_width) / 2
    y = (img.height - text_height) / 2

    # Add text to the image
    d.text((x, y), text, fill=(255, 255, 255), font=font, align="center")

    # Save the image to a BytesIO object in WebP format
    img_io = io.BytesIO()
    img.save(img_io, 'WEBP')
    img_io.seek(0)

    return Response(img_io, mimetype='image/webp')

@app.route("/<path:path>")
def catch_all(path: str):
    if os.path.isfile("templates/" + path):
        return render_template(path)

    # Try with .html
    if os.path.isfile("templates/" + path + ".html"):
        return render_template(path + ".html")

    if os.path.isfile("templates/" + path.strip("/") + ".html"):
        return render_template(path.strip("/") + ".html")

    # Try to find a file matching
    if path.count("/") < 1:
        # Try to find a file matching
        filename = find(path, "templates")
        if filename:
            return send_file(filename)

    return render_template("404.html"), 404


# endregion


# region Error Catching
# 404 catch all
@app.errorhandler(404)
def not_found(e):
    return render_template("404.html"), 404


# endregion
if __name__ == "__main__":
    app.run(debug=True, port=5000, host="0.0.0.0")