import math
import os
import sqlite3
from datetime import datetime
from flask import Flask, jsonify, render_template, request, redirect, url_for, flash
from dotenv import load_dotenv
import requests
from urllib.parse import quote
load_dotenv()

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DB_PATH = os.path.join(BASE_DIR, "salla_demo.db")

app = Flask(__name__, template_folder="templates", static_folder="static")
app.secret_key = os.getenv("FLASK_SECRET_KEY", "dev-secret")
app.config["GOOGLE_MAPS_API_KEY"] = os.getenv("GOOGLE_MAPS_API_KEY", "")

ALERT_PHONE_NUMBER = os.getenv("ALERT_PHONE_NUMBER", "")
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "")

print("GOOGLE_MAPS_API_KEY loaded:", bool(app.config["GOOGLE_MAPS_API_KEY"]))

SALLA_CENTER = {"lat": 66.8346, "lng": 28.6659}

ATTRACTIONS = [
    {
        "id": "att-1",
        "name": "Salla National Park",
        "category": "nature",
        "lat": 66.8448,
        "lng": 28.7040,
        "price_level": 1,
        "tags": ["nature", "hiking", "national park", "photography"],
        "description": "Explore fell landscapes, forest trails, and year-round outdoor experiences in Salla National Park.",
        "link": "https://www.visitsalla.fi/en/find-activities/attractions-and-places-to-visit/",
    },
    {
        "id": "att-2",
        "name": "Oulanka National Park / Hautajärvi",
        "category": "nature",
        "lat": 66.5600,
        "lng": 29.3200,
        "price_level": 1,
        "tags": ["nature", "hiking", "arctic circle", "national park"],
        "description": "Southern Salla offers access to Oulanka National Park and the Arctic Circle in Hautajärvi.",
        "link": "https://www.visitsalla.fi/en/find-activities/attractions-and-places-to-visit/",
    },
    {
        "id": "att-3",
        "name": "Museum of War and Reconstruction",
        "category": "culture",
        "lat": 66.8341,
        "lng": 28.6648,
        "price_level": 1,
        "tags": ["history", "museum", "culture"],
        "description": "Discover Salla’s history through exhibitions and local heritage.",
        "link": "https://www.visitsalla.fi/en/tekeminen/war-memorials-and-other-historical-sites/",
    },
    {
        "id": "att-4",
        "name": "Sallatunturi Area",
        "category": "activity",
        "lat": 66.8325,
        "lng": 28.6722,
        "price_level": 2,
        "tags": ["cycling", "ski", "routes", "family", "activities"],
        "description": "A year-round area for routes, skiing, outdoor activities, and guided experiences.",
        "link": "https://www.visitsalla.fi/en/",
    },
    {
        "id": "att-5",
        "name": "Outdoor trails and cycling routes",
        "category": "cycling",
        "lat": 66.8400,
        "lng": 28.6850,
        "price_level": 1,
        "tags": ["cycling", "mountain biking", "trails", "summer"],
        "description": "Discover marked biking and hiking routes in Salla’s landscapes.",
        "link": "https://www.visitsalla.fi/en/find-activities/outdoor-trails-and-xc-ski-tracks/",
    },
    {
        "id": "att-6",
        "name": "Salla Church",
        "category": "culture",
        "lat": 66.8350,
        "lng": 28.6680,
        "price_level": 1,
        "tags": ["church", "culture", "history"],
        "description": "A cultural stop in the village area near other local sights.",
        "link": "https://www.visitsalla.fi/en/find-activities/attractions-and-places-to-visit/",
    },
]

SERVICES = [
    {
        "id": "svc-1",
        "name": "Salla Health Centre",
        "type": "health",
        "lat": 66.8348,
        "lng": 28.6672,
        "priority": 10,
        "contact": "+358 demo health",
        "description": "Primary demo health support point in Salla centre.",
        "address": "Salla centre",
    },
    {
        "id": "svc-2",
        "name": "Salla Tourist Info",
        "type": "info",
        "lat": 66.8344,
        "lng": 28.6663,
        "priority": 7,
        "contact": "+358 tourist info",
        "description": "Tourist guidance and central assistance point.",
        "address": "Tourist centre",
    },
    {
        "id": "svc-3",
        "name": "Fell Side Bike Repair",
        "type": "bike_repair",
        "lat": 66.8334,
        "lng": 28.6728,
        "priority": 8,
        "contact": "+358 demo bike",
        "description": "Sample private bike repair partner near the resort.",
        "address": "Resort area",
    },
    {
        "id": "svc-4",
        "name": "North Trail Bike Service",
        "type": "bike_repair",
        "lat": 66.8468,
        "lng": 28.6965,
        "priority": 6,
        "contact": "+358 trail service",
        "description": "Sample trail-side bike support point.",
        "address": "North trail access",
    },
    {
        "id": "svc-5",
        "name": "Salla Adventure Rentals",
        "type": "bike_rental",
        "lat": 66.8327,
        "lng": 28.6711,
        "priority": 8,
        "contact": "+358 rentals",
        "description": "Sample rental partner offering mountain bikes and fatbikes.",
        "address": "Resort rentals",
    },
    {
        "id": "svc-6",
        "name": "Village Souvenir Hub",
        "type": "souvenir",
        "lat": 66.8352,
        "lng": 28.6640,
        "priority": 5,
        "contact": "+358 souvenirs",
        "description": "Demo reward redemption and souvenir pickup location.",
        "address": "Village centre",
    },
]

CANDIDATE_PARTNER_LOCATIONS = [
    {
        "name": "Town Centre Opportunity",
        "lat": 66.8351,
        "lng": 28.6668,
        "reason": "High accessibility and service clustering.",
    },
    {
        "name": "Resort Gateway Opportunity",
        "lat": 66.8329,
        "lng": 28.6730,
        "reason": "Near rentals, cycling start points, and tourist flow.",
    },
    {
        "name": "National Park Access Opportunity",
        "lat": 66.8450,
        "lng": 28.7012,
        "reason": "Strong for outdoor visitors and guided services.",
    },
]


def get_db():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


def init_db():
    conn = get_db()
    conn.execute(
        """
        CREATE TABLE IF NOT EXISTS partners (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            business_name TEXT NOT NULL,
            owner_name TEXT NOT NULL,
            email TEXT NOT NULL,
            partner_type TEXT NOT NULL,
            fee_plan TEXT NOT NULL,
            lat REAL,
            lng REAL,
            notes TEXT,
            created_at TEXT NOT NULL
        )
        """
    )
    conn.execute(
        """
        CREATE TABLE IF NOT EXISTS alerts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            alert_type TEXT NOT NULL,
            user_name TEXT,
            lat REAL NOT NULL,
            lng REAL NOT NULL,
            vitals_json TEXT,
            resolved INTEGER DEFAULT 0,
            created_at TEXT NOT NULL
        )
        """
    )
    conn.commit()
    conn.close()



def haversine_km(lat1, lon1, lat2, lon2):
    r = 6371.0
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    d_phi = math.radians(lat2 - lat1)
    d_lam = math.radians(lon2 - lon1)
    a = math.sin(d_phi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(d_lam / 2) ** 2
    return 2 * r * math.atan2(math.sqrt(a), math.sqrt(1 - a))


def estimate_minutes(distance_km, mode):
    speeds = {
        "bike": 14.0,
        "walk": 4.8,
        "car": 40.0,
        "emergency": 55.0,
    }
    speed = speeds.get(mode, 14.0)
    return max(1, round((distance_km / speed) * 60))


def nearest_service(lat, lng, service_type):
    options = [s for s in SERVICES if s["type"] == service_type]
    ranked = []
    for service in options:
        distance = haversine_km(lat, lng, service["lat"], service["lng"])
        ranked.append(
            {
                **service,
                "distance_km": round(distance, 2),
                "eta_minutes": estimate_minutes(
                    distance,
                    "bike" if service_type == "bike_repair" else "car",
                ),
            }
        )
    ranked.sort(key=lambda x: (x["distance_km"], -x["priority"]))
    return ranked[0] if ranked else None


def generate_route(origin_lat, origin_lng, dest_lat, dest_lng):
    mid1 = {
        "lat": round((origin_lat * 0.7 + dest_lat * 0.3), 6),
        "lng": round((origin_lng * 0.7 + dest_lng * 0.3) + 0.008, 6),
    }
    mid2 = {
        "lat": round((origin_lat * 0.35 + dest_lat * 0.65), 6),
        "lng": round((origin_lng * 0.35 + dest_lng * 0.65) + 0.004, 6),
    }
    return [
        {"lat": origin_lat, "lng": origin_lng},
        mid1,
        mid2,
        {"lat": dest_lat, "lng": dest_lng},
    ]


def build_trip_plan(budget, days, need_bike, interests):
    selected = []
    total_estimated = 0
    normalized_interests = {i.strip().lower() for i in interests if i.strip()}

    for attraction in ATTRACTIONS:
        score = 0
        if normalized_interests.intersection(set(attraction["tags"])):
            score += 3
        score += max(0, 4 - attraction["price_level"])
        if need_bike and "cycling" in attraction["tags"]:
            score += 3

        attraction_copy = {**attraction, "score": score}
        selected.append(attraction_copy)

    selected.sort(key=lambda x: x["score"], reverse=True)
    chosen = selected[: max(2, min(days + 1, len(selected)))]

    itinerary = []
    for index, item in enumerate(chosen, start=1):
        cost = item["price_level"] * 18
        total_estimated += cost
        itinerary.append(
            {
                "day": min(index, days),
                "title": item["name"],
                "category": item["category"],
                "estimated_cost_eur": cost,
                "description": item["description"],
            }
        )

    rewards = []
    if days >= 2:
        rewards.append("Free souvenir voucher after 2 completed activities")
    if total_estimated >= 80:
        rewards.append("Discount on selected bike rental or guided trail add-on")
    if need_bike:
        rewards.append("Bonus points for using registered local rental partners")

    return {
        "budget_eur": budget,
        "days": days,
        "need_bike": need_bike,
        "recommended_itinerary": itinerary,
        "estimated_total_cost_eur": total_estimated,
        "rewards": rewards,
    }

def send_telegram_alert(message: str) -> bool:
    if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
        return False

    url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    payload = {
        "chat_id": TELEGRAM_CHAT_ID,
        "text": message,
    }

    try:
        resp = requests.post(url, json=payload, timeout=10)
        resp.raise_for_status()
        return True
    except Exception as exc:
        print("Telegram alert failed:", exc)
        return False


def build_google_maps_navigation_url(dest_lat: float, dest_lng: float, travel_mode: str = "driving") -> str:
    destination = f"{dest_lat},{dest_lng}"
    return (
        "https://www.google.com/maps/dir/?api=1"
        f"&destination={quote(destination)}"
        f"&travelmode={quote(travel_mode)}"
        "&dir_action=navigate"
    )

@app.route("/")
def index():
    return render_template("index.html", page_title="Salla Adventure Hub")


@app.route("/planner")
def planner_page():
    return render_template("planner.html", page_title="Trip Planner")


@app.route("/partners")
def partners_page():
    conn = get_db()
    partners = conn.execute("SELECT * FROM partners ORDER BY id DESC").fetchall()
    conn.close()
    return render_template(
        "partners.html",
        page_title="Partners",
        partners=partners,
        services=SERVICES,
    )


@app.route("/register-partner", methods=["GET", "POST"])
def register_partner():
    if request.method == "POST":
        form = request.form
        conn = get_db()
        conn.execute(
            """
            INSERT INTO partners
            (business_name, owner_name, email, partner_type, fee_plan, lat, lng, notes, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                form.get("business_name", "").strip(),
                form.get("owner_name", "").strip(),
                form.get("email", "").strip(),
                form.get("partner_type", "").strip(),
                form.get("fee_plan", "").strip(),
                float(form.get("lat") or 0),
                float(form.get("lng") or 0),
                form.get("notes", "").strip(),
                datetime.utcnow().isoformat(),
            ),
        )
        conn.commit()
        conn.close()
        flash("Partner registration saved for demo review.", "success")
        return redirect(url_for("partners_page"))

    return render_template("register_partner.html", page_title="Register Partner")


@app.route("/map-demo")
def map_demo():
    return render_template(
        "map_demo.html",
        page_title="Salla Map Demo",
        google_maps_api_key=app.config["GOOGLE_MAPS_API_KEY"],
        salla_center=SALLA_CENTER,
        services=SERVICES,
        attractions=ATTRACTIONS,
        alert_phone_number=ALERT_PHONE_NUMBER,
    )


@app.route("/safety")
def safety_page():
    return render_template("safety.html", page_title="Safety Support", services=SERVICES)


@app.route("/about")
def about_page():
    return render_template("about.html", page_title="Architecture & Hackathon Plan")


@app.route("/contact")
def contact_page():
    return render_template("contact.html", page_title="Contact")


@app.route("/api/map-data")
def map_data():
    return jsonify(
        {
            "center": SALLA_CENTER,
            "attractions": ATTRACTIONS,
            "services": SERVICES,
        }
    )


@app.route("/api/services")
def api_services():
    return jsonify({"services": SERVICES, "center": SALLA_CENTER})


@app.route("/api/planner", methods=["POST"])
def api_planner():
    data = request.get_json(force=True)
    budget = int(data.get("budget_eur", 150))
    days = int(data.get("days", 2))
    need_bike = bool(data.get("need_bike", True))
    interests = data.get("interests", [])
    return jsonify(build_trip_plan(budget, days, need_bike, interests))


@app.route("/api/nearest-service", methods=["POST"])
def api_nearest_service():
    data = request.get_json(force=True)
    lat = float(data["lat"])
    lng = float(data["lng"])
    service_type = data.get("service_type", "health")

    nearest = nearest_service(lat, lng, service_type)
    if not nearest:
        return jsonify({"error": "No service found"}), 404

    route = generate_route(lat, lng, nearest["lat"], nearest["lng"])
    travel_mode = "bicycling" if service_type == "bike_repair" else "driving"
    navigation_url = build_google_maps_navigation_url(nearest["lat"], nearest["lng"], travel_mode)

    return jsonify(
        {
            "origin": {"lat": lat, "lng": lng},
            "service": nearest,
            "route": route,
            "navigation_url": navigation_url,
            "logic": {
                "method": "Nearest-service ranking by Haversine distance for demo; swap with Valhalla routing in production.",
                "service_type": service_type,
            },
        }
    )


@app.route("/api/route", methods=["POST"])
def api_route():
    data = request.get_json(force=True)
    origin = data["origin"]
    destination = data["destination"]

    path = generate_route(
        float(origin["lat"]),
        float(origin["lng"]),
        float(destination["lat"]),
        float(destination["lng"]),
    )
    distance_km = haversine_km(
        float(origin["lat"]),
        float(origin["lng"]),
        float(destination["lat"]),
        float(destination["lng"]),
    )

    return jsonify(
        {
            "path": path,
            "distance_km": round(distance_km, 2),
            "duration_sec": round((distance_km / 15) * 3600),
        }
    )


@app.route("/api/register-tracking", methods=["POST"])
def api_register_tracking():
    data = request.get_json(force=True)
    lat = float(data["lat"])
    lng = float(data["lng"])
    alert_type = data.get("alert_type", "health")
    user_name = data.get("user_name", "Tourist")
    vitals = data.get("vitals", {})

    conn = get_db()
    conn.execute(
        "INSERT INTO alerts (alert_type, user_name, lat, lng, vitals_json, created_at) VALUES (?, ?, ?, ?, ?, ?)",
        (alert_type, user_name, lat, lng, str(vitals), datetime.utcnow().isoformat()),
    )
    conn.commit()
    conn.close()

    service_type = "bike_repair" if alert_type == "bike_repair" else "health"
    nearest = nearest_service(lat, lng, service_type)
    route = generate_route(lat, lng, nearest["lat"], nearest["lng"]) if nearest else []

    telegram_message = (
        f"Salla Adventure Hub alert\n"
        f"User: {user_name}\n"
        f"Alert: {alert_type.replace('_', ' ')}\n"
        f"Location: {lat}, {lng}\n"
        f"Nearest service: {nearest['name'] if nearest else 'N/A'}\n"
        f"Service location: {nearest['lat'] if nearest else 'N/A'}, {nearest['lng'] if nearest else 'N/A'}\n"
        f"Vitals: {vitals}"
    )
    telegram_sent = send_telegram_alert(telegram_message)

    return jsonify(
        {
            "status": "received",
            "message": f'{alert_type.replace("_", " ").title()} trigger recorded for demo.',
            "closest_service": nearest,
            "route": route,
            "telegram_sent": telegram_sent,
            "next_action": "Guide user to nearest service; escalate to emergency workflow if user is unresponsive.",
        }
    )

@app.route("/api/location-recommendations")
def api_location_recommendations():
    partner_type = request.args.get("partner_type", "bike_rental")
    suggestions = []

    for item in CANDIDATE_PARTNER_LOCATIONS:
        nearby_count = sum(
            1
            for s in SERVICES
            if haversine_km(item["lat"], item["lng"], s["lat"], s["lng"]) < 2.5
        )
        suggestions.append(
            {
                **item,
                "partner_type": partner_type,
                "nearby_services_within_2_5km": nearby_count,
                "demo_score": round(
                    nearby_count * 1.6 + (2 if "Resort" in item["name"] else 1),
                    1,
                ),
            }
        )

    suggestions.sort(key=lambda x: x["demo_score"], reverse=True)
    return jsonify({"partner_type": partner_type, "suggestions": suggestions})


@app.errorhandler(404)
def not_found(_):
    return render_template("404.html", page_title="Page Not Found"), 404


if __name__ == "__main__":
    init_db()
    app.run(debug=True)
    
