from flask import Flask, jsonify, send_from_directory
from flask_cors import CORS
import time, requests, mysql.connector, os, threading, subprocess, re, platform
from datetime import datetime
from collections import defaultdict
from threading import Lock

app = Flask(__name__, static_folder="frontend/build", static_url_path="/")
CORS(app)

# ===== Configuration =====
DB_CONFIG = {
    "host": "165.22.220.143",
    "user": "api_monit",
    "password": "API-Monit#--",
    "database": "api"
}
MAIL_TO = "arunkumars@schemaxtech.com"
MAIL_SUBJECT = "ALERT -- API Monitoring Alert"
MAIL_COOLDOWN = 600  # 10 mins
MAIL_ENABLED = True  # disable during local testing

# ===== Global Cache =====
last_status, last_mail_time = {}, {}
check_lock = Lock()  # Prevent overlapping checks


def send_mail(subject, body):
    """Send mail using msmtp (skips on Windows/local)"""
    if not MAIL_ENABLED or platform.system() == "Windows":
        print("[Mail Skipped] =>", subject)
        return
    try:
        subprocess.run(["msmtp", MAIL_TO],
                       input=f"Subject: {subject}\n\n{body}",
                       text=True, check=True)
        print(f"[Mail Sent] {subject}")
    except Exception as e:
        print("Mail Error:", e)


def db_query(query, fetch=True, params=None):
    """Run DB query safely"""
    try:
        with mysql.connector.connect(**DB_CONFIG) as conn:
            cursor = conn.cursor()
            cursor.execute(query, params or ())
            if fetch:
                return cursor.fetchall()
            conn.commit()
    except Exception as e:
        print("DB Error:", e)
    return []


def get_api_groups():
    """Fetch active APIs grouped by app"""
    rows = db_query("SELECT app_name, api_name, api_url FROM api_list WHERE active=TRUE")
    api_data = defaultdict(list)
    for app, name, url in rows:
        api_data[app].append({"api_name": name.strip(), "api_url": url.strip()})
    return api_data


def format_url(url):
    """Ensure proper URL format"""
    if url.startswith(("http://", "https://")):
        return url
    return f"http://{url}" if re.match(r"^\d{1,3}(\.\d{1,3}){3}", url) else f"https://{url}"


def get_error_message(code):
    """HTTP code mapping"""
    return {
        400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found",
        500: "Internal Server Error", 502: "Bad Gateway", 503: "Service Unavailable",
        504: "Gateway Timeout"
    }.get(code, f"HTTP {code}")


def log_api_error(app, api, url, status, code, rtime, error):
    """Insert API log"""
    sql = """INSERT INTO api_logs (app_name, api_name, api_url, status_code,
             response_time, success, error_message, checked_at)
             VALUES (%s,%s,%s,%s,%s,%s,%s,%s)"""
    db_query(sql, fetch=False, params=(app, api, url, code, rtime, status == "OK", error, datetime.now()))


def check_all_apis():
    """Check all APIs and log"""
    if not check_lock.acquire(blocking=False):
        print("[Skip] Another check is already running.")
        return {}

    try:
        results, api_groups = {}, get_api_groups()

        for app_name, apis in api_groups.items():
            app_results = []
            for api in apis:
                name, full_url = api["api_name"], format_url(api["api_url"])
                status, code, err, rtime = "OK", 200, None, None
                try:
                    start = time.time()
                    resp = requests.get(full_url, timeout=5)
                    rtime, code = round(time.time() - start, 3), resp.status_code
                    if not (200 <= code < 300):
                        status, err = "FAILED", get_error_message(code)
                except requests.exceptions.Timeout:
                    status, code, err = "DOWN", 504, "Timeout"
                except requests.exceptions.ConnectionError:
                    status, code, err = "DOWN", 503, "Unreachable"
                except Exception as e:
                    status, code, err = "DOWN", 520, str(e)

                log_api_error(app_name, name, full_url, status, code, rtime, err)
                app_results.append({
                    "api_name": name, "url": full_url, "status": status,
                    "code": code, "response_time": rtime, "error": err,
                    "checked_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                })

                # Mail alert logic
                prev, now = last_status.get(full_url), time.time()
                cooldown = now - last_mail_time.get(full_url, 0)
                if status in ("DOWN", "FAILED") and (prev != status or cooldown > MAIL_COOLDOWN):
                    send_mail(
                        f"{MAIL_SUBJECT}: {app_name}/{name} is {status}",
                        f"App: {app_name}\nAPI: {name}\nURL: {full_url}\nStatus: {status}\nCode: {code}\nError: {err}"
                    )
                    last_mail_time[full_url] = now

                last_status[full_url] = status

            results[app_name] = app_results

        return results
    finally:
        check_lock.release()


@app.route("/api/check", methods=["GET"])
def api_check():
    results = check_all_apis()
    if not results:
        msg = "Skipped duplicate check (another run in progress)"
        print(msg)
        return jsonify({"message": msg, "checked_at": datetime.now().isoformat()})
    return jsonify({"checked_at": datetime.now().isoformat(), "results": results})


@app.route("/", defaults={"path": ""})
@app.route("/<path:path>")
def serve_react(path):
    target = os.path.join(app.static_folder, path)
    return send_from_directory(app.static_folder, path if os.path.exists(target) else "index.html")


def schedule_check(interval=300):
    """Background scheduler to run API checks periodically"""
    def loop():
        while True:
            print(f"[{datetime.now()}] Checking APIs...")
            check_all_apis()
            time.sleep(interval)
    threading.Thread(target=loop, daemon=True).start()


if __name__ == "__main__":
    import os
    # Prevent duplicate threads from Flask reloader
    if os.environ.get("WERKZEUG_RUN_MAIN") == "true" or not app.debug:
        schedule_check(30)  # Run every 30 seconds only once

    app.run(host="0.0.0.0", port=3333, debug=True)
