"""
Targeted exploration of ymspace route.htm based on Phase 1 findings.

Discovered valid actions from error messages:
  searchByDistance, getCentroid, loadImageByApKey, findAll,
  getHeaders, search, getLayerInfo, dataTransmissionAPI, findGeom

Key findings:
  - findAll requires 'tableName' param
  - search requires schema name
  - loadImageByApKey requires 'naviKey' (NOT 'apKey')
  - findGeom throws NullPointerException (needs params)
"""
import sys
import os
import json
import time
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"
OUTPUT_DIR = Path(r"C:\Users\thc1006\Desktop\NQSD\新增資料夾\data\ymmap_archive\route_data\v2")
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": "application/json, text/html, */*",
    "Referer": "https://ymspace.ga.nycu.edu.tw/gisweb/public/route.htm",
})

DELAY = 0.25
total_requests = 0
hits = []


def req(params, label="", method="GET"):
    """Make a request and return result dict."""
    global total_requests
    total_requests += 1
    try:
        if method == "POST":
            resp = SESSION.post(BASE_URL, data=params, timeout=15)
        else:
            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),
            "params": params,
            "label": label,
        }

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

        # Determine if this is real data vs error
        is_error = False
        if result["is_json"] and isinstance(result["data"], dict):
            msg = result["data"].get("message", "")
            if any(x in msg for x in ["Exception", "not met", "not present", "null", "error", "Error"]):
                is_error = True

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

        if is_real_data:
            result["is_real"] = True
        else:
            result["is_real"] = False

        return result

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


def save(filename, data):
    filepath = OUTPUT_DIR / filename
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    return filepath


def log_hit(label, result):
    """Log a successful hit."""
    data = result.get("data")
    preview = ""
    if isinstance(data, (dict, list)):
        preview = json.dumps(data, ensure_ascii=False)[:200]
    elif isinstance(data, str):
        preview = data[:200]
    print(f"  [HIT] {label} (status={result['status']}, len={result['length']})")
    print(f"         Preview: {preview}")
    hits.append({"label": label, "status": result["status"], "length": result["length"]})


def log_error(label, result):
    """Log an error response but show the error message (which reveals API info)."""
    data = result.get("data", {})
    msg = ""
    if isinstance(data, dict):
        msg = data.get("message", str(data))[:150]
    elif isinstance(data, str):
        msg = data[:150]
    print(f"  [ERR] {label} -> {msg}")


# ============================================================
# PHASE A: getHeaders - discover table/layer names
# ============================================================
def phase_a_getHeaders():
    print("=" * 70)
    print("PHASE A: getHeaders - discover available tables/schemas")
    print("=" * 70)

    results = {}

    # Try getHeaders with various params
    tests = [
        {"action": "getHeaders"},
        {"action": "getHeaders", "tableName": "route"},
        {"action": "getHeaders", "tableName": "routes"},
        {"action": "getHeaders", "tableName": "building"},
        {"action": "getHeaders", "tableName": "buildings"},
        {"action": "getHeaders", "tableName": "floor"},
        {"action": "getHeaders", "tableName": "floors"},
        {"action": "getHeaders", "tableName": "room"},
        {"action": "getHeaders", "tableName": "rooms"},
        {"action": "getHeaders", "tableName": "node"},
        {"action": "getHeaders", "tableName": "nodes"},
        {"action": "getHeaders", "tableName": "edge"},
        {"action": "getHeaders", "tableName": "edges"},
        {"action": "getHeaders", "tableName": "network"},
        {"action": "getHeaders", "tableName": "path"},
        {"action": "getHeaders", "tableName": "ap"},
        {"action": "getHeaders", "tableName": "wifi"},
        {"action": "getHeaders", "tableName": "poi"},
        {"action": "getHeaders", "tableName": "facility"},
        {"action": "getHeaders", "tableName": "geom"},
        {"action": "getHeaders", "tableName": "geometry"},
        {"action": "getHeaders", "tableName": "spatial"},
        {"action": "getHeaders", "tableName": "map"},
        {"action": "getHeaders", "tableName": "navi"},
        {"action": "getHeaders", "tableName": "navigation"},
        {"action": "getHeaders", "tableName": "indoor"},
        {"action": "getHeaders", "tableName": "outdoor"},
        {"action": "getHeaders", "tableName": "campus"},
        {"action": "getHeaders", "tableName": "yangming"},
        {"action": "getHeaders", "tableName": "ym"},
        {"action": "getHeaders", "tableName": "layer"},
        {"action": "getHeaders", "tableName": "layers"},
        {"action": "getHeaders", "tableName": "image"},
        {"action": "getHeaders", "tableName": "images"},
        {"action": "getHeaders", "tableName": "nav_point"},
        {"action": "getHeaders", "tableName": "nav_edge"},
        {"action": "getHeaders", "tableName": "nav_node"},
        {"action": "getHeaders", "tableName": "route_node"},
        {"action": "getHeaders", "tableName": "route_edge"},
        {"action": "getHeaders", "tableName": "route_point"},
        {"action": "getHeaders", "tableName": "link"},
        {"action": "getHeaders", "tableName": "links"},
    ]

    for params in tests:
        label = "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")
            save(f"headers_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE B: getLayerInfo - discover layers
# ============================================================
def phase_b_getLayerInfo():
    print("\n" + "=" * 70)
    print("PHASE B: getLayerInfo - discover layer information")
    print("=" * 70)

    results = {}

    tests = [
        {"action": "getLayerInfo"},
        {"action": "getLayerInfo", "layerName": "route"},
        {"action": "getLayerInfo", "layerName": "building"},
        {"action": "getLayerInfo", "layerName": "floor"},
        {"action": "getLayerInfo", "layerName": "room"},
        {"action": "getLayerInfo", "layerName": "node"},
        {"action": "getLayerInfo", "layerName": "edge"},
        {"action": "getLayerInfo", "layerName": "ap"},
        {"action": "getLayerInfo", "layerName": "navi"},
        {"action": "getLayerInfo", "layerName": "campus"},
        {"action": "getLayerInfo", "layer": "route"},
        {"action": "getLayerInfo", "name": "route"},
        {"action": "getLayerInfo", "tableName": "route"},
    ]

    for params in tests:
        label = "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")
            save(f"layerInfo_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE C: findAll with various tableName values
# ============================================================
def phase_c_findAll():
    print("\n" + "=" * 70)
    print("PHASE C: findAll - try various table names")
    print("=" * 70)

    results = {}

    table_names = [
        # Common GIS table names
        "route", "routes", "building", "buildings",
        "floor", "floors", "room", "rooms",
        "node", "nodes", "edge", "edges",
        "network", "path", "paths",
        "ap", "aps", "wifi", "wifi_ap",
        "poi", "pois", "facility", "facilities",
        "geom", "geometry", "spatial",
        "navi", "navigation", "nav_point", "nav_edge", "nav_node",
        "route_node", "route_edge", "route_point", "route_link",
        "link", "links", "segment", "segments",
        "indoor", "outdoor", "campus", "map",
        "yangming", "ym", "layer", "layers",
        "image", "images", "photo", "photos",
        # PostGIS-style
        "public.route", "public.building", "public.node",
        "gis.route", "gis.building",
        # Schema guesses
        "ym_building", "ym_route", "ym_node", "ym_floor",
        "campus_building", "campus_route",
        "navi_route", "navi_node", "navi_edge",
        "indoor_map", "indoor_route", "indoor_node",
        # CamelCase
        "Route", "Building", "Floor", "Room", "Node", "Edge",
        "NaviPoint", "NaviEdge", "NaviRoute",
        "RouteNode", "RouteEdge",
        "IndoorMap", "IndoorRoute",
    ]

    for tname in table_names:
        label = f"findAll_tableName={tname}"
        result = req({"action": "findAll", "tableName": tname}, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            save(f"findAll_{tname}.json", result["data"])
            results[tname] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE D: findGeom - try all parameter combinations
# ============================================================
def phase_d_findGeom():
    print("\n" + "=" * 70)
    print("PHASE D: findGeom - try different parameter combos")
    print("=" * 70)

    results = {}

    building_ids = [f"Y{i:03d}" for i in range(1, 20)] + \
                   [f"B{i:03d}" for i in range(1, 21)] + \
                   [f"P{i:03d}" for i in range(1, 10)]

    # First try with different param names to see what findGeom actually expects
    param_tests = [
        {"action": "findGeom", "buildId": "Y001"},
        {"action": "findGeom", "building_id": "Y001"},
        {"action": "findGeom", "buildingId": "Y001"},
        {"action": "findGeom", "build_id": "Y001"},
        {"action": "findGeom", "id": "Y001"},
        {"action": "findGeom", "bid": "Y001"},
        {"action": "findGeom", "name": "Y001"},
        {"action": "findGeom", "tableName": "route"},
        {"action": "findGeom", "tableName": "building"},
        {"action": "findGeom", "tableName": "node"},
        {"action": "findGeom", "tableName": "edge"},
        {"action": "findGeom", "tableName": "route", "buildId": "Y001"},
        {"action": "findGeom", "tableName": "building", "buildId": "Y001"},
        {"action": "findGeom", "tableName": "node", "buildId": "Y001"},
        {"action": "findGeom", "tableName": "route", "id": "1"},
        {"action": "findGeom", "tableName": "route", "all": "true"},
        {"action": "findGeom", "type": "route"},
        {"action": "findGeom", "type": "building"},
        {"action": "findGeom", "type": "node"},
        {"action": "findGeom", "layer": "route"},
        {"action": "findGeom", "layer": "building"},
        {"action": "findGeom", "schema": "route"},
        {"action": "findGeom", "schema": "public"},
        {"action": "findGeom", "naviKey": "1"},
        {"action": "findGeom", "naviKey": "Y001"},
        {"action": "findGeom", "key": "Y001"},
    ]

    for params in param_tests:
        label = "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")[:80]
            save(f"findGeom_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE E: loadImageByApKey with naviKey
# ============================================================
def phase_e_loadImageByApKey():
    print("\n" + "=" * 70)
    print("PHASE E: loadImageByApKey with naviKey (1-200)")
    print("=" * 70)

    results = {}

    # The error said it needs 'naviKey', not 'apKey'
    for i in range(1, 201):
        if i % 50 == 0:
            print(f"  Progress: {i}/200")

        result = req({"action": "loadImageByApKey", "naviKey": str(i)}, f"naviKey={i}")
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(f"naviKey={i}", result)
            results[str(i)] = {
                "length": result["length"],
                "content_type": result["content_type"],
                "is_json": result.get("is_json"),
            }
            if result.get("is_json"):
                save(f"loadImage_naviKey_{i}.json", result["data"])
            else:
                # Save binary data
                data = result.get("data", "")
                if isinstance(data, str) and len(data) > 20:
                    results[str(i)]["data_preview"] = data[:200]
        elif not result.get("is_error"):
            log_error(f"naviKey={i}", result)

    # Also try string-based naviKeys
    string_keys = [
        "Y001", "Y002", "Y003", "B001", "B002", "P001",
        "Y001-1F", "Y001-2F", "Y001-B1",
        "AP001", "AP002", "wifi-001",
    ]

    for key in string_keys:
        result = req({"action": "loadImageByApKey", "naviKey": key}, f"naviKey={key}")
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(f"naviKey={key}", result)
            results[key] = {
                "length": result["length"],
                "content_type": result["content_type"],
            }
            if result.get("is_json"):
                save(f"loadImage_naviKey_{key}.json", result["data"])

    save("_loadImage_summary.json", results)
    print(f"\nFound {len(results)} working naviKeys")
    return results


# ============================================================
# PHASE F: search action
# ============================================================
def phase_f_search():
    print("\n" + "=" * 70)
    print("PHASE F: search action with various schemas")
    print("=" * 70)

    results = {}

    tests = [
        {"action": "search", "schema": "public"},
        {"action": "search", "schema": "route"},
        {"action": "search", "schema": "gis"},
        {"action": "search", "schema": "navi"},
        {"action": "search", "schema": "campus"},
        {"action": "search", "schema": "indoor"},
        {"action": "search", "schema": "ym"},
        {"action": "search", "schema": "map"},
        {"action": "search", "schema": "yangming"},
        {"action": "search", "schema": "building"},
        {"action": "search", "tableName": "route"},
        {"action": "search", "tableName": "building"},
        {"action": "search", "tableName": "node"},
        {"action": "search", "q": "Y001"},
        {"action": "search", "keyword": "Y001"},
        {"action": "search", "query": "Y001"},
        {"action": "search", "term": "Y001"},
        {"action": "search", "name": "Y001"},
        {"action": "search", "text": "Y001"},
        {"action": "search", "schema": "public", "tableName": "route"},
        {"action": "search", "schema": "public", "tableName": "building"},
        {"action": "search", "schema": "public", "tableName": "node"},
        {"action": "search", "schema": "public", "tableName": "edge"},
        {"action": "search", "schema": "public", "tableName": "navi"},
        {"action": "search", "schema": "public", "tableName": "geom"},
    ]

    for params in tests:
        label = "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")[:80]
            save(f"search_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE G: searchByDistance & getCentroid
# ============================================================
def phase_g_spatial():
    print("\n" + "=" * 70)
    print("PHASE G: searchByDistance & getCentroid")
    print("=" * 70)

    results = {}

    # Yang Ming campus approximate coordinates (WGS84)
    ym_lat = 25.0218
    ym_lng = 121.5169

    tests = [
        # searchByDistance
        {"action": "searchByDistance"},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng)},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng), "distance": "100"},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng), "distance": "500"},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng), "distance": "1000"},
        {"action": "searchByDistance", "x": str(ym_lng), "y": str(ym_lat)},
        {"action": "searchByDistance", "x": str(ym_lng), "y": str(ym_lat), "distance": "500"},
        {"action": "searchByDistance", "latitude": str(ym_lat), "longitude": str(ym_lng)},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng), "tableName": "route"},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng), "tableName": "building"},
        {"action": "searchByDistance", "lat": str(ym_lat), "lng": str(ym_lng), "tableName": "node"},
        {"action": "searchByDistance", "point": f"{ym_lng},{ym_lat}", "distance": "500"},
        {"action": "searchByDistance", "point": f"POINT({ym_lng} {ym_lat})", "distance": "500"},

        # getCentroid
        {"action": "getCentroid"},
        {"action": "getCentroid", "buildId": "Y001"},
        {"action": "getCentroid", "buildingId": "Y001"},
        {"action": "getCentroid", "id": "Y001"},
        {"action": "getCentroid", "name": "Y001"},
        {"action": "getCentroid", "tableName": "route"},
        {"action": "getCentroid", "tableName": "building"},
        {"action": "getCentroid", "tableName": "node"},
        {"action": "getCentroid", "naviKey": "1"},
        {"action": "getCentroid", "naviKey": "Y001"},
    ]

    for params in tests:
        label = "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")[:80]
            save(f"spatial_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE H: dataTransmissionAPI
# ============================================================
def phase_h_dataTransmission():
    print("\n" + "=" * 70)
    print("PHASE H: dataTransmissionAPI")
    print("=" * 70)

    results = {}

    tests = [
        {"action": "dataTransmissionAPI"},
        {"action": "dataTransmissionAPI", "type": "route"},
        {"action": "dataTransmissionAPI", "type": "building"},
        {"action": "dataTransmissionAPI", "type": "node"},
        {"action": "dataTransmissionAPI", "type": "edge"},
        {"action": "dataTransmissionAPI", "type": "ap"},
        {"action": "dataTransmissionAPI", "type": "wifi"},
        {"action": "dataTransmissionAPI", "tableName": "route"},
        {"action": "dataTransmissionAPI", "tableName": "building"},
        {"action": "dataTransmissionAPI", "data": "route"},
        {"action": "dataTransmissionAPI", "format": "json"},
        {"action": "dataTransmissionAPI", "buildId": "Y001"},
        {"action": "dataTransmissionAPI", "naviKey": "1"},
    ]

    for params in tests:
        label = "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label)
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")[:80]
            save(f"dataTransmission_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    # Also try POST
    for params in tests[:5]:
        label = "POST_" + "&".join(f"{k}={v}" for k, v in params.items())
        result = req(params, label, method="POST")
        time.sleep(DELAY)

        if result.get("is_real"):
            log_hit(label, result)
            safe = label.replace("=", "_").replace("&", "__")[:80]
            save(f"dataTransmission_{safe}.json", result["data"])
            results[label] = result["data"]
        else:
            log_error(label, result)

    return results


# ============================================================
# PHASE I: Try the main page and related endpoints
# ============================================================
def phase_i_related_endpoints():
    print("\n" + "=" * 70)
    print("PHASE I: Related endpoints and the main page")
    print("=" * 70)

    results = {}

    # Try fetching the base page itself
    try:
        resp = SESSION.get(BASE_URL, timeout=15)
        if resp.status_code == 200 and len(resp.text) > 100:
            save("_base_page.html", resp.text)
            print(f"  Base page: {len(resp.text)} bytes")
            results["base_page"] = {"length": len(resp.text), "status": resp.status_code}

            # Extract any JS references
            import re
            js_refs = re.findall(r'src=["\']([^"\']*\.js[^"\']*)["\']', resp.text)
            css_refs = re.findall(r'href=["\']([^"\']*\.css[^"\']*)["\']', resp.text)
            api_refs = re.findall(r'["\']([^"\']*\.htm[^"\']*)["\']', resp.text)
            ajax_refs = re.findall(r'(?:url|URL)\s*[:=]\s*["\']([^"\']+)["\']', resp.text)

            results["js_files"] = js_refs
            results["css_files"] = css_refs
            results["htm_files"] = api_refs
            results["ajax_urls"] = ajax_refs

            print(f"  JS refs: {js_refs}")
            print(f"  CSS refs: {css_refs}")
            print(f"  HTM refs: {api_refs}")
            print(f"  AJAX refs: {ajax_refs}")

    except Exception as e:
        print(f"  Error fetching base page: {e}")

    # Try related endpoint patterns
    related_urls = [
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/map.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/navi.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/building.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/floor.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/indoor.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/api.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/data.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/search.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/layer.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/gis.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/network.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/wifi.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/public/ap.htm",
        "https://ymspace.ga.nycu.edu.tw/gisweb/",
        "https://ymspace.ga.nycu.edu.tw/gisweb/api/",
    ]

    for url in related_urls:
        try:
            resp = SESSION.get(url, timeout=10)
            if resp.status_code == 200 and len(resp.content) > 50:
                name = url.split("/")[-1] or "index"
                print(f"  {url}: {resp.status_code} ({len(resp.content)} bytes)")
                results[url] = {"status": resp.status_code, "length": len(resp.content)}
                # Save HTML pages
                if "html" in resp.headers.get("Content-Type", "").lower() or name.endswith(".htm"):
                    save(f"related_{name}.html" if "." in name else f"related_{name}.html", resp.text)
            else:
                print(f"  {url}: {resp.status_code}")
        except Exception as e:
            print(f"  {url}: ERROR - {e}")

        time.sleep(DELAY)

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


# ============================================================
# PHASE J: Fetch and analyze JavaScript files
# ============================================================
def phase_j_analyze_js(js_files):
    print("\n" + "=" * 70)
    print("PHASE J: Fetching and analyzing JavaScript files")
    print("=" * 70)

    results = {}
    base = "https://ymspace.ga.nycu.edu.tw/gisweb/public/"

    for js_url in js_files:
        if not js_url.startswith("http"):
            full_url = base + js_url.lstrip("./")
        else:
            full_url = js_url

        try:
            resp = SESSION.get(full_url, timeout=15)
            if resp.status_code == 200 and len(resp.text) > 50:
                name = js_url.split("/")[-1].split("?")[0]
                save(f"js_{name}", resp.text)
                print(f"  {name}: {len(resp.text)} bytes")

                # Extract API-related patterns
                import re
                actions = re.findall(r'action["\s]*[:=]\s*["\'](\w+)["\']', resp.text)
                params = re.findall(r'(?:param|data|query)\w*\s*[:=]\s*\{([^}]+)\}', resp.text)
                urls = re.findall(r'(?:url|URL|href)\s*[:=]\s*["\']([^"\']+\.htm[^"\']*)["\']', resp.text)
                ajax_calls = re.findall(r'\$\.(?:ajax|get|post|getJSON)\s*\(\s*["\']([^"\']+)["\']', resp.text)
                fetch_calls = re.findall(r'fetch\s*\(\s*["\']([^"\']+)["\']', resp.text)

                results[name] = {
                    "size": len(resp.text),
                    "actions_found": list(set(actions)),
                    "urls_found": list(set(urls)),
                    "ajax_calls": list(set(ajax_calls)),
                    "fetch_calls": list(set(fetch_calls)),
                }

                if actions:
                    print(f"    Actions: {list(set(actions))}")
                if urls:
                    print(f"    URLs: {list(set(urls))}")
                if ajax_calls:
                    print(f"    AJAX: {list(set(ajax_calls))}")
        except Exception as e:
            print(f"  {js_url}: ERROR - {e}")

        time.sleep(DELAY)

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


def main():
    print(f"Route.htm Targeted Exploration v2 - {datetime.now().isoformat()}")
    print(f"Base URL: {BASE_URL}")
    print(f"Output: {OUTPUT_DIR}")
    print()

    # Phase A: getHeaders
    headers_data = phase_a_getHeaders()

    # Phase B: getLayerInfo
    layer_data = phase_b_getLayerInfo()

    # Phase C: findAll with table names
    findall_data = phase_c_findAll()

    # Phase D: findGeom with various params
    findgeom_data = phase_d_findGeom()

    # Phase E: loadImageByApKey with naviKey
    loadimage_data = phase_e_loadImageByApKey()

    # Phase F: search
    search_data = phase_f_search()

    # Phase G: spatial queries
    spatial_data = phase_g_spatial()

    # Phase H: dataTransmissionAPI
    data_trans = phase_h_dataTransmission()

    # Phase I: Related endpoints
    related = phase_i_related_endpoints()

    # Phase J: Analyze JS files
    js_files = related.get("js_files", []) if isinstance(related, dict) else []
    if js_files:
        js_data = phase_j_analyze_js(js_files)

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

    save("_FINAL_SUMMARY.json", {
        "date": datetime.now().isoformat(),
        "total_requests": total_requests,
        "hits": hits,
        "valid_actions": [
            "searchByDistance", "getCentroid", "loadImageByApKey",
            "findAll", "getHeaders", "search",
            "getLayerInfo", "dataTransmissionAPI", "findGeom"
        ],
    })


if __name__ == "__main__":
    main()
