from functools import cache
import json
from flask import (
    Flask,
    make_response,
    redirect,
    request,
    jsonify,
    render_template,
    send_from_directory,
    send_file,
)
import os
import json
import requests
from datetime import datetime, timedelta
import dotenv
from pytz import timezone
import pytz
import re


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():
    return render_template("index.html")

# 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)
    return pytz.timezone(full_tz) if isinstance(full_tz, str) else 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 %Z"),
        }
    )

@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)
            offset = to_tz_time.strftime('%z')
            offset_formatted = f"UTC{offset[:3]}:{offset[3:]}" if offset else ""
            tz_name = to_tz_time.tzname() if to_tz_time.tzname() else offset_formatted
            results[original_tz] = f"{to_tz_time.strftime('%Y-%m-%d %H:%M:%S')} {tz_name}"
    except Exception as e:
        return jsonify({"error": str(e)}), 400

    return jsonify(results)

@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")