import threading
from flask import Flask, request, jsonify, render_template
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime
import time
import math
import os
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

app = Flask(__name__)

# --- CONFIGURATION (LOADED FROM .env) ---
EMAIL_CONF = {
    "smtp_server": os.getenv("SMTP_SERVER", "mail.eruditech.com"),
    "smtp_port": int(os.getenv("SMTP_PORT", 465)),
    "sender_email": os.getenv("SENDER_EMAIL", "speedbot@eruditech.com"),
    "password": os.getenv("EMAIL_PASSWORD", ""),
    "receiver_email": os.getenv("RECEIVER_EMAIL", "")
}

print(EMAIL_CONF)

# Reference Location (Home)
REFERENCE_LOC = {
    "lat": float(os.getenv("REFERENCE_LAT", 23.7164929)),
    "lon": float(os.getenv("REFERENCE_LON", 90.3856552)),
    "max_distance_meters": int(os.getenv("MAX_DISTANCE_METERS", 500))
}

SPEED_THRESHOLD_KMH = float(os.getenv("SPEED_THRESHOLD_KMH", 30.0))
OFFLINE_EMAIL_THRESHOLD = 30  # (in seconds)

# --- GLOBAL STATE (In-Memory Storage) ---
bike_state = {
    "lat": REFERENCE_LOC["lat"],
    "lon": REFERENCE_LOC["lon"],
    "speed": 0,
    "last_updated": 0
}

alert_state = {
    "is_speeding": False,
    "last_speed_email_time": 0,
    "distance_warning_count": 0,
    "offline_email_sent": False # <--- NEW FLAG
}

# --- HELPER FUNCTIONS ---

def calculate_distance(lat1, lon1, lat2, lon2):
    # Haversine formula to calculate distance in meters
    R = 6371000 # Earth radius in meters
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    delta_phi = math.radians(lat2 - lat1)
    delta_lambda = math.radians(lon2 - lon1)
    a = math.sin(delta_phi/2)**2 + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda/2)**2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    return R * c

def send_email(subject, body):
    try:
        print("sending mail")
        msg = MIMEMultipart()
        msg['From'] = EMAIL_CONF["sender_email"]
        msg['To'] = EMAIL_CONF["receiver_email"]
        msg['Subject'] = subject
        msg.attach(MIMEText(body, 'plain'))

        # Connect to SMTP Server (Using SSL)
        server = smtplib.SMTP(EMAIL_CONF["smtp_server"], EMAIL_CONF["smtp_port"])
        server.starttls()
        server.login(EMAIL_CONF["sender_email"], EMAIL_CONF["password"])
        server.send_message(msg)
        server.quit()
        print(f"Email sent: {subject}")
    except Exception as e:
        print(f"Failed to send email: {e}")

# --- NEW: BACKGROUND MONITOR THREAD ---
def monitor_device_health():
    """Checks every 10 seconds if the device has gone offline."""
    print("Health Monitor Started...")
    while True:
        time.sleep(10) # Check interval
        
        current_time = time.time()
        # If last_updated is 0, device hasn't connected yet, ignore.
        if bike_state["last_updated"] == 0:
            continue

        time_since_last_contact = current_time - bike_state["last_updated"]

        # Check for Offline Status (> 2 mins)
        if time_since_last_contact > OFFLINE_EMAIL_THRESHOLD:
            if not alert_state["offline_email_sent"]:
                # Device just went offline
                send_email(
                    "ALERT: Bike Signal Lost!", 
                    f"The device stopped sending data {int(time_since_last_contact)} seconds ago.\nLast Known Speed: {bike_state['speed']} km/h\nLast Known Location: https://maps.google.com/?q={bike_state['lat']},{bike_state['lon']}"
                )
                alert_state["offline_email_sent"] = True
        else:
            # Device is Online, reset the email flag
            alert_state["offline_email_sent"] = False

# --- ROUTES ---

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/status', methods=['GET'])
def get_status():
    # Frontend polls this for real-time map updates
    return jsonify(bike_state)

@app.route('/update', methods=['POST'])
def update_data():
    global bike_state, alert_state
    
    data = request.json
    if not data:
        return "No data", 400

    # Update current state
    current_lat = float(data.get('lat', 0))
    current_lon = float(data.get('lon', 0))
    current_speed = float(data.get('speed', 0)) # Expected in km/h from ESP32
    current_time = time.time()

    bike_state.update({
        "lat": current_lat,
        "lon": current_lon,
        "speed": current_speed,
        "last_updated": current_time
    })

    # Note: Offline flag resets automatically in the background thread 
    # or we could force it here: alert_state["offline_email_sent"] = False

    # Logic Engine (Speed & Distance)
    dist_from_home = calculate_distance(current_lat, current_lon, REFERENCE_LOC["lat"], REFERENCE_LOC["lon"])
    
    alerts_to_send = []

    # 1. Speed Logic
    if current_speed > SPEED_THRESHOLD_KMH:
        alert_state["is_speeding"] = True
        # Check if 5 seconds have passed since last email
        if (current_time - alert_state["last_speed_email_time"]) >= 5:
            alerts_to_send.append(f"SPEED ALERT! Speed: {current_speed} km/h (Limit: {SPEED_THRESHOLD_KMH})")
            alert_state["last_speed_email_time"] = current_time
    else:
        # Recovery Logic (Speed dropped)
        if alert_state["is_speeding"]:
            send_email(
                "Safe: Speed Normal", 
                f"Speed is back under limit.\nCurrent Speed: {current_speed} km/h\nLocation: https://maps.google.com/?q={current_lat},{current_lon}"
            )
            alert_state["is_speeding"] = False

    # 2. Geofence Logic
    is_now_away = dist_from_home > REFERENCE_LOC["max_distance_meters"]
    print(dist_from_home)
    
    if is_now_away:
        print("is now away")
        if alert_state["distance_warning_count"] < 3:
            alerts_to_send.append(f"GEOFENCE ALERT! Distance from home: {int(dist_from_home)}m")
            alert_state["distance_warning_count"] += 1
            print(alerts_to_send)
    else:
        # Reset counter if back home
        alert_state["distance_warning_count"] = 0

    # 3. Send Combined Alerts (if any)
    if alerts_to_send:
        print("alerts to send", alerts_to_send)
        subject = "BIKE ALERT: " + (" & ".join(["Speeding" if "SPEED" in x else "Geofence" for x in alerts_to_send]))
        body = "\n".join(alerts_to_send)
        body += f"\n\nCurrent Location: https://maps.google.com/?q={current_lat},{current_lon}"
        
        print(body, "body ----------------------------")

        # We only send if the 5s timer allows (managed by speed logic) 
        # OR if it's purely a geofence alert we send immediately (but max 3 times total controlled by counter)
        print(subject, "--------------------------")
        if "Speeding" in subject:
            # Timer was already checked above
            send_email(subject, body)
        elif "Geofence" in subject:
            print(subject, "geoffecde -------------")
            send_email(subject, body)

    return "OK", 200

if __name__ == '__main__':
    # Start the monitor thread before the web server
    t = threading.Thread(target=monitor_device_health)
    t.daemon = True # Thread dies when main app dies
    t.start()
    
    app.run(host='0.0.0.0', port=5000)