import os
import requests
import math
import time
import io
import shutil
import json
import logging
from flask import Flask, jsonify, render_template, send_file, request
from piaware_monitor import PiAwareClient, load_config
from database import DatabaseManager
from dataclasses import asdict

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


app = Flask(__name__)
config = load_config()
db = DatabaseManager()

piaware_ip = config.get("PIAWARE_IP", "piaware.local")
receiver_lat = config.get("RECEIVER_LAT")
receiver_lon = config.get("RECEIVER_LON")
client = PiAwareClient(ip_address=piaware_ip)

from pathlib import Path
CACHE_DIR = Path(__file__).parent / "cache" / "tiles"
CACHE_DIR.mkdir(parents=True, exist_ok=True)

@app.route('/tiles/<provider>/<int:z>/<int:x>/<int:y>.png')
def tile_cache(provider, z, x, y):
    PROVIDERS = {
        'osm': f"https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
        'carto_dark': f"https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
        'carto_dark_nolabels': f"https://a.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
        'satellite': f"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
        'topo': f"https://a.tile.opentopomap.org/{z}/{x}/{y}.png"
    }
    if provider not in PROVIDERS:
        return "", 404
        
    tile_path = CACHE_DIR / provider / str(z) / str(x) / f"{y}.png"
    if tile_path.exists():
        return send_file(tile_path, mimetype='image/png')
    
    url = PROVIDERS[provider]
    headers = {'User-Agent': 'PiAwareMonitor/1.0'}
    try:
        r = requests.get(url, headers=headers, timeout=5)
        if r.status_code == 200:
            tile_path.parent.mkdir(parents=True, exist_ok=True)
            with open(tile_path, 'wb') as f:
                f.write(r.content)
            return send_file(tile_path, mimetype='image/png')
    except Exception:
        pass
    return "", 404

# 1x1 transparent PNG
TRANSPARENT_PNG = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\x00\x01\x00\x00\x05\x00\x01\r\n-\xb4\x00\x00\x00\x00IEND\xaeB`\x82'

class WeatherCache:
    path = None
    last_check = 0

@app.route('/tiles/weather/<int:z>/<int:x>/<int:y>.png')
def tile_weather(z, x, y):
    if z > 11:
        return send_file(io.BytesIO(TRANSPARENT_PNG), mimetype='image/png')
        
    current_time = time.time()
    if current_time - WeatherCache.last_check > 600 or not WeatherCache.path:
        try:
            r = requests.get('https://api.rainviewer.com/public/weather-maps.json', timeout=5)
            if r.status_code == 200:
                new_path = r.json()['radar']['past'][-1]['path']
                if WeatherCache.path and new_path != WeatherCache.path:
                    # Clean up old weather tiles
                    old_dir = CACHE_DIR / "weather" / WeatherCache.path.replace('/', '_')
                    if old_dir.exists():
                        shutil.rmtree(old_dir, ignore_errors=True)
                WeatherCache.path = new_path
                WeatherCache.last_check = current_time
        except:
            pass
            
    if not WeatherCache.path:
        return send_file(io.BytesIO(TRANSPARENT_PNG), mimetype='image/png')
        
    safe_path = WeatherCache.path.replace('/', '_')
    tile_path = CACHE_DIR / "weather" / safe_path / str(z) / str(x) / f"{y}.png"
    
    if tile_path.exists():
        return send_file(tile_path, mimetype='image/png')
        
    url = f"https://tilecache.rainviewer.com{WeatherCache.path}/256/{z}/{x}/{y}/2/1_1.png"
    try:
        r = requests.get(url, headers={'User-Agent': 'PiAwareMonitor/1.0'}, timeout=5)
        if r.status_code == 200:
            if len(r.content) == 1370:
                return send_file(io.BytesIO(TRANSPARENT_PNG), mimetype='image/png')
            tile_path.parent.mkdir(parents=True, exist_ok=True)
            with open(tile_path, 'wb') as f:
                f.write(r.content)
            return send_file(tile_path, mimetype='image/png')
    except:
        pass
        
    return send_file(io.BytesIO(TRANSPARENT_PNG), mimetype='image/png')

@app.route("/")
def index():
    return render_template("index.html", retention=config.get("HISTORY_RETENTION_DAYS", 30))

@app.route("/atc")
def atc_page():
    return render_template("atc.html", refresh_rate=config.get("REFRESH_RATE", 5))

@app.route("/history")
def history_page():
    return render_template("history.html")

@app.route("/api/history")
def api_history():
    rows = db.get_recent_history(limit=200)
    return jsonify(rows)

def get_destination_point(lat, lon, distance, bearing):
    R = 6371.0
    lat1, lon1, brng = map(math.radians, [lat, lon, bearing])
    lat2 = math.asin(math.sin(lat1)*math.cos(distance/R) + math.cos(lat1)*math.sin(distance/R)*math.cos(brng))
    lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(distance/R)*math.cos(lat1), math.cos(distance/R) - math.sin(lat1)*math.sin(lat2))
    return math.degrees(lat2), math.degrees(lon2)

def get_convex_hull(points):
    pts = sorted([tuple(p) for p in points])
    if len(pts) <= 2:
        return [list(p) for p in pts]

    def cross(o, a, b):
        return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])

    lower = []
    for p in pts:
        while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
            lower.pop()
        lower.append(p)

    upper = []
    for p in reversed(pts):
        while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0:
            upper.pop()
        upper.append(p)

    hull = lower[:-1] + upper[:-1]
    return [list(p) for p in hull]

@app.route("/api/coverage")
def api_coverage():
    rows = db.get_coverage()
    points = []
    for bearing in range(360):
        dist = rows.get(bearing, 0)
        if dist > 0:
            lat, lon = get_destination_point(receiver_lat, receiver_lon, dist, bearing)
            points.append([lat, lon])
            
    if len(points) > 2:
        points = get_convex_hull(points)
        
    return jsonify(points)

@app.route("/stats")
def stats_page():
    return render_template("stats.html")

@app.route("/api/charts")
def api_charts():
    data = db.get_charts_data()
    data['achievements'] = db.get_achievements()
    
    # Traduction des codes de compagnies aériennes
    AIRLINE_MAPPING = {
        "AFR": "Air France",
        "RYR": "Ryanair",
        "EZY": "EasyJet",
        "EJU": "EasyJet Europe",
        "DLH": "Lufthansa",
        "BAW": "British Airways",
        "WZZ": "Wizz Air",
        "VLG": "Vueling",
        "TAP": "TAP Air Portugal",
        "EWG": "Eurowings",
        "KLM": "KLM",
        "SWR": "Swiss",
        "IBE": "Iberia",
        "AEA": "Air Europa",
        "TRA": "Transavia",
        "TVF": "Transavia France",
        "AAL": "American Airlines",
        "UAL": "United Airlines",
        "DAL": "Delta Air Lines",
        "FDX": "FedEx",
        "UPS": "UPS",
        "BCS": "DHL",
        "DHX": "DHL",
        "UAE": "Emirates",
        "QTR": "Qatar Airways",
        "THY": "Turkish Airlines"
    }
    
    translated_airlines = {}
    for code, count in data.get('airlines', {}).items():
        name = AIRLINE_MAPPING.get(code.upper(), code.upper())
        translated_airlines[name] = count
    data['airlines'] = translated_airlines
    
    return jsonify(data)

import json

DETAILS_CACHE_FILE = Path(__file__).parent / "cache" / "details.json"
if DETAILS_CACHE_FILE.exists():
    try:
        with open(DETAILS_CACHE_FILE, "r") as f:
            details_cache = json.load(f)
    except:
        details_cache = {}
else:
    details_cache = {}

def get_wikimedia_photos(query, limit=5):
    url = "https://commons.wikimedia.org/w/api.php"
    params = {
        "action": "query",
        "generator": "search",
        "gsrnamespace": "6",
        "gsrsearch": query,
        "gsrlimit": str(limit),
        "prop": "imageinfo",
        "iiprop": "url",
        "format": "json"
    }
    headers = {
        'User-Agent': 'PiAwareMonitor/1.0 (contact@example.com)'
    }
    urls = []
    try:
        r = requests.get(url, params=params, headers=headers, timeout=5)
        if r.status_code == 200:
            data = r.json()
            pages = data.get("query", {}).get("pages", {})
            for page_id, page in pages.items():
                imageinfo = page.get("imageinfo", [])
                if imageinfo:
                    img_url = imageinfo[0].get("url")
                    if img_url and img_url not in urls:
                        urls.append(img_url)
    except Exception as e:
        logger.error(f"Error searching Wikimedia for {query}: {e}")
    return urls

def init_details_for_hex(hex_id):
    details = {
        "type": None,
        "reg": None,
        "photo_list": [],
        "photo_index": 0,
        "photo_status": "no_photo",
        "photo": None
    }
    
    photo_list = []
    
    # 1. Planespotters API
    try:
        r = requests.get(f"https://api.planespotters.net/pub/photos/hex/{hex_id}", headers={'User-Agent': 'PiAwareMonitor/1.0'}, timeout=5)
        if r.status_code == 200:
            data = r.json()
            if data.get("photos"):
                for p in data["photos"]:
                    src = p.get("thumbnail_large", {}).get("src")
                    if src and src not in photo_list:
                        photo_list.append(src)
    except Exception as e:
        logger.error(f"Planespotters error for {hex_id}: {e}")
        
    # 2. HexDB API
    try:
        r = requests.get(f"https://hexdb.io/api/v1/aircraft/{hex_id}", headers={'User-Agent': 'PiAwareMonitor/1.0'}, timeout=5)
        if r.status_code == 200:
            data = r.json()
            details["type"] = data.get("Type")
            details["reg"] = data.get("Registration")
    except Exception as e:
        logger.error(f"HexDB error for {hex_id}: {e}")
        
    # 3. Fallback Wikimedia Commons
    if len(photo_list) < 5:
        if details["reg"]:
            wiki_urls = get_wikimedia_photos(details["reg"], limit=5)
            for u in wiki_urls:
                if u not in photo_list:
                    photo_list.append(u)
        if len(photo_list) < 5 and details["type"]:
            wiki_urls = get_wikimedia_photos(f"{details['type']} aircraft", limit=5)
            for u in wiki_urls:
                if u not in photo_list:
                    photo_list.append(u)
                    
    details["photo_list"] = photo_list
    if photo_list:
        details["photo"] = photo_list[0]
        details["photo_status"] = "pending"
        
    return details

@app.route("/api/details/<hex_id>")
def api_details(hex_id):
    if hex_id in details_cache:
        details = details_cache[hex_id]
        # Normalisation des anciennes entrées du cache
        if "photo_status" not in details:
            details["photo_list"] = [details["photo"]] if details.get("photo") else []
            details["photo_index"] = 0
            details["photo_status"] = "approved" if details.get("photo") else "no_photo"
            details_cache[hex_id] = details
        return jsonify(details)
        
    details = init_details_for_hex(hex_id)
    details_cache[hex_id] = details
    
    try:
        DETAILS_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
        with open(DETAILS_CACHE_FILE, "w") as f:
            json.dump(details_cache, f)
    except Exception as e:
        logger.error(f"Failed to save details cache: {e}")
        
    return jsonify(details)

@app.route("/api/details/<hex_id>/action", methods=["POST"])
def api_details_action(hex_id):
    if hex_id not in details_cache:
        details = init_details_for_hex(hex_id)
    else:
        details = details_cache[hex_id]
        
    data = request.json or {}
    action = data.get("action")
    
    if action == "approve":
        details["photo_status"] = "approved"
    elif action == "reject":
        # Afficher la photo suivante
        photo_list = details.get("photo_list", [])
        photo_index = details.get("photo_index", 0) + 1
        if photo_index < len(photo_list):
            details["photo_index"] = photo_index
            details["photo"] = photo_list[photo_index]
            details["photo_status"] = "pending"
        else:
            details["photo"] = None
            details["photo_status"] = "rejected"
    elif action == "cancel":
        details["photo"] = None
        details["photo_status"] = "rejected"
    elif action == "search":
        query = data.get("query")
        if query:
            new_urls = get_wikimedia_photos(query, limit=5)
            if new_urls:
                details["photo_list"] = new_urls
                details["photo_index"] = 0
                details["photo"] = new_urls[0]
                details["photo_status"] = "pending"
            else:
                return jsonify({"success": False, "error": "Aucune image trouvée."}), 200
        else:
            return jsonify({"success": False, "error": "Requête vide."}), 400
            
    details_cache[hex_id] = details
    try:
        DETAILS_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
        with open(DETAILS_CACHE_FILE, "w") as f:
            json.dump(details_cache, f)
    except Exception as e:
        logger.error(f"Failed to save details cache on action: {e}")
        
    return jsonify(details)

@app.route("/api/config", methods=["POST"])
def api_update_config():
    global config
    data = request.json
    if "HISTORY_RETENTION_DAYS" in data:
        try:
            days = int(data["HISTORY_RETENTION_DAYS"])
            config["HISTORY_RETENTION_DAYS"] = days
            with open("config.json", "w") as f:
                json.dump(config, f, indent=4)
            db.cleanup_history(days)
            return jsonify({"success": True})
        except Exception as e:
            logger.error(f"Erreur API /api/config : {e}")
            return jsonify({"success": False, "error": str(e)}), 500
    return jsonify({"success": False}), 400

@app.route("/api/airports")
def get_airports():
    receiver_lat = request.args.get('lat', type=float)
    receiver_lon = request.args.get('lon', type=float)
    if receiver_lat is None or receiver_lon is None:
        return jsonify([])
    
    csv_path = Path("data/airports.csv")
    if not csv_path.exists():
        import urllib.request
        try:
            csv_path.parent.mkdir(exist_ok=True)
            urllib.request.urlretrieve("https://davidmegginson.github.io/ourairports-data/airports.csv", csv_path)
        except Exception as e:
            logger.error(f"Erreur téléchargement aéroports: {e}")
            return jsonify([])

    import csv
    import math
    def haversine(lat1, lon1, lat2, lon2):
        if None in (lat1, lon1, lat2, lon2): return 9999
        R = 6371.0
        dlat = math.radians(lat2 - lat1)
        dlon = math.radians(lon2 - lon1)
        a = math.sin(dlat / 2)**2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon / 2)**2
        return R * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))

    airports = []
    try:
        with open(csv_path, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                if row.get('type') in ['large_airport', 'medium_airport', 'small_airport', 'heliport', 'seaplane_base'] and row.get('latitude_deg'):
                    lat = float(row['latitude_deg'])
                    lon = float(row['longitude_deg'])
                    dist = haversine(receiver_lat, receiver_lon, lat, lon)
                    if dist < 300:
                        airports.append({
                            "icao": row.get('ident'),
                            "name": row.get('name'),
                            "type": row.get('type'),
                            "lat": lat,
                            "lon": lon
                        })
    except Exception as e:
        logger.error(f"Erreur lecture CSV aéroports: {e}")
    
    return jsonify(airports)

cleanup_counter = 0

@app.route("/api/data")
def api_data():
    global cleanup_counter
    cleanup_counter += 1
    if cleanup_counter > 100:
        cleanup_counter = 0
        db.cleanup_history(config.get("HISTORY_RETENTION_DAYS", 30))
        
    raw_status = client.fetch_status()
    raw_aircraft = client.fetch_aircraft()
    
    aircraft_list = []
    total_messages = 0
    if raw_aircraft:
        total_messages = raw_aircraft.get("messages", 0)
        aircraft_list = client.parse_aircraft(raw_aircraft.get("aircraft", []), receiver_lat, receiver_lon)
    
    status_obj = None
    if raw_status:
        status_obj = client.parse_status(raw_status, total_messages, len(aircraft_list))
        
    # Mise à jour de la base de données
    for a in aircraft_list:
        db.update_flight(a)
        
    stats = db.get_stats()
    
    return jsonify({
        "status": asdict(status_obj) if status_obj else None,
        "aircraft": [asdict(a) for a in aircraft_list],
        "stats": stats,
        "receiver": {"lat": receiver_lat, "lon": receiver_lon}
    })

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