from django.shortcuts import render, get_object_or_404
from django.http import Http404
from django.conf import settings
import random
import math
import json
from .services.presets import get_environment_preset
import networkx as nx
from .services.layouts import (
    rectangular_layout,
    circular_layout,
    diagonal_layout,
    triangular_layout,
)
from .services.random_city import generate_random_city_data
from .services.com_con_city import generate_com_con_city_data
from .services.osm_city import generate_osm_city_data, generate_osm_road_midpoints_only



def city_digital_twin(request, city_id, innovation_pct=None, technology_pct=None, science_pct=None):
    try:
        lat = float(request.GET.get('lat', 0))
        long = float(request.GET.get('long', 0))
        scale = float(request.GET.get('scale', 1.0))  # default to 1.0 (normal scale)

        if city_id == "osm_city":
            city_data = generate_osm_city_data(lat, long,scale=scale)
        elif city_id == "com_con":
            city_data = generate_com_con_city_data(lat, long)
        elif city_id == "random_city":
            city_data = generate_random_city_data()
        elif city_id == "dream":
            innovation_pct = innovation_pct or request.GET.get('innovation', 0)
            technology_pct = technology_pct or request.GET.get('technology', 0)
            science_pct = science_pct or request.GET.get('science', 0)

            innovation_pct = int(innovation_pct)
            technology_pct = int(technology_pct)
            science_pct = int(science_pct)

            city_data = generate_random_city_data(innovation_pct, technology_pct, science_pct)
        else:
            city_data = get_city_data(city_id)

        if not city_data:
            city_data = get_example_data()

        preset = get_environment_preset(lat, long)

        context = {
            'city_data': city_data,
            'environment_preset': preset,
            'lat': lat,
            'long': long,
        }
        return render(request, 'pxy_city_digital_twins/city_digital_twin.html', context)
    except (ValueError, TypeError):
        raise Http404("Invalid data provided.")


def get_city_data(city_id):
    # Implement fetching logic here
    # This is a mock function to demonstrate fetching logic
    if str(city_id) == "1" or str(city_id) == "123e4567-e89b-12d3-a456-426614174000":
        return {
            # Real data retrieval logic goes here
        }
    return None

def get_example_data():
    return {
        'buildings': [
            {
                'id': 1,
                'status': 'Occupied',
                'position_x': 0,
                'height': 10,
                'position_z': 0,
                'width': 5,
                'depth': 5,
                'color': '#8a2be2',
                'file': '',  # No file for a simple box representation
            },
            {
                'id': 2,
                'status': 'Vacant',
                'position_x': 10,
                'height': 15,
                'position_z': 10,
                'width': 7,
                'depth': 7,
                'color': '#5f9ea0',
                'file': '',  # No file for a simple box representation
            }
        ],
        'lamps': [
            {
                'id': 1,
                'status': 'Functional',
                'position_x': 3,
                'position_z': 3,
                'height': 4,
                'color': '#ffff00',
            },
            {
                'id': 2,
                'status': 'Broken',
                'position_x': 8,
                'position_z': 8,
                'height': 4,
                'color': '#ff0000',
            }
        ],
        'trees': [
            {
                'id': 1,
                'status': 'Healthy',
                'position_x': 5,
                'position_z': 5,
                'height': 6,
                'radius_bottom': 0.2,
                'radius_top': 1,
                'color_trunk': '#8b4513',
                'color_leaves': '#228b22',
            },
            {
                'id': 2,
                'status': 'Diseased',
                'position_x': 15,
                'position_z': 15,
                'height': 6,
                'radius_bottom': 0.2,
                'radius_top': 1,
                'color_trunk': '#a0522d',
                'color_leaves': '#6b8e23',
            }
        ]
    }

def city_augmented_digital_twin(request, city_id):
    try:
        lat = float(request.GET.get('lat', 0))
        long = float(request.GET.get('long', 0))
        scale = float(request.GET.get('scale', 1.0))  # default to 1.0

        if city_id == "osm_city":
            city_data = generate_osm_road_midpoints_only(lat, long, scale=scale)
        elif city_id == "random_city":
            city_data = generate_random_city_data()
        elif city_id == "gauge_ahead":
            # No city data needed, just render the special AR scene
            return render(request, 'pxy_city_digital_twins/spawn_gauge_ahead.html')
        else:
            raise Http404("Unsupported city_id for AR view")

        preset = get_environment_preset(lat, long)

        context = {
            'city_data': city_data,
            'environment_preset': preset,
            'lat': lat,
            'long': long,
        }
        return render(request, 'pxy_city_digital_twins/city_augmented_digital_twin.html', context)

    except (ValueError, TypeError):
        raise Http404("Invalid parameters provided.")


def city_digital_twin_cesium_ion(request):
    try:
        lat = float(request.GET.get("lat", 0))
        long = float(request.GET.get("long", 0))
    except (ValueError, TypeError):
        raise Http404("Invalid parameters provided.")

    context = {
        "lat": lat,
        "long": long,
        "ion_token": 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkZmM5ZmE1MS0yNWFlLTQ1YjEtOGZiNy1lMmFlOGYyYTI0MjYiLCJpZCI6Mzc0NDg3LCJpYXQiOjE3Njc0MTAxNzN9.tFBLet8CEmvfCivzZM5ExfHL752iPQIShw6Pqa49Gkw',  # settings.CESIUM_ION_TOKEN,
    }
    return render(
        request,
        "pxy_city_digital_twins/city_digital_twin_cesium_ion.html",
        context,
    )


def osm_roads_debug(request):
    try:
        lat = float(request.GET.get("lat", 0))
        long = float(request.GET.get("long", 0))
        scale = float(request.GET.get("scale", 1.0))
    except (ValueError, TypeError):
        raise Http404("Invalid parameters provided.")

    city_data = generate_osm_city_data(lat, long, scale=scale)
    roads_json = json.dumps(city_data.get("roads", []))
    context = {
        "lat": lat,
        "long": long,
        "scale": scale,
        "roads_json": roads_json,
    }
    return render(request, "pxy_city_digital_twins/osm_roads_debug.html", context)


from django.shortcuts import render
from django.http import Http404
from .services.waste_routes import get_dispatch_data_for
import json

def waste_route_debug(request, subdivision, vehicle):
    # 1) load all routes for this subdivision
    dispatch_data = get_dispatch_data_for(subdivision)
    if not dispatch_data:
        return render(request, 'pxy_city_digital_twins/waste_route_default.html', {
            'subdivision': subdivision
        })

    # 2) pick your route by the required `vehicle` path-param
    try:
        selected = next(r for r in dispatch_data if r['route_id'] == vehicle)
    except StopIteration:
        return render(request, 'pxy_city_digital_twins/waste_route_not_found.html', {
            'subdivision': subdivision
        })

    # 3) derive a “center” latitude/longitude from that route:
    coords = selected.get('coords', [])
    if coords:
        avg_lon = sum(pt[0] for pt in coords) / len(coords)
        avg_lat = sum(pt[1] for pt in coords) / len(coords)
    else:
        steps = selected.get('steps', [])
        avg_lon = sum(s['position'][0] for s in steps) / len(steps)
        avg_lat = sum(s['position'][1] for s in steps) / len(steps)

    # 4) generate your OSM‐based city around that center
    city_data = generate_osm_city_data(avg_lat, avg_lon)

    # 5) sanitize building heights (replace NaN or missing with default 10.0)
    default_height = 10.0
    for b in city_data.get('buildings', []):
        h = b.get('height')
        if not isinstance(h, (int, float)) or (isinstance(h, float) and math.isnan(h)):
            b['height'] = default_height

    # 6) render the VR template
    return render(request, 'pxy_city_digital_twins/waste_route_debug.html', {
        'subdivision':         subdivision,
        'vehicle':             vehicle,
        'selected_route_json': json.dumps(selected),
        'city_data':           city_data,
    })



import math
import json
from django.shortcuts import render
from pyproj import Transformer

from .services.osm_city import generate_osm_city_data
from .services.waste_routes import get_dispatch_data_for

def waste_route(request, subdivision, vehicle):
    """
    URL: /waste/<subdivision>/<vehicle>/
    Renders a single vehicle's waste‐collection route in VR,
    overlaid on an OSM‐generated city around the route center.
    """
    # 1) load all routes for this subdivision
    dispatch_data = get_dispatch_data_for(subdivision)
    if not dispatch_data:
        return render(request, 'pxy_city_digital_twins/waste_route_default.html', {
            'subdivision': subdivision
        })

    # 2) pick your route by the required `vehicle` path-param
    try:
        selected = next(r for r in dispatch_data if r['route_id'] == vehicle)
    except StopIteration:
        return render(request, 'pxy_city_digital_twins/waste_route_not_found.html', {
            'subdivision': subdivision
        })

    # 3) derive center lon/lat from coords (or fallback to steps)
    coords = selected.get('coords', [])
    if coords:
        avg_lon = sum(pt[0] for pt in coords) / len(coords)
        avg_lat = sum(pt[1] for pt in coords) / len(coords)
    else:
        steps = selected.get('steps', [])
        avg_lon = sum(s['position'][0] for s in steps) / len(steps)
        avg_lat = sum(s['position'][1] for s in steps) / len(steps)

    # 4) generate your OSM‐based city around that center
    city_data = generate_osm_city_data(avg_lat, avg_lon)

    # 5) sanitize building heights (replace NaN or missing with default 10.0)
    default_height = 10.0
    for b in city_data.get('buildings', []):
        h = b.get('height')
        if not isinstance(h, (int, float)) or (isinstance(h, float) and math.isnan(h)):
            b['height'] = default_height

    # 6) project all coords (and steps) to Web Mercator, recenter them
    transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
    # route
    merc = [transformer.transform(lon, lat) for lon, lat in coords]
    if not merc:
        merc = [transformer.transform(*s['position']) for s in selected.get('steps', [])]
    avg_x = sum(x for x, _ in merc) / len(merc)
    avg_z = sum(z for _, z in merc) / len(merc)
    rel_coords = [[x - avg_x, z - avg_z] for x, z in merc]

    # steps
    step_positions = []
    for step in selected.get('steps', []):
        lon, lat = step['position']
        x, z = transformer.transform(lon, lat)
        step_positions.append({
            'x': x - avg_x,
            'z': z - avg_z,
            'step_id': step.get('id'),
            'step_type': step.get('step_type'),
            'popup': step.get('popup'),
        })

    # 7) render
    return render(request, 'pxy_city_digital_twins/virtual_waste_route.html', {
        'subdivision':         subdivision,
        'vehicle':             vehicle,
        'selected_route_json': json.dumps(selected),
        'city_data':           city_data,
        'rel_coords':          rel_coords,
        'step_positions':      step_positions,
    })

def augmented_waste_route(request, subdivision, vehicle):
    """
    URL: /waste/<subdivision>/<vehicle>/
    Renders a single vehicle's waste‐collection route in VR,
    overlaid on an OSM‐generated city around the route center.
    """
    # 1) load all routes for this subdivision
    dispatch_data = get_dispatch_data_for(subdivision)
    if not dispatch_data:
        return render(request, 'pxy_city_digital_twins/waste_route_default.html', {
            'subdivision': subdivision
        })

    # 2) pick your route by the required `vehicle` path-param
    try:
        selected = next(r for r in dispatch_data if r['route_id'] == vehicle)
    except StopIteration:
        return render(request, 'pxy_city_digital_twins/waste_route_not_found.html', {
            'subdivision': subdivision
        })

    # 3) derive center lon/lat from coords (or fallback to steps)
    coords = selected.get('coords', [])
    if coords:
        avg_lon = sum(pt[0] for pt in coords) / len(coords)
        avg_lat = sum(pt[1] for pt in coords) / len(coords)
    else:
        steps = selected.get('steps', [])
        avg_lon = sum(s['position'][0] for s in steps) / len(steps)
        avg_lat = sum(s['position'][1] for s in steps) / len(steps)

    # 4) generate your OSM‐based city around that center
    city_data = generate_osm_city_data(avg_lat, avg_lon)

    # 5) sanitize building heights (replace NaN or missing with default 10.0)
    default_height = 10.0
    for b in city_data.get('buildings', []):
        h = b.get('height')
        if not isinstance(h, (int, float)) or (isinstance(h, float) and math.isnan(h)):
            b['height'] = default_height

    # 6) project all coords (and steps) to Web Mercator, recenter them
    transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
    # route
    merc = [transformer.transform(lon, lat) for lon, lat in coords]
    if not merc:
        merc = [transformer.transform(*s['position']) for s in selected.get('steps', [])]
    avg_x = sum(x for x, _ in merc) / len(merc)
    avg_z = sum(z for _, z in merc) / len(merc)
    rel_coords = [[x - avg_x, z - avg_z] for x, z in merc]

    # steps
    step_positions = []
    for step in selected.get('steps', []):
        lon, lat = step['position']
        x, z = transformer.transform(lon, lat)
        step_positions.append({
            'x': x - avg_x,
            'z': z - avg_z,
            'step': step
        })

    # 7) render
    return render(request, 'pxy_city_digital_twins/augmented_waste_route.html', {
        'subdivision':         subdivision,
        'vehicle':             vehicle,
        'selected_route_json': json.dumps(selected),
        'city_data':           city_data,
        'rel_coords':          rel_coords,
        'step_positions':      step_positions,
    })


import json
from django.http import JsonResponse, HttpResponseBadRequest
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt   # or use the csrf token client-side
from .models import WastePickup

@csrf_exempt
@require_POST
def record_pickup(request, subdivision, vehicle):
    """
    Expect JSON body:
      { "step_id": "<id>", "quarter": <1-4> }
    """
    try:
        payload = json.loads(request.body)
        step_id = payload['step_id']
        quarter = int(payload['quarter'])
        if quarter not in (1,2,3,4):
            raise ValueError
    except (KeyError, ValueError, json.JSONDecodeError):
        return HttpResponseBadRequest("Invalid payload")

    WastePickup.objects.create(
        subdivision=subdivision,
        vehicle=vehicle,
        step_id=step_id,
        quarter=quarter
    )
    return JsonResponse({"status":"ok"})
