"""
Exploration v3: Based on GIPS framework discovery from map.htm.

Key findings from map.htm:
  - Framework: GIPS (gips.core.js, gips.map.js, etc.)
  - naviKey is a LAYER name like "building_estate"
  - naviType is like "buildinfo"
  - _layerId is like "NAVI_LAYER_127"
  - Data server: map.ntu.edu.tw/ntuga (but ymspace uses local /gisweb)
  - GeoServer: map.ntu.edu.tw/geoserver
  - Building IDs like "A011" with floor "1F"
  - EPSG:3826 coordinate system

This script:
  1. Fetches all JS files to understand the API
  2. Tries naviKey with layer-style names
  3. Explores the gisweb base page
  4. Tries to get the actual route/navigation data
"""
import sys
import os
import json
import time
import re
import urllib3
import requests
from pathlib import Path
from datetime import datetime

sys.stdout.reconfigure(encoding='utf-8')
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://ymspace.ga.nycu.edu.tw/gisweb/public/route.htm"
GISWEB_BASE = "https://ymspace.ga.nycu.edu.tw/gisweb"
OUTPUT_DIR = Path(r"C:\Users\thc1006\Desktop\NQSD\新增資料夾\data\ymmap_archive\route_data\v3")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

SESSION = requests.Session()
SESSION.verify = False
SESSION.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept": "*/*",
    "Referer": "https://ymspace.ga.nycu.edu.tw/gisweb/public/route.htm",
})

DELAY = 0.25
total_requests = 0
hits = []


def req_url(url, label="", params=None):
    """Make a request to a full URL."""
    global total_requests
    total_requests += 1
    try:
        resp = SESSION.get(url, params=params, timeout=15)
        return {
            "status": resp.status_code,
            "content_type": resp.headers.get("Content-Type", ""),
            "length": len(resp.content),
            "text": resp.text,
            "content": resp.content,
        }
    except Exception as e:
        return {"error": str(e)}


def req(params, label=""):
    """Make a request to route.htm."""
    global total_requests
    total_requests += 1
    try:
        resp = SESSION.get(BASE_URL, params=params, timeout=15)
        ct = resp.headers.get("Content-Type", "")

        result = {
            "status": resp.status_code,
            "content_type": ct,
            "length": len(resp.content),
        }

        try:
            data = resp.json()
            result["data"] = data
            result["is_json"] = True
        except (json.JSONDecodeError, ValueError):
            result["data"] = resp.text.strip()
            result["is_json"] = False

        # Check for errors
        is_error = False
        if result["is_json"] and isinstance(result["data"], dict):
            msg = str(result["data"].get("message", "")) + str(result["data"].get("msg", ""))
            if any(x in msg for x in ["Exception", "not met", "not present", "null", "error", "Error", "Incorrect"]):
                is_error = True
            if result["data"].get("success") is False:
                is_error = True

        result["is_error"] = is_error
        result["is_real"] = not is_error and result["length"] > 10

        return result

    except Exception as e:
        return {"error": str(e), "is_real": False, "is_error": True}


def save(filename, data):
    filepath = OUTPUT_DIR / filename
    if isinstance(data, str):
        with open(filepath, "w", encoding="utf-8") as f:
            f.write(data)
    elif isinstance(data, bytes):
        with open(filepath, "wb") as f:
            f.write(data)
    else:
        with open(filepath, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
    return filepath


# ============================================================
# PHASE 1: Fetch and analyze all GIPS JS files
# ============================================================
def phase1_fetch_js():
    print("=" * 70)
    print("PHASE 1: Fetching GIPS framework JavaScript files")
    print("=" * 70)

    js_files = [
        "/gisweb/js/lib/gips/gips.core.js",
        "/gisweb/js/lib/gips/gips.util.js",
        "/gisweb/js/lib/myLib/myLib.core.js",
        "/gisweb/js/lib/gips/map/gips.map.js",
        "/gisweb/js/lib/gips/map/gips.map.geomap.js",
        "/gisweb/js/lib/gips/map/gips.map.gisMapWrapper.js",
        "/gisweb/js/lib/gips/map/gips.map.gisMapConfig.js",
        "/gisweb/js/lib/gips/map/gips.map.gisMapManager.js",
        "/gisweb/js/ux/extra.js",
        "/gisweb/js/lib/OpenLayersPlugin/OpenLayersPlugin.js",
    ]

    all_actions = set()
    all_params = set()
    all_api_urls = set()
    js_analysis = {}

    for js_path in js_files:
        url = f"https://ymspace.ga.nycu.edu.tw{js_path}"
        name = js_path.split("/")[-1]
        print(f"  Fetching {name}...", end=" ")

        result = req_url(url)
        time.sleep(DELAY)

        if result.get("status") == 200 and result.get("length", 0) > 50:
            text = result["text"]
            save(f"js_{name}", text)
            print(f"OK ({len(text)} bytes)")

            # Analyze for API patterns
            actions = re.findall(r'action\s*[:=]\s*["\'](\w+)["\']', text)
            actions2 = re.findall(r'["\']action["\']\s*:\s*["\'](\w+)["\']', text)
            params_found = re.findall(r'(?:naviKey|tableName|layerName|buildId|floorId|roomId|query|schema)\s*[:=]\s*["\']([^"\']+)["\']', text)
            api_urls = re.findall(r'(?:url|URL)\s*[:=]\s*["\']([^"\']*(?:route|navi|public|api)[^"\']*)["\']', text)
            ajax_calls = re.findall(r'\$\.(?:ajax|get|post|getJSON)\s*\([^)]*', text)
            route_refs = re.findall(r'route\.htm[^"\']*', text)
            navi_refs = re.findall(r'navi\w*\s*[:=]\s*["\']([^"\']+)["\']', text)

            all_actions.update(actions)
            all_actions.update(actions2)
            all_params.update(params_found)
            all_api_urls.update(api_urls)

            js_analysis[name] = {
                "size": len(text),
                "actions": list(set(actions + actions2)),
                "params": list(set(params_found)),
                "api_urls": list(set(api_urls)),
                "route_refs": list(set(route_refs)),
                "navi_refs": list(set(navi_refs)),
                "ajax_count": len(ajax_calls),
            }

            if actions or actions2:
                print(f"    Actions: {list(set(actions + actions2))}")
            if params_found:
                print(f"    Params: {list(set(params_found))[:10]}")
            if route_refs:
                print(f"    Route refs: {list(set(route_refs))}")
        else:
            print(f"FAIL (status={result.get('status')})")

    print(f"\n  All discovered actions: {sorted(all_actions)}")
    print(f"  All discovered params: {sorted(all_params)}")
    print(f"  All API URLs: {sorted(all_api_urls)}")

    save("_js_analysis.json", js_analysis)
    save("_discovered_api.json", {
        "actions": sorted(all_actions),
        "params": sorted(all_params),
        "api_urls": sorted(all_api_urls),
    })

    return js_analysis, all_actions, all_params


# ============================================================
# PHASE 2: Fetch the gisweb main page and discover naviKeys
# ============================================================
def phase2_gisweb_pages():
    print("\n" + "=" * 70)
    print("PHASE 2: Exploring gisweb pages for naviKey values")
    print("=" * 70)

    pages_to_fetch = [
        f"{GISWEB_BASE}/",
        f"{GISWEB_BASE}/public/route.htm",
        f"{GISWEB_BASE}/index.htm",
        f"{GISWEB_BASE}/index.html",
        f"{GISWEB_BASE}/index.jsp",
        f"{GISWEB_BASE}/main.htm",
        f"{GISWEB_BASE}/home.htm",
        f"{GISWEB_BASE}/navi.htm",
        f"{GISWEB_BASE}/navi/",
        f"{GISWEB_BASE}/public/navi.htm",
        f"{GISWEB_BASE}/route/",
    ]

    all_navi_keys = set()
    all_layer_ids = set()

    for url in pages_to_fetch:
        name = url.replace(GISWEB_BASE, "").replace("/", "_") or "root"
        print(f"  Fetching {url}...", end=" ")
        result = req_url(url)
        time.sleep(DELAY)

        if result.get("status") == 200 and result.get("length", 0) > 100:
            text = result["text"]
            save(f"page_{name}.html", text)
            print(f"OK ({len(text)} bytes)")

            # Extract naviKey values
            navi_keys = re.findall(r'naviKey\s*[:=]\s*["\']([^"\']+)["\']', text)
            layer_ids = re.findall(r'_layerId\s*[:=]\s*["\']([^"\']+)["\']', text)
            navi_types = re.findall(r'naviType\s*[:=]\s*["\']([^"\']+)["\']', text)
            type_names = re.findall(r'typeName\s*[:=]\s*["\']([^"\']+)["\']', text)

            all_navi_keys.update(navi_keys)
            all_layer_ids.update(layer_ids)

            if navi_keys:
                print(f"    naviKeys: {navi_keys}")
            if layer_ids:
                print(f"    layerIds: {layer_ids}")
            if navi_types:
                print(f"    naviTypes: {navi_types}")
            if type_names:
                print(f"    typeNames: {type_names}")
        else:
            print(f"status={result.get('status', 'ERR')}")

    print(f"\n  All naviKeys: {sorted(all_navi_keys)}")
    print(f"  All layerIds: {sorted(all_layer_ids)}")
    return all_navi_keys, all_layer_ids


# ============================================================
# PHASE 3: Try route.htm with discovered naviKey values
# ============================================================
def phase3_try_navikeys(navi_keys):
    print("\n" + "=" * 70)
    print("PHASE 3: Testing route.htm with naviKey values")
    print("=" * 70)

    # Combine discovered keys with guesses
    keys_to_try = list(navi_keys) + [
        "building_estate", "route", "navi", "indoor", "outdoor",
        "campus", "building", "floor", "room", "node", "edge",
        "ap", "wifi", "path", "network",
        "ym_building", "ym_route", "ym_indoor",
        "NAVI_LAYER_127", "NAVI_LAYER_1", "NAVI_LAYER_2",
        "1", "2", "3", "4", "5",
    ]

    # Remove duplicates preserving order
    seen = set()
    unique_keys = []
    for k in keys_to_try:
        if k not in seen:
            seen.add(k)
            unique_keys.append(k)

    results = {}

    # Test each naviKey with each valid action
    valid_actions = [
        "findGeom", "findAll", "getHeaders", "search",
        "getLayerInfo", "loadImageByApKey",
        "searchByDistance", "getCentroid", "dataTransmissionAPI",
    ]

    for navi_key in unique_keys:
        for action in valid_actions:
            params = {"action": action, "naviKey": navi_key}
            label = f"{action}_naviKey={navi_key}"

            result = req(params, label)
            time.sleep(DELAY)

            if result.get("is_real"):
                data = result.get("data")
                preview = ""
                if isinstance(data, (dict, list)):
                    preview = json.dumps(data, ensure_ascii=False)[:300]
                elif isinstance(data, str):
                    preview = data[:300]
                print(f"  [HIT] {label} (len={result['length']})")
                print(f"         {preview}")
                hits.append(label)
                safe = label.replace("=", "_")[:80]
                save(f"navikey_{safe}.json", data)
                results[label] = {
                    "length": result["length"],
                    "data_preview": preview,
                }
            elif result.get("is_error"):
                data = result.get("data", {})
                msg = ""
                if isinstance(data, dict):
                    msg = str(data.get("message", ""))[:100]
                # Only print interesting errors (not the generic ones)
                if msg and "not met" not in msg and "NullPointerException" not in msg:
                    print(f"  [info] {label}: {msg}")

    save("_navikey_results.json", results)
    return results


# ============================================================
# PHASE 4: Try findAll and search with tableName from the framework
# ============================================================
def phase4_table_discovery():
    print("\n" + "=" * 70)
    print("PHASE 4: Deep table/schema discovery via findAll")
    print("=" * 70)

    results = {}

    # The error "tableName is null" suggests the param name might be different
    # Try sending tableName via different mechanisms
    for tname in ["route", "building", "node", "edge", "navi", "building_estate"]:
        # Try as form-encoded POST
        for action in ["findAll", "getHeaders"]:
            params = {"action": action}
            data_body = {"tableName": tname}

            global total_requests
            total_requests += 1

            try:
                # POST with form data
                resp = SESSION.post(BASE_URL, params=params, data=data_body, timeout=15)
                label = f"POST_{action}_tableName={tname}"
                print(f"  {label}: status={resp.status_code}, len={len(resp.content)}")

                try:
                    jdata = resp.json()
                    msg = jdata.get("message", jdata.get("msg", ""))
                    if msg and "null" not in str(msg).lower() and "not met" not in str(msg):
                        print(f"    Response: {json.dumps(jdata, ensure_ascii=False)[:200]}")
                        if not any(x in str(msg) for x in ["Exception", "error", "Error"]):
                            save(f"post_{action}_{tname}.json", jdata)
                            results[label] = jdata
                except Exception:
                    if len(resp.text) > 20:
                        print(f"    Text: {resp.text[:200]}")
            except Exception as e:
                print(f"  {label}: ERROR {e}")

            time.sleep(DELAY)

    # Try with JSON body
    for tname in ["route", "building", "node", "building_estate"]:
        for action in ["findAll", "getHeaders", "dataTransmissionAPI"]:
            total_requests += 1
            try:
                resp = SESSION.post(
                    BASE_URL,
                    params={"action": action},
                    json={"tableName": tname},
                    timeout=15
                )
                label = f"POST_JSON_{action}_tableName={tname}"
                print(f"  {label}: status={resp.status_code}, len={len(resp.content)}")

                try:
                    jdata = resp.json()
                    msg = str(jdata.get("message", ""))
                    if "null" not in msg.lower() and "not met" not in msg:
                        print(f"    Response: {json.dumps(jdata, ensure_ascii=False)[:200]}")
                except Exception:
                    pass
            except Exception as e:
                print(f"  {label}: ERROR {e}")

            time.sleep(DELAY)

    return results


# ============================================================
# PHASE 5: Try dataTransmissionAPI with query param
# ============================================================
def phase5_dataTransmission():
    print("\n" + "=" * 70)
    print("PHASE 5: dataTransmissionAPI with query parameter")
    print("=" * 70)

    results = {}

    # The error said it needs 'query' param
    queries = [
        # SQL-like queries (common in GIS systems)
        "SELECT * FROM route",
        "SELECT * FROM building",
        "SELECT * FROM node",
        "SELECT * FROM edge",
        "SELECT * FROM navi",
        "SELECT * FROM building_estate",
        "SELECT * FROM layer",
        "SELECT * FROM indoor",
        "route",
        "building",
        "node",
        # Try as naviKey reference
        "building_estate",
        "NAVI_LAYER_127",
        # JSON
        '{"tableName":"route"}',
        '{"tableName":"building"}',
        '{"type":"route"}',
        # Simple queries
        "all",
        "*",
        "1",
    ]

    for query in queries:
        label = f"dataTransmission_query={query[:50]}"
        result = req({"action": "dataTransmissionAPI", "query": query}, label)
        time.sleep(DELAY)

        data = result.get("data", {})
        msg = ""
        if isinstance(data, dict):
            msg = str(data.get("message", ""))[:150]

        if result.get("is_real"):
            preview = json.dumps(data, ensure_ascii=False)[:300] if not isinstance(data, str) else data[:300]
            print(f"  [HIT] query=\"{query}\" -> {preview}")
            safe = re.sub(r'[^\w]', '_', query)[:40]
            save(f"dataTransmission_{safe}.json", data)
            results[query] = data
        else:
            print(f"  query=\"{query[:50]}\" -> {msg}")

    return results


# ============================================================
# PHASE 6: Probe for other controller endpoints
# ============================================================
def phase6_other_controllers():
    print("\n" + "=" * 70)
    print("PHASE 6: Discovering other controller endpoints")
    print("=" * 70)

    results = {}

    # Based on GIPS framework patterns
    endpoints = [
        # Standard GIPS endpoints
        f"{GISWEB_BASE}/public/route.htm",
        f"{GISWEB_BASE}/public/gmap.htm",
        f"{GISWEB_BASE}/public/map.htm",
        f"{GISWEB_BASE}/public/layer.htm",
        f"{GISWEB_BASE}/public/data.htm",
        f"{GISWEB_BASE}/public/navi.htm",
        f"{GISWEB_BASE}/public/building.htm",
        f"{GISWEB_BASE}/public/search.htm",
        f"{GISWEB_BASE}/public/api.htm",
        f"{GISWEB_BASE}/public/geo.htm",
        f"{GISWEB_BASE}/public/indoor.htm",
        f"{GISWEB_BASE}/public/floor.htm",
        f"{GISWEB_BASE}/public/image.htm",
        f"{GISWEB_BASE}/public/upload.htm",
        # Servlet patterns
        f"{GISWEB_BASE}/route",
        f"{GISWEB_BASE}/navi",
        f"{GISWEB_BASE}/building",
        f"{GISWEB_BASE}/layer",
        f"{GISWEB_BASE}/map",
        f"{GISWEB_BASE}/data",
        f"{GISWEB_BASE}/api",
        f"{GISWEB_BASE}/search",
        # Spring MVC patterns
        f"{GISWEB_BASE}/api/route",
        f"{GISWEB_BASE}/api/building",
        f"{GISWEB_BASE}/api/layer",
        f"{GISWEB_BASE}/api/navi",
        f"{GISWEB_BASE}/api/map",
        f"{GISWEB_BASE}/api/data",
        f"{GISWEB_BASE}/api/indoor",
        f"{GISWEB_BASE}/rest/route",
        f"{GISWEB_BASE}/rest/building",
        f"{GISWEB_BASE}/rest/layer",
        # WMS/WFS patterns
        f"{GISWEB_BASE}/wms",
        f"{GISWEB_BASE}/wfs",
        f"{GISWEB_BASE}/ows",
        f"{GISWEB_BASE}/geoserver/wms",
        f"{GISWEB_BASE}/geoserver/wfs",
    ]

    for url in endpoints:
        result = req_url(url)
        time.sleep(DELAY)

        status = result.get("status", 0)
        length = result.get("length", 0)
        ct = result.get("content_type", "")

        if status == 200 and length > 100:
            name = url.replace(GISWEB_BASE, "").replace("/", "_") or "root"
            text = result.get("text", "")

            # Check if it's an HTML page (redirect/login) or actual API
            is_html = "<!DOCTYPE" in text[:200] or "<html" in text[:200].lower()

            if is_html and length < 15000:
                # Could be an application page
                print(f"  {url}: HTML page ({length} bytes)")
                # Check if it's different from the base page
                if length != 9519:
                    save(f"endpoint_{name}.html", text)
                    results[url] = {"type": "html", "length": length}
            elif not is_html:
                print(f"  [HIT] {url}: API response ({length} bytes, {ct})")
                save(f"endpoint_{name}.json" if "json" in ct else f"endpoint_{name}.txt", text)
                results[url] = {"type": "api", "length": length, "content_type": ct}
        elif status == 200:
            print(f"  {url}: {status} (small: {length} bytes)")
        elif status != 404:
            print(f"  {url}: {status}")

    save("_endpoint_scan.json", results)
    return results


# ============================================================
# PHASE 7: Deep dive into route.htm page itself (fetch as HTML)
# ============================================================
def phase7_route_page():
    print("\n" + "=" * 70)
    print("PHASE 7: Fetching route.htm as a page (no action param)")
    print("=" * 70)

    result = req_url(BASE_URL)
    if result.get("status") == 200:
        text = result["text"]
        save("route_page.html", text)
        print(f"  route.htm page: {len(text)} bytes")

        # Extract all JS/CSS/API references
        js_refs = re.findall(r'src=["\']([^"\']+\.js[^"\']*)["\']', text)
        css_refs = re.findall(r'href=["\']([^"\']+\.css[^"\']*)["\']', text)
        api_refs = re.findall(r'["\']([^"\']*\.htm[^"\']*)["\']', text)
        action_refs = re.findall(r'action\s*[:=]\s*["\'](\w+)["\']', text)
        navi_refs = re.findall(r'naviKey\s*[:=]\s*["\']([^"\']+)["\']', text)

        print(f"  JS files: {js_refs}")
        print(f"  Actions in page: {action_refs}")
        print(f"  NaviKeys in page: {navi_refs}")

        # Fetch any additional JS files found
        for js_path in js_refs:
            if js_path.startswith("/"):
                url = f"https://ymspace.ga.nycu.edu.tw{js_path}"
            elif js_path.startswith("http"):
                url = js_path
            else:
                url = f"https://ymspace.ga.nycu.edu.tw/gisweb/public/{js_path}"

            name = js_path.split("/")[-1].split("?")[0]
            if not (OUTPUT_DIR / f"js_{name}").exists():
                r = req_url(url)
                time.sleep(DELAY)
                if r.get("status") == 200 and r.get("length", 0) > 50:
                    save(f"js_{name}", r["text"])
                    print(f"  Downloaded JS: {name} ({len(r['text'])} bytes)")

                    # Check for route-specific API patterns
                    route_patterns = re.findall(r'route\.htm[^"\']*["\']', r["text"])
                    if route_patterns:
                        print(f"    Route patterns: {route_patterns[:5]}")

        return text
    return None


# ============================================================
# PHASE 8: Search specifically in gips.map.gisMapWrapper.js
# for API call patterns
# ============================================================
def phase8_analyze_gips_deeply():
    print("\n" + "=" * 70)
    print("PHASE 8: Deep analysis of GIPS map wrapper for API patterns")
    print("=" * 70)

    # The gisMapWrapper is most likely where navigation/route API calls are made
    wrapper_file = OUTPUT_DIR / "js_gips.map.gisMapWrapper.js"
    if wrapper_file.exists():
        text = wrapper_file.read_text(encoding="utf-8", errors="replace")

        # Find all $.ajax, $.get, $.post calls
        ajax_blocks = re.findall(r'\$\.ajax\s*\(\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}', text, re.DOTALL)
        get_calls = re.findall(r'\$\.get\s*\(\s*["\']([^"\']+)["\']', text)
        post_calls = re.findall(r'\$\.post\s*\(\s*["\']([^"\']+)["\']', text)
        getjson_calls = re.findall(r'\$\.getJSON\s*\(\s*["\']([^"\']+)["\']', text)

        # Find function definitions related to routing
        route_funcs = re.findall(r'(\w+)\s*[:=]\s*function\s*\([^)]*\)\s*\{[^}]*route[^}]*\}', text, re.IGNORECASE)
        navi_funcs = re.findall(r'(\w+)\s*[:=]\s*function\s*\([^)]*\)\s*\{[^}]*navi[^}]*\}', text, re.IGNORECASE)

        # Find all URL constructions
        url_constructs = re.findall(r'(?:url|URL)\s*[:=]\s*([^\n,;]+)', text)

        # Find route.htm references with context
        route_refs_with_context = []
        for match in re.finditer(r'route\.htm', text):
            start = max(0, match.start() - 200)
            end = min(len(text), match.end() + 200)
            context = text[start:end]
            route_refs_with_context.append(context)

        # Find all findGeom references with context
        findgeom_refs = []
        for match in re.finditer(r'findGeom', text):
            start = max(0, match.start() - 300)
            end = min(len(text), match.end() + 300)
            context = text[start:end]
            findgeom_refs.append(context)

        print(f"  AJAX blocks: {len(ajax_blocks)}")
        print(f"  $.get calls: {get_calls}")
        print(f"  $.post calls: {post_calls}")
        print(f"  $.getJSON calls: {getjson_calls}")
        print(f"  Route-related functions: {route_funcs}")
        print(f"  Navi-related functions: {navi_funcs}")

        if route_refs_with_context:
            print(f"\n  route.htm references ({len(route_refs_with_context)}):")
            for i, ctx in enumerate(route_refs_with_context[:5]):
                print(f"    [{i+1}] ...{ctx.strip()[:300]}...")

        if findgeom_refs:
            print(f"\n  findGeom references ({len(findgeom_refs)}):")
            for i, ctx in enumerate(findgeom_refs[:5]):
                print(f"    [{i+1}] ...{ctx.strip()[:300]}...")

        save("_wrapper_analysis.json", {
            "ajax_blocks_count": len(ajax_blocks),
            "get_calls": get_calls,
            "post_calls": post_calls,
            "getjson_calls": getjson_calls,
            "url_constructs": [u.strip()[:200] for u in url_constructs[:20]],
            "route_refs": [r[:300] for r in route_refs_with_context[:10]],
            "findgeom_refs": [r[:300] for r in findgeom_refs[:10]],
        })

    # Also check gips.map.js
    map_file = OUTPUT_DIR / "js_gips.map.js"
    if map_file.exists():
        text = map_file.read_text(encoding="utf-8", errors="replace")
        route_refs = re.findall(r'route\.htm[^"\']*', text)
        findgeom_refs2 = re.findall(r'findGeom[^"\']*', text)
        print(f"\n  gips.map.js route refs: {route_refs}")
        print(f"  gips.map.js findGeom refs: {findgeom_refs2}")


def main():
    print(f"Route.htm Deep Exploration v3 - {datetime.now().isoformat()}")
    print(f"Output: {OUTPUT_DIR}")
    print()

    # Phase 1: Fetch JS files
    js_data, actions, params = phase1_fetch_js()

    # Phase 2: Explore gisweb pages
    navi_keys, layer_ids = phase2_gisweb_pages()

    # Phase 3: Try naviKey values
    navikey_results = phase3_try_navikeys(navi_keys)

    # Phase 4: Deep table discovery
    table_results = phase4_table_discovery()

    # Phase 5: dataTransmissionAPI
    dt_results = phase5_dataTransmission()

    # Phase 6: Other controllers
    endpoint_results = phase6_other_controllers()

    # Phase 7: Route page itself
    route_page = phase7_route_page()

    # Phase 8: Deep JS analysis
    phase8_analyze_gips_deeply()

    # Final summary
    print("\n" + "=" * 70)
    print("FINAL SUMMARY v3")
    print("=" * 70)
    print(f"Total requests: {total_requests}")
    print(f"Total hits: {len(hits)}")
    for h in hits:
        print(f"  - {h}")

    save("_FINAL_SUMMARY_v3.json", {
        "date": datetime.now().isoformat(),
        "total_requests": total_requests,
        "hits": hits,
    })


if __name__ == "__main__":
    main()
