# /// script
# requires-python = "==3.11.*"
# dependencies = [
#   "codewords-client==0.4.0",
#   "fastapi==0.116.1"
# ]
# [tool.env-checker]
# env_vars = [
#   "PORT=8000",
#   "LOGLEVEL=INFO",
#   "CODEWORDS_API_KEY",
#   "CODEWORDS_RUNTIME_URI"
# ]
# ///

from typing import Literal, Optional

from codewords_client import logger, run_service
from fastapi import FastAPI
from pydantic import BaseModel, Field


def generate_study_area_map_html(
    title: str,
    api_base_url: str,
    subcities_endpoint: str,
    basemap: str,
    inset_position: str,
    initial_zoom: int,
    center_lat: float,
    center_lon: float
) -> str:
    """Generate complete HTML page with advanced Study Area Map Generator."""
    logger.info("STEPLOG START generate_study_area_map")
    logger.info("Generating Study Area Map", title=title, basemap=basemap)
    
    # Determine basemap tile layer based on selection
    if basemap == "osm":
        tile_url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        tile_attr = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        tile_max_zoom = 19
    elif basemap == "cartodb":
        tile_url = "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png"
        tile_attr = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>'
        tile_max_zoom = 20
    else:  # esri_imagery
        tile_url = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
        tile_attr = 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
        tile_max_zoom = 18
    
    return f'''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{title}</title>
    
    <!-- Leaflet CSS -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css" />
    
    <style>
        * {{
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }}
        
        body {{
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #1a1a1a;
            color: #e0e0e0;
        }}
        
        #header {{
            background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
            padding: 15px 20px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
        }}
        
        h1 {{
            text-align: center;
            color: #ecf0f1;
            font-size: 24px;
            font-weight: 600;
            letter-spacing: 0.5px;
        }}
        
        #map-container {{
            position: relative;
            height: calc(100vh - 90px);
            margin: 0;
        }}
        
        #map {{
            height: 100%;
            width: 100%;
        }}
        
        /* Enhanced Control Panel */
        #control-panel {{
            position: absolute;
            top: 10px;
            left: 10px;
            z-index: 1000;
            background: rgba(26, 26, 26, 0.98);
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.6);
            max-width: 380px;
            max-height: 90vh;
            overflow-y: auto;
        }}
        
        #control-panel::-webkit-scrollbar {{
            width: 8px;
        }}
        
        #control-panel::-webkit-scrollbar-track {{
            background: #2c3e50;
            border-radius: 4px;
        }}
        
        #control-panel::-webkit-scrollbar-thumb {{
            background: #34495e;
            border-radius: 4px;
        }}
        
        .control-section {{
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 1px solid #34495e;
        }}
        
        .control-section:last-child {{
            border-bottom: none;
        }}
        
        .control-section h3 {{
            color: #3498db;
            font-size: 14px;
            margin-bottom: 12px;
            text-transform: uppercase;
            letter-spacing: 1px;
        }}
        
        label {{
            display: block;
            margin-bottom: 6px;
            color: #bdc3c7;
            font-size: 13px;
            font-weight: 500;
        }}
        
        input[type="text"],
        input[type="number"],
        select {{
            width: 100%;
            padding: 10px;
            margin-bottom: 12px;
            border-radius: 4px;
            border: 2px solid #34495e;
            background: #2c3e50;
            color: #ecf0f1;
            font-size: 13px;
            transition: all 0.3s ease;
        }}
        
        input[type="text"]:focus,
        input[type="number"]:focus,
        select:focus {{
            outline: none;
            border-color: #3498db;
            box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
        }}
        
        input[type="radio"] {{
            margin-right: 8px;
        }}
        
        .radio-group {{
            display: flex;
            flex-direction: column;
            gap: 8px;
            margin-bottom: 12px;
        }}
        
        .radio-option {{
            display: flex;
            align-items: center;
            padding: 8px;
            background: #2c3e50;
            border-radius: 4px;
            cursor: pointer;
            transition: background 0.2s;
        }}
        
        .radio-option:hover {{
            background: #34495e;
        }}
        
        .radio-option input[type="radio"]:checked + label {{
            color: #3498db;
            font-weight: 600;
        }}
        
        button {{
            width: 100%;
            padding: 12px;
            background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
            color: #fff;
            border: none;
            border-radius: 4px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            margin-top: 10px;
        }}
        
        button:hover {{
            background: linear-gradient(135deg, #2980b9 0%, #21618c 100%);
            box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
        }}
        
        button:active {{
            transform: translateY(1px);
        }}
        
        .hidden {{
            display: none !important;
        }}
        
        /* Inset Map */
        #inset-map {{
            position: absolute;
            {inset_position.replace('top', 'top: 80px;').replace('bottom', 'bottom: 80px;')} right: 10px;
            width: 200px;
            height: 150px;
            z-index: 900;
            border: 3px solid #2c3e50;
            border-radius: 6px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.7);
            background: #1a1a1a;
        }}
        
        /* North Arrow - Draggable */
        #north-arrow {{
            position: absolute;
            bottom: 160px;
            left: 10px;
            z-index: 1000;
            background: rgba(26, 26, 26, 0.95);
            padding: 12px;
            border-radius: 50%;
            box-shadow: 0 3px 15px rgba(0,0,0,0.6);
            cursor: grab;
            user-select: none;
        }}
        #north-arrow:active {{ cursor: grabbing; }}
        
        /* Toolbar */
        #map-toolbar {{
            position: absolute;
            top: 10px;
            right: 220px;
            z-index: 1000;
            display: flex;
            gap: 4px;
            background: rgba(26,26,26,0.95);
            padding: 6px;
            border-radius: 8px;
            box-shadow: 0 3px 15px rgba(0,0,0,0.6);
        }}
        .tb-btn {{
            width: 36px; height: 36px;
            background: #2c3e50;
            border: 2px solid #34495e;
            border-radius: 4px;
            color: #ecf0f1;
            font-size: 16px;
            cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            transition: all 0.2s;
        }}
        .tb-btn:hover {{ background: #34495e; border-color: #3498db; }}
        .tb-btn.active {{ background: #3498db; border-color: #2980b9; }}
        .tb-btn-sep {{ width: 1px; background: #34495e; margin: 4px 2px; }}
        
        /* Export Modal */
        #export-modal {{
            display: none;
            position: fixed;
            top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.7);
            z-index: 3000;
            align-items: center; justify-content: center;
        }}
        #export-modal.show {{ display: flex; }}
        .export-panel {{
            background: #1a1a1a;
            border: 2px solid #34495e;
            border-radius: 12px;
            padding: 30px;
            max-width: 450px;
            width: 90%;
        }}
        .export-panel h3 {{ color: #3498db; margin-bottom: 20px; font-size: 18px; }}
        .export-row {{ display: flex; gap: 12px; margin-bottom: 14px; }}
        .export-row label {{ flex: 0 0 110px; padding-top: 10px; }}
        .export-row select, .export-row input {{ flex: 1; }}
        .export-actions {{ display: flex; gap: 10px; margin-top: 20px; }}
        .export-actions button {{ flex: 1; }}
        .btn-cancel {{ background: linear-gradient(135deg, #7f8c8d 0%, #95a5a6 100%) !important; }}
        
        /* Legend customization panel */
        #legend-settings {{
            display: none;
            position: absolute;
            top: 0; left: -220px;
            width: 210px;
            background: rgba(26,26,26,0.98);
            border: 2px solid #34495e;
            border-radius: 8px;
            padding: 12px;
            z-index: 1001;
        }}
        #legend-settings.show {{ display: block; }}
        #legend-settings h5 {{ color: #3498db; margin-bottom: 10px; font-size: 12px; }}
        .leg-row {{ display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }}
        .leg-row input[type=color] {{ width: 30px; height: 24px; border: none; cursor: pointer; background: none; }}
        .leg-row input[type=text] {{ flex: 1; padding: 4px 8px; font-size: 11px; }}
        .leg-row select {{ flex: 1; padding: 4px; font-size: 11px; }}
        
        /* Draw mode toggle */
        #draw-mode-toggle {{
            display: none;
            margin-bottom: 12px;
        }}
        #draw-mode-toggle.show {{ display: block; }}
        
        /* Legend - Draggable + Resizable */
        .legend {{
            position: absolute;
            bottom: 20px;
            right: 10px;
            z-index: 1000;
            background: rgba(26, 26, 26, 0.98);
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 3px 15px rgba(0,0,0,0.6);
            min-width: 180px;
            cursor: grab;
            resize: both;
            overflow: auto;
            user-select: none;
        }}
        .legend:active {{ cursor: grabbing; }}
        .legend-header {{ display: flex; justify-content: space-between; align-items: center; }}
        .legend-gear {{ cursor: pointer; color: #7f8c8d; font-size: 14px; transition: color 0.2s; }}
        .legend-gear:hover {{ color: #3498db; }}
        
        .legend h4 {{
            margin: 0 0 10px 0;
            color: #ecf0f1;
            font-size: 13px;
            font-weight: 600;
        }}
        
        .legend-item {{
            display: flex;
            align-items: center;
            margin-bottom: 8px;
        }}
        
        .legend-color {{
            width: 28px;
            height: 18px;
            margin-right: 10px;
            border: 2px solid #34495e;
            border-radius: 3px;
        }}
        
        .legend-label {{
            color: #bdc3c7;
            font-size: 12px;
        }}
        
        /* Grid Labels */
        .grid-label {{
            background: rgba(26, 26, 26, 0.85);
            border: 1px solid #34495e;
            color: #ecf0f1;
            font-size: 10px;
            font-weight: 600;
            padding: 3px 6px;
            border-radius: 3px;
            box-shadow: 0 1px 5px rgba(0,0,0,0.5);
            white-space: nowrap;
        }}
        
        /* Leaflet custom styling */
        .leaflet-control-attribution {{
            background: rgba(26, 26, 26, 0.85) !important;
            color: #bdc3c7 !important;
            font-size: 10px !important;
        }}
        
        .leaflet-control-attribution a {{
            color: #3498db !important;
        }}
        
        .leaflet-bar {{
            background: rgba(26, 26, 26, 0.95) !important;
            border: 2px solid #34495e !important;
        }}
        
        .leaflet-bar a {{
            background-color: #2c3e50 !important;
            color: #ecf0f1 !important;
            border-bottom: 1px solid #34495e !important;
        }}
        
        .leaflet-bar a:hover {{
            background-color: #34495e !important;
        }}
        
        .leaflet-popup-content-wrapper {{
            background: #2c3e50 !important;
            color: #ecf0f1 !important;
        }}
        
        .leaflet-popup-tip {{
            background: #2c3e50 !important;
        }}
        
        /* Loading Indicator */
        .loading {{
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(26, 26, 26, 0.95);
            padding: 20px 30px;
            border-radius: 8px;
            color: #3498db;
            font-weight: 600;
            z-index: 2000;
            box-shadow: 0 4px 20px rgba(0,0,0,0.7);
        }}
    </style>
</head>
<body>
    <div id="header">
        <h1 id="map-title">{title}</h1>
    </div>
    
    <div id="map-container">
        <div id="map"></div>
        
        <!-- Control Panel -->
        <div id="control-panel">
            <!-- Area Selection Section -->
            <div class="control-section">
                <h3>📍 Study Area Selection</h3>
                
                <div class="radio-group">
                    <div class="radio-option">
                        <input type="radio" id="mode-subcity" name="area-mode" value="subcity" checked>
                        <label for="mode-subcity">Subcities (Hawassa)</label>
                    </div>
                    <div class="radio-option">
                        <input type="radio" id="mode-admin" name="area-mode" value="admin">
                        <label for="mode-admin">Administrative Boundaries</label>
                    </div>
                    <div class="radio-option">
                        <input type="radio" id="mode-search" name="area-mode" value="search">
                        <label for="mode-search">Search Location (OSM)</label>
                    </div>
                </div>
                
                <!-- Subcity Selection -->
                <div id="subcity-controls">
                    <label for="subcity-select">Select Subcity:</label>
                    <select id="subcity-select">
                        <option value="">All Subcities</option>
                    </select>
                </div>
                
                <!-- Admin Boundary Selection -->
                <div id="admin-controls" class="hidden">
                    <label for="admin-country">Country:</label>
                    <select id="admin-country">
                        <option value="">Loading...</option>
                    </select>
                    
                    <label for="admin-region">Region/State:</label>
                    <select id="admin-region" disabled>
                        <option value="">Select country first</option>
                    </select>
                    
                    <label for="admin-zone">Zone/Province:</label>
                    <select id="admin-zone" disabled>
                        <option value="">Select region first</option>
                    </select>
                    
                    <label for="admin-district">District/Woreda:</label>
                    <select id="admin-district" disabled>
                        <option value="">Select zone first</option>
                    </select>
                </div>
                
                <!-- OSM Search -->
                <div id="search-controls" class="hidden">
                    <label for="location-search">Search City/Place:</label>
                    <input type="text" id="location-search" placeholder="e.g., Addis Ababa, Ethiopia">
                    <button id="search-btn" type="button">🔍 Search Location</button>
                </div>
            </div>
            
            <!-- Map Customization Section -->
            <div class="control-section">
                <h3>🎨 Map Customization</h3>
                
                <label for="custom-title">Map Title:</label>
                <input type="text" id="custom-title" value="{title}" placeholder="Enter map title">
                
                <label for="basemap-select">Basemap Style:</label>
                <select id="basemap-select">
                    <option value="osm" {'selected' if basemap == 'osm' else ''}>OpenStreetMap</option>
                    <option value="cartodb" {'selected' if basemap == 'cartodb' else ''}>CartoDB Positron</option>
                    <option value="esri_imagery" {'selected' if basemap == 'esri_imagery' else ''}>ESRI World Imagery</option>
                </select>
                
                <label for="zoom-level">Zoom Level: <span id="zoom-value">{initial_zoom}</span></label>
                <input type="range" id="zoom-level" min="1" max="18" value="{initial_zoom}" style="width:100%; margin-bottom:12px;">
                
                <label for="zoom-radius">Zoom Radius (degrees):</label>
                <input type="number" id="zoom-radius" value="0.5" step="0.1" min="0.1" max="5.0">
                
                <label for="inset-position">Inset Map Position:</label>
                <select id="inset-position">
                    <option value="top" {'selected' if inset_position == 'top' else ''}>Top Right</option>
                    <option value="bottom" {'selected' if inset_position == 'bottom' else ''}>Bottom Right</option>
                </select>
            </div>
            
            <!-- Draw Mode Toggle -->
            <div class="control-section" id="draw-mode-toggle">
                <h3>✏️ Draw Mode</h3>
                <div class="radio-group">
                    <div class="radio-option">
                        <input type="radio" id="draw-replace" name="draw-mode" value="replace" checked>
                        <label for="draw-replace">Replace current layers</label>
                    </div>
                    <div class="radio-option">
                        <input type="radio" id="draw-overlay" name="draw-mode" value="overlay">
                        <label for="draw-overlay">Overlay on existing</label>
                    </div>
                </div>
            </div>
            
            <!-- Action Buttons -->
            <button id="apply-settings" type="button">✨ Apply Settings</button>
        </div>
        
        <!-- Toolbar -->
        <div id="map-toolbar">
            <button class="tb-btn" id="tb-pan" title="Pan">✋</button>
            <button class="tb-btn" id="tb-zoom-full" title="Zoom Full Extent">🌍</button>
            <div class="tb-btn-sep"></div>
            <button class="tb-btn" id="tb-draw-marker" title="Draw Point">📍</button>
            <button class="tb-btn" id="tb-draw-polyline" title="Draw Line">📏</button>
            <button class="tb-btn" id="tb-draw-polygon" title="Draw Polygon">⬡</button>
            <button class="tb-btn" id="tb-draw-rect" title="Draw Rectangle">▭</button>
            <button class="tb-btn" id="tb-draw-clear" title="Clear Drawings">🗑️</button>
            <div class="tb-btn-sep"></div>
            <button class="tb-btn" id="tb-export" title="Export Map">📥</button>
        </div>
        
        <!-- Inset Map -->
        <div id="inset-map"></div>
        
        <!-- North Arrow -->
        <div id="north-arrow">
            <svg width="55" height="55" viewBox="0 0 55 55">
                <defs>
                    <linearGradient id="northGradient" x1="0%" y1="0%" x2="0%" y2="100%">
                        <stop offset="0%" style="stop-color:#e74c3c;stop-opacity:1" />
                        <stop offset="100%" style="stop-color:#c0392b;stop-opacity:1" />
                    </linearGradient>
                </defs>
                <path d="M 27.5 5 L 33 22 L 27.5 20 L 22 22 Z" fill="url(#northGradient)" stroke="#ecf0f1" stroke-width="1.8"/>
                <path d="M 27.5 50 L 33 33 L 27.5 35 L 22 33 Z" fill="#ecf0f1" stroke="#34495e" stroke-width="1.8"/>
                <circle cx="27.5" cy="27.5" r="3.5" fill="#2c3e50" stroke="#ecf0f1" stroke-width="1.8"/>
                <text x="27.5" y="13" text-anchor="middle" fill="#ecf0f1" font-size="9" font-weight="bold">N</text>
            </svg>
        </div>
        
        <!-- Legend with customization -->
        <div class="legend" id="map-legend">
            <div class="legend-header">
                <h4 id="legend-title">Legend</h4>
                <span class="legend-gear" id="legend-gear-btn" title="Customize Legend">⚙️</span>
            </div>
            <div class="legend-item" data-idx="0">
                <div class="legend-color" id="lc-0" style="background: rgba(52, 152, 219, 0.4); border-color: #3498db;"></div>
                <span class="legend-label" id="ll-0">Boundaries</span>
            </div>
            <div class="legend-item" data-idx="1">
                <div class="legend-color" id="lc-1" style="background: rgba(231, 76, 60, 0.6); border-color: #e74c3c;"></div>
                <span class="legend-label" id="ll-1">Selected Area</span>
            </div>
            <div class="legend-item" data-idx="2">
                <div class="legend-color" id="lc-2" style="background: none; border: 2px dashed #3498db;"></div>
                <span class="legend-label" id="ll-2">Coord. Grid</span>
            </div>
            <div class="legend-item" data-idx="3" id="draw-legend" style="display:none;">
                <div class="legend-color" id="lc-3" style="background: rgba(46, 204, 113, 0.4); border-color: #27ae60;"></div>
                <span class="legend-label" id="ll-3">Drawn Features</span>
            </div>
            <!-- Legend Settings Panel -->
            <div id="legend-settings">
                <h5>⚙️ Legend Settings</h5>
                <div class="leg-row">
                    <label style="flex:0 0 40px; font-size:11px;">Font:</label>
                    <select id="leg-font" style="flex:1;">
                        <option value="'Segoe UI', sans-serif">Segoe UI</option>
                        <option value="Arial, sans-serif">Arial</option>
                        <option value="Georgia, serif">Georgia</option>
                        <option value="'Courier New', monospace">Courier</option>
                    </select>
                </div>
                <div class="leg-row">
                    <label style="flex:0 0 40px; font-size:11px;">Size:</label>
                    <select id="leg-size" style="flex:1;">
                        <option value="11px">Small</option>
                        <option value="13px" selected>Medium</option>
                        <option value="15px">Large</option>
                    </select>
                </div>
                <hr style="border-color:#34495e; margin: 8px 0;">
                <div class="leg-row">
                    <input type="color" id="leg-color-0" value="#3498db">
                    <input type="text" id="leg-text-0" value="Boundaries">
                </div>
                <div class="leg-row">
                    <input type="color" id="leg-color-1" value="#e74c3c">
                    <input type="text" id="leg-text-1" value="Selected Area">
                </div>
                <div class="leg-row">
                    <input type="color" id="leg-color-2" value="#3498db">
                    <input type="text" id="leg-text-2" value="Coord. Grid">
                </div>
                <div style="display:flex; gap:6px; margin-top:10px;">
                    <button id="leg-apply" style="padding:6px; font-size:11px; margin:0;">Apply</button>
                    <button id="leg-reset" class="btn-cancel" style="padding:6px; font-size:11px; margin:0;">Reset</button>
                </div>
            </div>
        </div>
        
        <!-- Export Modal -->
        <div id="export-modal">
            <div class="export-panel">
                <h3>📥 Export Cartographic Map</h3>
                <div class="export-row">
                    <label>Paper Size:</label>
                    <select id="exp-paper">
                        <option value="a4">A4 (210×297mm)</option>
                        <option value="a3">A3 (297×420mm)</option>
                        <option value="letter">Letter (216×279mm)</option>
                    </select>
                </div>
                <div class="export-row">
                    <label>Orientation:</label>
                    <select id="exp-orient">
                        <option value="landscape" selected>Landscape</option>
                        <option value="portrait">Portrait</option>
                    </select>
                </div>
                <div class="export-row">
                    <label>Format:</label>
                    <select id="exp-format">
                        <option value="png">PNG Image</option>
                        <option value="pdf">PDF Document</option>
                    </select>
                </div>
                <div class="export-actions">
                    <button id="exp-download">⬇️ Download</button>
                    <button id="exp-cancel" class="btn-cancel">Cancel</button>
                </div>
            </div>
        </div>
    </div>
    
    <!-- Leaflet JS -->
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
    
    <script>
        // Configuration
        const API_BASE_URL = '{api_base_url}';
        const SUBCITIES_ENDPOINT = '{subcities_endpoint}';
        
        // Initialize main map
        const map = L.map('map', {{
            center: [{center_lat}, {center_lon}],
            zoom: {initial_zoom},
            zoomControl: true,
            attributionControl: true
        }});
        
        // Add initial basemap
        let currentBasemap = L.tileLayer('{tile_url}', {{
            attribution: '{tile_attr}',
            maxZoom: {tile_max_zoom}
        }}).addTo(map);
        
        // Initialize inset map
        const insetMap = L.map('inset-map', {{
            center: [9.0, 39.0],
            zoom: 5,
            dragging: false,
            zoomControl: false,
            attributionControl: false,
            scrollWheelZoom: false,
            doubleClickZoom: false,
            touchZoom: false
        }});
        
        L.tileLayer('https://{{s}}.basemaps.cartocdn.com/light_all/{{z}}/{{x}}/{{y}}{{r}}.png', {{
            attribution: '',
            maxZoom: 10
        }}).addTo(insetMap);
        
        // Add scale control
        L.control.scale({{
            imperial: true,
            metric: true,
            position: 'bottomleft'
        }}).addTo(map);
        
        // Global state
        let mainLayer = null;
        let gridLayer = null;
        let insetBox = null;
        let countryBoundary = null;
        let currentMode = 'subcity';
        
        // UTM conversion functions
        function latToUTM(lat, lng) {{
            const k0 = 0.9996;
            const a = 6378137;
            const lat0 = 0;
            const latRad = lat * Math.PI / 180;
            const N = a / Math.sqrt(1 - 0.00669438 * Math.sin(latRad) * Math.sin(latRad));
            const y = k0 * N * (latRad - lat0);
            return Math.round(y + 10000000);
        }}
        
        function lngToUTM(lng, lat) {{
            const k0 = 0.9996;
            const a = 6378137;
            const lng0 = 39;
            const latRad = lat * Math.PI / 180;
            const lngRad = lng * Math.PI / 180;
            const lng0Rad = lng0 * Math.PI / 180;
            const N = a / Math.sqrt(1 - 0.00669438 * Math.sin(latRad) * Math.sin(latRad));
            const x = k0 * N * (lngRad - lng0Rad) * Math.cos(latRad);
            return Math.round(x + 500000);
        }}
        
        // Create 4-side coordinate grid
        function createGrid(bounds) {{
            if (gridLayer) {{
                map.removeLayer(gridLayer);
            }}
            
            gridLayer = L.layerGroup().addTo(map);
            
            const padding = 0.01;
            const minLat = bounds.getSouth() - padding;
            const maxLat = bounds.getNorth() + padding;
            const minLng = bounds.getWest() - padding;
            const maxLng = bounds.getEast() + padding;
            
            // Smart spacing based on extent
            const latExtent = maxLat - minLat;
            const lngExtent = maxLng - minLng;
            const numLines = 6;
            const latSpacing = latExtent / numLines;
            const lngSpacing = lngExtent / numLines;
            
            // Draw latitude lines with labels on left and right
            for (let i = 0; i <= numLines; i++) {{
                const lat = minLat + (i * latSpacing);
                
                L.polyline(
                    [[lat, minLng], [lat, maxLng]],
                    {{ color: '#3498db', weight: 1, opacity: 0.5, dashArray: '5, 5' }}
                ).addTo(gridLayer);
                
                const utmY = latToUTM(lat, (minLng + maxLng) / 2);
                
                // Left label
                L.marker([lat, minLng], {{
                    icon: L.divIcon({{
                        className: 'grid-label',
                        html: `${{lat.toFixed(4)}}° / ${{utmY}}m N`,
                        iconSize: [140, 20],
                        iconAnchor: [-5, 10]
                    }})
                }}).addTo(gridLayer);
                
                // Right label
                L.marker([lat, maxLng], {{
                    icon: L.divIcon({{
                        className: 'grid-label',
                        html: `${{lat.toFixed(4)}}° / ${{utmY}}m N`,
                        iconSize: [140, 20],
                        iconAnchor: [145, 10]
                    }})
                }}).addTo(gridLayer);
            }}
            
            // Draw longitude lines with labels on top and bottom
            for (let i = 0; i <= numLines; i++) {{
                const lng = minLng + (i * lngSpacing);
                
                L.polyline(
                    [[minLat, lng], [maxLat, lng]],
                    {{ color: '#3498db', weight: 1, opacity: 0.5, dashArray: '5, 5' }}
                ).addTo(gridLayer);
                
                const utmX = lngToUTM(lng, (minLat + maxLat) / 2);
                
                // Bottom label
                L.marker([minLat, lng], {{
                    icon: L.divIcon({{
                        className: 'grid-label',
                        html: `${{lng.toFixed(4)}}° / ${{utmX}}m E`,
                        iconSize: [140, 20],
                        iconAnchor: [70, -5]
                    }})
                }}).addTo(gridLayer);
                
                // Top label
                L.marker([maxLat, lng], {{
                    icon: L.divIcon({{
                        className: 'grid-label',
                        html: `${{lng.toFixed(4)}}° / ${{utmX}}m E`,
                        iconSize: [140, 20],
                        iconAnchor: [70, 25]
                    }})
                }}).addTo(gridLayer);
            }}
        }}
        
        // Update inset map box
        function updateInsetBox(bounds) {{
            if (insetBox) {{
                insetMap.removeLayer(insetBox);
            }}
            
            const bbox = [
                [bounds.getSouth(), bounds.getWest()],
                [bounds.getNorth(), bounds.getEast()]
            ];
            
            insetBox = L.rectangle(bbox, {{
                color: '#e74c3c',
                weight: 3,
                fillColor: '#e74c3c',
                fillOpacity: 0.3
            }}).addTo(insetMap);
            
            insetMap.fitBounds(bbox, {{ padding: [10, 10] }});
        }}
        
        // Style features
        function styleFeature(feature, isSelected = false) {{
            return {{
                fillColor: isSelected ? '#e74c3c' : '#3498db',
                weight: 2,
                opacity: 1,
                color: isSelected ? '#c0392b' : '#2980b9',
                fillOpacity: isSelected ? 0.6 : 0.4
            }};
        }}
        
        // Load subcities
        async function loadSubcities() {{
            try {{
                const response = await fetch(SUBCITIES_ENDPOINT);
                const data = await response.json();
                
                if (mainLayer) map.removeLayer(mainLayer);
                
                mainLayer = L.geoJSON(data, {{
                    style: feature => styleFeature(feature),
                    onEachFeature: (feature, layer) => {{
                        if (feature.properties && feature.properties.name) {{
                            layer.bindPopup(`<strong>${{feature.properties.name}}</strong>`);
                            layer.on('click', () => {{
                                document.getElementById('subcity-select').value = feature.properties.name;
                                selectSubcity(feature.properties.name);
                            }});
                        }}
                    }}
                }}).addTo(map);
                
                map.fitBounds(mainLayer.getBounds(), {{ padding: [50, 50] }});
                
                // Populate dropdown
                const select = document.getElementById('subcity-select');
                select.innerHTML = '<option value="">All Subcities</option>';
                data.features.forEach(f => {{
                    const opt = document.createElement('option');
                    opt.value = f.properties.name;
                    opt.textContent = f.properties.name;
                    select.appendChild(opt);
                }});
            }} catch (error) {{
                console.error('Error loading subcities:', error);
                alert('Failed to load subcities. Please check the API.');
            }}
        }}
        
        // Select subcity
        function selectSubcity(name) {{
            if (!mainLayer) return;
            
            mainLayer.setStyle(feature => styleFeature(feature, feature.properties.name === name));
            
            if (name) {{
                mainLayer.eachLayer(layer => {{
                    if (layer.feature.properties.name === name) {{
                        const bounds = layer.getBounds();
                        map.fitBounds(bounds, {{ padding: [80, 80] }});
                        createGrid(bounds);
                        updateInsetBox(bounds);
                        layer.openPopup();
                    }}
                }});
            }} else {{
                if (gridLayer) map.removeLayer(gridLayer);
                map.fitBounds(mainLayer.getBounds(), {{ padding: [50, 50] }});
            }}
        }}
        
        // Load admin levels
        async function loadAdminLevels(level, parentCode = null) {{
            try {{
                const url = `${{API_BASE_URL}}/admin_levels.php?level=${{level}}${{parentCode ? '&parent_code=' + parentCode : ''}}`;
                const response = await fetch(url);
                const data = await response.json();
                return data.data || [];
            }} catch (error) {{
                console.error('Error loading admin levels:', error);
                return [];
            }}
        }}
        
        // Load admin boundaries
        async function loadAdminBoundaries(level, code) {{
            try {{
                const url = `${{API_BASE_URL}}/admin_boundaries.php?level=${{level}}&code=${{code}}&dissolve=true`;
                const response = await fetch(url);
                const data = await response.json();
                
                if (mainLayer) map.removeLayer(mainLayer);
                if (gridLayer) map.removeLayer(gridLayer);
                
                mainLayer = L.geoJSON(data, {{
                    style: () => styleFeature({{}}, true),
                    onEachFeature: (feature, layer) => {{
                        if (feature.properties && feature.properties.name) {{
                            layer.bindPopup(`<strong>${{feature.properties.name}}</strong>`);
                        }}
                    }}
                }}).addTo(map);
                
                const bounds = mainLayer.getBounds();
                map.fitBounds(bounds, {{ padding: [80, 80] }});
                createGrid(bounds);
                updateInsetBox(bounds);
                
                // Load country boundary for inset
                loadCountryBoundary(code);
            }} catch (error) {{
                console.error('Error loading boundaries:', error);
                alert('Failed to load boundaries. Please check selection.');
            }}
        }}
        
        // Load country boundary for inset map
        async function loadCountryBoundary(code) {{
            try {{
                const countryCode = code.substring(0, 2);
                const url = `${{API_BASE_URL}}/admin_boundaries.php?level=0&code=${{countryCode}}&dissolve=true`;
                const response = await fetch(url);
                const data = await response.json();
                
                if (countryBoundary) insetMap.removeLayer(countryBoundary);
                
                countryBoundary = L.geoJSON(data, {{
                    style: {{ color: '#95a5a6', weight: 2, fillColor: '#ecf0f1', fillOpacity: 0.2 }}
                }}).addTo(insetMap);
            }} catch (error) {{
                console.error('Error loading country boundary:', error);
            }}
        }}
        
        // OSM Nominatim search
        async function searchLocation(query) {{
            try {{
                const url = `https://nominatim.openstreetmap.org/search?format=json&q=${{encodeURIComponent(query)}}`;
                const response = await fetch(url);
                const data = await response.json();
                
                if (data.length > 0) {{
                    const result = data[0];
                    const lat = parseFloat(result.lat);
                    const lon = parseFloat(result.lon);
                    const radius = parseFloat(document.getElementById('zoom-radius').value);
                    
                    const bounds = [
                        [lat - radius, lon - radius],
                        [lat + radius, lon + radius]
                    ];
                    
                    if (mainLayer) map.removeLayer(mainLayer);
                    
                    map.fitBounds(bounds, {{ padding: [80, 80] }});
                    createGrid(L.latLngBounds(bounds));
                    updateInsetBox(L.latLngBounds(bounds));
                    
                    L.marker([lat, lon], {{
                        icon: L.divIcon({{
                            className: '',
                            html: '<div style="background:#e74c3c; width:20px; height:20px; border-radius:50%; border:3px solid #fff;"></div>',
                            iconSize: [20, 20],
                            iconAnchor: [10, 10]
                        }})
                    }}).addTo(map).bindPopup(`<strong>${{result.display_name}}</strong>`).openPopup();
                }} else {{
                    alert('Location not found. Try a different search term.');
                }}
            }} catch (error) {{
                console.error('Error searching location:', error);
                alert('Search failed. Please try again.');
            }}
        }}
        
        // Event listeners
        document.querySelectorAll('input[name="area-mode"]').forEach(radio => {{
            radio.addEventListener('change', (e) => {{
                currentMode = e.target.value;
                
                document.getElementById('subcity-controls').classList.toggle('hidden', currentMode !== 'subcity');
                document.getElementById('admin-controls').classList.toggle('hidden', currentMode !== 'admin');
                document.getElementById('search-controls').classList.toggle('hidden', currentMode !== 'search');
                
                if (currentMode === 'subcity') {{
                    loadSubcities();
                }}
            }});
        }});
        
        document.getElementById('subcity-select').addEventListener('change', (e) => {{
            selectSubcity(e.target.value);
        }});
        
        // Admin cascading dropdowns
        document.getElementById('admin-country').addEventListener('change', async (e) => {{
            const code = e.target.value;
            const regionSelect = document.getElementById('admin-region');
            
            if (code) {{
                const regions = await loadAdminLevels(1, code);
                regionSelect.innerHTML = '<option value="">Select region</option>';
                regions.forEach(r => {{
                    const opt = document.createElement('option');
                    opt.value = r.code;
                    opt.textContent = r.name;
                    regionSelect.appendChild(opt);
                }});
                regionSelect.disabled = false;
                
                // Load country boundary
                loadAdminBoundaries(0, code);
            }} else {{
                regionSelect.disabled = true;
                regionSelect.innerHTML = '<option value="">Select country first</option>';
            }}
        }});
        
        document.getElementById('admin-region').addEventListener('change', async (e) => {{
            const code = e.target.value;
            const zoneSelect = document.getElementById('admin-zone');
            
            if (code) {{
                const zones = await loadAdminLevels(2, code);
                zoneSelect.innerHTML = '<option value="">Select zone</option>';
                zones.forEach(z => {{
                    const opt = document.createElement('option');
                    opt.value = z.code;
                    opt.textContent = z.name;
                    zoneSelect.appendChild(opt);
                }});
                zoneSelect.disabled = false;
                
                loadAdminBoundaries(1, code);
            }} else {{
                zoneSelect.disabled = true;
                zoneSelect.innerHTML = '<option value="">Select region first</option>';
            }}
        }});
        
        document.getElementById('admin-zone').addEventListener('change', async (e) => {{
            const code = e.target.value;
            const districtSelect = document.getElementById('admin-district');
            
            if (code) {{
                const districts = await loadAdminLevels(3, code);
                districtSelect.innerHTML = '<option value="">Select district</option>';
                districts.forEach(d => {{
                    const opt = document.createElement('option');
                    opt.value = d.code;
                    opt.textContent = d.name;
                    districtSelect.appendChild(opt);
                }});
                districtSelect.disabled = false;
                
                loadAdminBoundaries(2, code);
            }} else {{
                districtSelect.disabled = true;
                districtSelect.innerHTML = '<option value="">Select zone first</option>';
            }}
        }});
        
        document.getElementById('admin-district').addEventListener('change', (e) => {{
            const code = e.target.value;
            if (code) {{
                loadAdminBoundaries(3, code);
            }}
        }});
        
        // Search button
        document.getElementById('search-btn').addEventListener('click', () => {{
            const query = document.getElementById('location-search').value;
            if (query) {{
                searchLocation(query);
            }}
        }});
        
        document.getElementById('location-search').addEventListener('keypress', (e) => {{
            if (e.key === 'Enter') {{
                document.getElementById('search-btn').click();
            }}
        }});
        
        // Basemap switcher
        document.getElementById('basemap-select').addEventListener('change', (e) => {{
            const basemap = e.target.value;
            
            map.removeLayer(currentBasemap);
            
            if (basemap === 'osm') {{
                currentBasemap = L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
                    attribution: '&copy; OpenStreetMap contributors',
                    maxZoom: 19
                }});
            }} else if (basemap === 'cartodb') {{
                currentBasemap = L.tileLayer('https://{{s}}.basemaps.cartocdn.com/light_all/{{z}}/{{x}}/{{y}}{{r}}.png', {{
                    attribution: '&copy; OpenStreetMap &copy; CARTO',
                    maxZoom: 20
                }});
            }} else {{
                currentBasemap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{{z}}/{{y}}/{{x}}', {{
                    attribution: 'Tiles &copy; Esri',
                    maxZoom: 18
                }});
            }}
            
            currentBasemap.addTo(map);
        }});
        
        // Zoom level slider
        document.getElementById('zoom-level').addEventListener('input', (e) => {{
            const zoom = parseInt(e.target.value);
            document.getElementById('zoom-value').textContent = zoom;
            map.setZoom(zoom);
        }});
        
        // Custom title
        document.getElementById('custom-title').addEventListener('input', (e) => {{
            document.getElementById('map-title').textContent = e.target.value;
        }});
        
        // Inset position
        document.getElementById('inset-position').addEventListener('change', (e) => {{
            const pos = e.target.value;
            const inset = document.getElementById('inset-map');
            
            if (pos === 'top') {{
                inset.style.top = '80px';
                inset.style.bottom = 'auto';
            }} else {{
                inset.style.bottom = '80px';
                inset.style.top = 'auto';
            }}
        }});
        
        // ============ DRAGGABLE ELEMENTS ============
        function makeDraggable(el) {{
            let isDragging = false, startX, startY, startLeft, startTop;
            el.addEventListener('mousedown', (e) => {{
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT' || e.target.tagName === 'BUTTON') return;
                isDragging = true;
                startX = e.clientX;
                startY = e.clientY;
                const rect = el.getBoundingClientRect();
                startLeft = rect.left;
                startTop = rect.top;
                el.style.position = 'fixed';
                e.preventDefault();
            }});
            document.addEventListener('mousemove', (e) => {{
                if (!isDragging) return;
                el.style.left = (startLeft + e.clientX - startX) + 'px';
                el.style.top = (startTop + e.clientY - startY) + 'px';
                el.style.right = 'auto';
                el.style.bottom = 'auto';
            }});
            document.addEventListener('mouseup', () => {{ isDragging = false; }});
        }}
        
        makeDraggable(document.getElementById('north-arrow'));
        makeDraggable(document.getElementById('map-legend'));
        
        // ============ DRAW TOOLS (Leaflet.draw) ============
        const drawnItems = new L.FeatureGroup().addTo(map);
        let activeDrawHandler = null;
        let drawMode = 'replace';
        
        const drawHandlers = {{
            marker: new L.Draw.Marker(map),
            polyline: new L.Draw.Polyline(map, {{ shapeOptions: {{ color: '#2ecc71', weight: 3 }} }}),
            polygon: new L.Draw.Polygon(map, {{ shapeOptions: {{ color: '#2ecc71', weight: 2, fillOpacity: 0.3 }} }}),
            rectangle: new L.Draw.Rectangle(map, {{ shapeOptions: {{ color: '#2ecc71', weight: 2, fillOpacity: 0.3 }} }})
        }};
        
        function startDraw(type) {{
            if (activeDrawHandler) activeDrawHandler.disable();
            document.querySelectorAll('.tb-btn').forEach(b => b.classList.remove('active'));
            document.getElementById('draw-mode-toggle').classList.add('show');
            document.getElementById('draw-legend').style.display = 'flex';
            activeDrawHandler = drawHandlers[type];
            activeDrawHandler.enable();
            document.getElementById('tb-draw-' + (type === 'rectangle' ? 'rect' : type)).classList.add('active');
        }}
        
        map.on(L.Draw.Event.CREATED, (e) => {{
            const layer = e.layer;
            drawMode = document.querySelector('input[name="draw-mode"]:checked').value;
            
            if (drawMode === 'replace') {{
                drawnItems.clearLayers();
                if (mainLayer) map.removeLayer(mainLayer);
            }}
            
            drawnItems.addLayer(layer);
            const bounds = drawnItems.getBounds();
            if (bounds.isValid()) {{
                map.fitBounds(bounds, {{ padding: [80, 80] }});
                createGrid(bounds);
                updateInsetBox(bounds);
            }}
        }});
        
        // Toolbar handlers
        document.getElementById('tb-pan').addEventListener('click', () => {{
            if (activeDrawHandler) activeDrawHandler.disable();
            activeDrawHandler = null;
            document.querySelectorAll('.tb-btn').forEach(b => b.classList.remove('active'));
            document.getElementById('tb-pan').classList.add('active');
        }});
        
        document.getElementById('tb-zoom-full').addEventListener('click', () => {{
            const allBounds = L.latLngBounds([]);
            if (mainLayer) allBounds.extend(mainLayer.getBounds());
            if (drawnItems.getLayers().length > 0) allBounds.extend(drawnItems.getBounds());
            if (allBounds.isValid()) map.fitBounds(allBounds, {{ padding: [50, 50] }});
        }});
        
        document.getElementById('tb-draw-marker').addEventListener('click', () => startDraw('marker'));
        document.getElementById('tb-draw-polyline').addEventListener('click', () => startDraw('polyline'));
        document.getElementById('tb-draw-polygon').addEventListener('click', () => startDraw('polygon'));
        document.getElementById('tb-draw-rect').addEventListener('click', () => startDraw('rectangle'));
        
        document.getElementById('tb-draw-clear').addEventListener('click', () => {{
            drawnItems.clearLayers();
            if (activeDrawHandler) activeDrawHandler.disable();
            activeDrawHandler = null;
            document.querySelectorAll('.tb-btn').forEach(b => b.classList.remove('active'));
            document.getElementById('draw-legend').style.display = 'none';
        }});
        
        // ============ LEGEND CUSTOMIZATION ============
        const defaultLegend = {{
            colors: ['#3498db', '#e74c3c', '#3498db'],
            labels: ['Boundaries', 'Selected Area', 'Coord. Grid'],
            font: "'Segoe UI', sans-serif",
            size: '13px'
        }};
        
        document.getElementById('legend-gear-btn').addEventListener('click', (e) => {{
            e.stopPropagation();
            document.getElementById('legend-settings').classList.toggle('show');
        }});
        
        document.getElementById('leg-apply').addEventListener('click', () => {{
            const legend = document.getElementById('map-legend');
            legend.style.fontFamily = document.getElementById('leg-font').value;
            legend.style.fontSize = document.getElementById('leg-size').value;
            
            for (let i = 0; i < 3; i++) {{
                const color = document.getElementById('leg-color-' + i).value;
                const text = document.getElementById('leg-text-' + i).value;
                document.getElementById('lc-' + i).style.borderColor = color;
                if (i < 2) document.getElementById('lc-' + i).style.background = color + '66';
                document.getElementById('ll-' + i).textContent = text;
            }}
            document.getElementById('legend-settings').classList.remove('show');
        }});
        
        document.getElementById('leg-reset').addEventListener('click', () => {{
            document.getElementById('leg-font').value = defaultLegend.font;
            document.getElementById('leg-size').value = defaultLegend.size;
            const legend = document.getElementById('map-legend');
            legend.style.fontFamily = defaultLegend.font;
            legend.style.fontSize = defaultLegend.size;
            
            for (let i = 0; i < 3; i++) {{
                document.getElementById('leg-color-' + i).value = defaultLegend.colors[i];
                document.getElementById('leg-text-' + i).value = defaultLegend.labels[i];
                document.getElementById('lc-' + i).style.borderColor = defaultLegend.colors[i];
                if (i < 2) document.getElementById('lc-' + i).style.background = defaultLegend.colors[i] + '66';
                else document.getElementById('lc-' + i).style.border = '2px dashed ' + defaultLegend.colors[i];
                document.getElementById('ll-' + i).textContent = defaultLegend.labels[i];
            }}
            document.getElementById('legend-settings').classList.remove('show');
        }});
        
        // ============ EXPORT FUNCTIONALITY ============
        document.getElementById('tb-export').addEventListener('click', () => {{
            document.getElementById('export-modal').classList.add('show');
        }});
        
        document.getElementById('exp-cancel').addEventListener('click', () => {{
            document.getElementById('export-modal').classList.remove('show');
        }});
        
        document.getElementById('exp-download').addEventListener('click', async () => {{
            const format = document.getElementById('exp-format').value;
            const paper = document.getElementById('exp-paper').value;
            const orient = document.getElementById('exp-orient').value;
            
            // Hide controls during capture
            const controlPanel = document.getElementById('control-panel');
            const toolbar = document.getElementById('map-toolbar');
            const modal = document.getElementById('export-modal');
            controlPanel.style.display = 'none';
            toolbar.style.display = 'none';
            modal.classList.remove('show');
            
            // Wait for DOM update
            await new Promise(r => setTimeout(r, 500));
            
            try {{
                const mapContainer = document.getElementById('map-container');
                const canvas = await html2canvas(mapContainer, {{
                    useCORS: true,
                    allowTaint: true,
                    scale: 2,
                    logging: false,
                    backgroundColor: '#1a1a1a'
                }});
                
                if (format === 'png') {{
                    const link = document.createElement('a');
                    link.download = 'study_area_map.png';
                    link.href = canvas.toDataURL('image/png');
                    link.click();
                }} else {{
                    const {{ jsPDF }} = window.jspdf;
                    const sizes = {{ a4: [297, 210], a3: [420, 297], letter: [279.4, 215.9] }};
                    const [w, h] = orient === 'landscape' ? sizes[paper] : [sizes[paper][1], sizes[paper][0]];
                    const pdf = new jsPDF({{ orientation: orient, unit: 'mm', format: paper }});
                    
                    const title = document.getElementById('map-title').textContent;
                    pdf.setFontSize(16);
                    pdf.setTextColor(44, 62, 80);
                    pdf.text(title, w / 2, 12, {{ align: 'center' }});
                    
                    // Draw border frame
                    pdf.setDrawColor(44, 62, 80);
                    pdf.setLineWidth(0.5);
                    pdf.rect(5, 5, w - 10, h - 10);
                    
                    // Add map image
                    const imgData = canvas.toDataURL('image/png');
                    const imgW = w - 20;
                    const imgH = (canvas.height / canvas.width) * imgW;
                    pdf.addImage(imgData, 'PNG', 10, 18, imgW, Math.min(imgH, h - 30));
                    
                    pdf.save('study_area_map.pdf');
                }}
            }} catch (error) {{
                console.error('Export failed:', error);
                alert('Export failed. Please try again.');
            }}
            
            // Restore controls
            controlPanel.style.display = '';
            toolbar.style.display = '';
        }});
        
        // ============ CLICK-TO-ZOOM ON LAYERS ============
        map.on('click', (e) => {{
            if (activeDrawHandler) return; // Don't zoom while drawing
            let clickedLayer = null;
            if (mainLayer) {{
                mainLayer.eachLayer((layer) => {{
                    if (layer.getBounds && layer.getBounds().contains(e.latlng)) {{
                        clickedLayer = layer;
                    }}
                }});
            }}
            if (clickedLayer) {{
                const bounds = clickedLayer.getBounds();
                map.fitBounds(bounds, {{ padding: [80, 80] }});
                createGrid(bounds);
                updateInsetBox(bounds);
            }}
        }});
        
        // Initialize
        (async () => {{
            // Load countries for admin dropdown
            const countries = await loadAdminLevels(0);
            const countrySelect = document.getElementById('admin-country');
            countrySelect.innerHTML = '<option value="">Select country</option>';
            countries.forEach(c => {{
                const opt = document.createElement('option');
                opt.value = c.code;
                opt.textContent = c.name;
                countrySelect.appendChild(opt);
            }});
            
            // Load default view (subcities)
            loadSubcities();
        }})();
    </script>
</body>
</html>
'''

# -------------------------
# FastAPI Application
# -------------------------
app = FastAPI(
    title="Study Area Map Generator",
    description="Professional GIS tool for generating study area maps with admin boundaries, OSM search, inset maps, and advanced cartographic features.",
    version="3.0.0",
)

class MapConfigRequest(BaseModel):
    title: str = Field(
        default="Study Area Cartographic Map",
        description="Map title to display in the header",
        example="Hawassa Region Study Area"
    )
    api_base_url: str = Field(
        default="http://196.191.180.142/gie/api",
        description="Base URL for API endpoints (admin_levels.php and admin_boundaries.php)",
        example="http://196.191.180.142/gie/api"
    )
    subcities_endpoint: str = Field(
        default="http://196.191.180.142/gie/api/subcities.php",
        description="API endpoint for subcities GeoJSON data",
        example="http://196.191.180.142/gie/api/subcities.php"
    )
    basemap: Literal["osm", "cartodb", "esri_imagery"] = Field(
        default="cartodb",
        description="Basemap style: osm (OpenStreetMap), cartodb (CartoDB Positron), esri_imagery (ESRI World Imagery)",
        json_schema_extra={"enum": ["osm", "cartodb", "esri_imagery"]}
    )
    inset_position: Literal["top", "bottom"] = Field(
        default="bottom",
        description="Position of the inset map: top (top-right) or bottom (bottom-right)",
        json_schema_extra={"enum": ["top", "bottom"]}
    )
    initial_zoom: int = Field(
        default=12,
        description="Initial zoom level for the map (1-18)",
        ge=1,
        le=18,
        example=12
    )
    center_lat: float = Field(
        default=7.0626,
        description="Initial map center latitude (WGS84 decimal degrees)",
        example=7.0626
    )
    center_lon: float = Field(
        default=38.4767,
        description="Initial map center longitude (WGS84 decimal degrees)",
        example=38.4767
    )

class MapResponse(BaseModel):
    html_page: str = Field(..., description="Complete HTML page with interactive Study Area Map Generator")
    message: str = Field(..., description="Status message")

@app.post("/", response_model=MapResponse)
async def generate_study_area_map(request: MapConfigRequest):
    """
    Generate a professional Study Area Map with advanced cartographic features.

    **Key Features:**
    - **Multi-source area selection**: Subcities, Administrative boundaries (Country/Region/Zone/District), or OSM location search
    - **Interactive controls**: Dropdown selectors, search box, basemap switcher, zoom radius adjuster
    - **Draw tools**: Point, Line, Polygon, Rectangle drawing with Replace/Overlay toggle
    - **Draggable north arrow**: Reposition by drag on the map
    - **Draggable & resizable legend**: Move and resize legend, customize colors/fonts/labels with Reset
    - **Cartographic elements**: North arrow, scale bar, coordinate grid (4-side labels with WGS84 + UTM), inset map
    - **Customization**: Custom titles, basemap selection (OSM/CartoDB/ESRI), inset position
    - **Map export**: PNG and PDF download with selectable paper size (A4/A3/Letter) and orientation
    - **Click-to-zoom**: Click any layer feature to zoom to its extent
    - **Professional GIS output**: Print-ready cartographic page layout with title, grid, frame, and all map elements

    **Study Area Selection Modes:**
    1. **Subcities**: Select Hawassa subcities from dropdown or click on map
    2. **Administrative Boundaries**: Cascading selection from Country → Region → Zone → District
    3. **OSM Search**: Search any city/location worldwide using OpenStreetMap Nominatim

    **Cartographic Features:**
    - **Coordinate Grid**: Auto-adjusting spacing with dual labels (WGS84 lat/lon + UTM Zone 37N)
    - **Grid Labels**: Positioned on all 4 sides (top, bottom, left, right) with ascending values
    - **North Arrow**: Professional SVG compass rose
    - **Scale Bar**: Metric and imperial units
    - **Inset Map**: Country outline with red box showing study area location (top-right or bottom-right)
    - **Legend**: Color-coded boundary display

    **Basemap Options:**
    - **osm**: OpenStreetMap standard view
    - **cartodb**: CartoDB Positron (clean minimal style)
    - **esri_imagery**: ESRI World Imagery (satellite view)

    **Parameters:**
    - **title**: Map title displayed in header
    - **api_base_url**: Base URL for admin_levels.php and admin_boundaries.php APIs
    - **subcities_endpoint**: URL for subcities GeoJSON API
    - **basemap**: Basemap style selection
    - **inset_position**: Position of inset map (top or bottom right)
    - **initial_zoom**: Starting zoom level (1-18)
    - **center_lat**: Initial center latitude in WGS84
    - **center_lon**: Initial center longitude in WGS84

    **Returns:**
    Complete standalone HTML page that can be deployed anywhere or opened in a browser.
    The page includes all interactive controls for runtime area selection and map customization.
    """
    logger.info(
        "Processing study area map request",
        title=request.title,
        basemap=request.basemap,
        inset_position=request.inset_position
    )

    # Generate the comprehensive HTML page
    html_content = generate_study_area_map_html(
        title=request.title,
        api_base_url=request.api_base_url,
        subcities_endpoint=request.subcities_endpoint,
        basemap=request.basemap,
        inset_position=request.inset_position,
        initial_zoom=request.initial_zoom,
        center_lat=request.center_lat,
        center_lon=request.center_lon
    )

    return MapResponse(
        html_page=html_content,
        message="Study Area Map generated successfully! The HTML includes interactive controls for area selection, basemap switching, and full cartographic features."
    )

if __name__ == "__main__":
    run_service(app)
