"""
Exploration v5: Final deep probe based on all discoveries.

Key findings from v4:
  - The server's navi configuration is NULL (not initialized)
  - But the GIPS framework endpoints exist and some partially work
  - dataTransmissionAPI needs JSON with both "target" and "action" fields
  - Other controllers: buildinfo.htm, roominfo.htm, campus.htm, userrole.htm

New endpoints discovered from JS:
  POST buildinfo.htm?action=getBoundByBuildId -> [buildId, proj]
  POST buildinfo.htm?action=getCentroidByBuildId -> [buildId, proj]
  POST buildinfo.htm?action=getFloorList -> [buildId]
  POST roominfo.htm?action=findByFloor -> [buildId, floor, locale]
  POST buildinfo.htm?action=loadPublicData -> [buildId, locale]
  POST buildinfo.htm?action=loadImage -> [buildId, naviKey]
  POST roominfo.htm?action=getBound -> [tableName, gid, roomId, proj]
  POST roominfo.htm?action=queryRoomExists -> [bid, floor]
  GET  /campus.htm?action=listAllWithExtent
  GET  userrole.htm?action=findAllLayers
  GET  buildinfo.htm?action=getBoundingBoxByBuildId
"""
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)

GISWEB_BASE = "https://ymspace.ga.nycu.edu.tw/gisweb"
PUBLIC_BASE = f"{GISWEB_BASE}/public"
OUTPUT_DIR = Path(r"C:\Users\thc1006\Desktop\NQSD\新增資料夾\data\ymmap_archive\route_data\v5")
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, */*",
    "Referer": f"{PUBLIC_BASE}/map.htm",
    "X-Requested-With": "XMLHttpRequest",
})

DELAY = 0.25
total_requests = 0
all_hits = []


def post_api(endpoint, action, data, label=""):
    """POST to a GIPS endpoint."""
    global total_requests
    total_requests += 1
    url = f"{PUBLIC_BASE}/{endpoint}?action={action}"
    try:
        resp = SESSION.post(url, data=data, timeout=15)
        ct = resp.headers.get("Content-Type", "")
        result = {
            "status": resp.status_code,
            "content_type": ct,
            "length": len(resp.content),
            "label": label,
        }
        try:
            result["data"] = resp.json()
            result["is_json"] = True
        except (json.JSONDecodeError, ValueError):
            result["data"] = resp.text.strip()
            result["is_json"] = False

        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", "null", "error", "Error", "not present", "not met", "Incorrect"]):
                is_error = True
            if result["data"].get("success") is False:
                is_error = True
        elif result["status"] >= 400:
            is_error = True

        result["is_error"] = is_error
        result["is_hit"] = not is_error and result["length"] > 5
        return result
    except Exception as e:
        return {"error": str(e), "is_hit": False, "is_error": True, "label": label}


def get_api(endpoint, action, params=None, label=""):
    """GET from a GIPS endpoint."""
    global total_requests
    total_requests += 1
    url = f"{PUBLIC_BASE}/{endpoint}"
    full_params = {"action": action}
    if params:
        full_params.update(params)
    try:
        resp = SESSION.get(url, params=full_params, timeout=15)
        ct = resp.headers.get("Content-Type", "")
        result = {
            "status": resp.status_code,
            "content_type": ct,
            "length": len(resp.content),
            "label": label,
        }
        try:
            result["data"] = resp.json()
            result["is_json"] = True
        except (json.JSONDecodeError, ValueError):
            result["data"] = resp.text.strip()
            result["is_json"] = False

        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", "null", "error", "Error", "not present", "not met", "Incorrect"]):
                is_error = True
            if result["data"].get("success") is False:
                is_error = True
        elif result["status"] >= 400:
            is_error = True

        result["is_error"] = is_error
        result["is_hit"] = not is_error and result["length"] > 5
        return result
    except Exception as e:
        return {"error": str(e), "is_hit": False, "is_error": True, "label": label}


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


def log_result(result, tag=""):
    label = result.get("label", "")
    if result.get("is_hit"):
        data = result.get("data")
        preview = ""
        if isinstance(data, (dict, list)):
            preview = json.dumps(data, ensure_ascii=False)[:400]
        elif isinstance(data, str):
            preview = data[:400]
        print(f"  [HIT] {label} (status={result['status']}, len={result['length']})")
        print(f"         {preview}")
        all_hits.append({"label": label, "length": result["length"]})
        return True
    else:
        data = result.get("data", {})
        msg = ""
        if isinstance(data, dict):
            msg = str(data.get("message", ""))[:150]
        elif isinstance(data, str):
            msg = data[:150]
        if tag:
            print(f"  [{tag}] {label}: {msg}")
        return False


# ============================================================
# PHASE 1: campus.htm - listAllWithExtent
# ============================================================
def phase1_campus():
    print("=" * 70)
    print("PHASE 1: campus.htm - listAllWithExtent")
    print("=" * 70)

    results = {}

    # GET /campus.htm?action=listAllWithExtent
    result = get_api("campus.htm", "listAllWithExtent", label="campus listAllWithExtent")
    time.sleep(DELAY)
    if log_result(result, "info"):
        save("campus_listAllWithExtent.json", result["data"])
        results["listAllWithExtent"] = result["data"]
    else:
        log_result(result, "err")

    # Try other campus actions
    for action in ["list", "listAll", "findAll", "getAll", "getCampusList"]:
        result = get_api("campus.htm", action, label=f"campus {action}")
        time.sleep(DELAY)
        if log_result(result, "info"):
            save(f"campus_{action}.json", result["data"])
            results[action] = result["data"]

    return results


# ============================================================
# PHASE 2: buildinfo.htm - building information
# ============================================================
def phase2_buildinfo():
    print("\n" + "=" * 70)
    print("PHASE 2: buildinfo.htm - building info, floor list, images")
    print("=" * 70)

    results = {}

    # Building IDs to test - from YM campus
    build_ids = [
        "Y001", "Y002", "Y003", "Y004", "Y005",
        "Y006", "Y007", "Y008", "Y009", "Y010",
        "Y011", "Y012", "Y013", "Y014", "Y015",
        "Y016", "Y017", "Y018", "Y019",
        "B001", "B002", "B003", "B004", "B005",
        "P001", "P002", "P003",
        # Also try NTU-style IDs (since the JS references NTU)
        "A001", "A002", "A003", "A011",
        # Numeric
        "1", "2", "3",
    ]

    projs = ["EPSG:3826", "EPSG:4326"]

    # Test getFloorList for all building IDs
    print("\n  --- getFloorList ---")
    for bid in build_ids:
        result = post_api("buildinfo.htm", "getFloorList", {"buildId": bid}, f"getFloorList buildId={bid}")
        time.sleep(DELAY)
        if log_result(result):
            save(f"buildinfo_floorList_{bid}.json", result["data"])
            results[f"floorList_{bid}"] = result["data"]
        elif bid in ["Y001", "A011", "1"]:
            log_result(result, "err")

    # Test getBoundByBuildId
    print("\n  --- getBoundByBuildId ---")
    for bid in build_ids[:10]:
        for proj in projs:
            result = post_api("buildinfo.htm", "getBoundByBuildId",
                              {"buildId": bid, "proj": proj},
                              f"getBound buildId={bid} proj={proj}")
            time.sleep(DELAY)
            if log_result(result):
                save(f"buildinfo_bound_{bid}_{proj.replace(':', '_')}.json", result["data"])
                results[f"bound_{bid}_{proj}"] = result["data"]
            elif proj == "EPSG:3826" and bid in ["Y001", "A011"]:
                log_result(result, "err")

    # Test getCentroidByBuildId
    print("\n  --- getCentroidByBuildId ---")
    for bid in build_ids[:10]:
        result = post_api("buildinfo.htm", "getCentroidByBuildId",
                          {"buildId": bid, "proj": "EPSG:4326"},
                          f"getCentroid buildId={bid}")
        time.sleep(DELAY)
        if log_result(result):
            save(f"buildinfo_centroid_{bid}.json", result["data"])
            results[f"centroid_{bid}"] = result["data"]
        elif bid in ["Y001", "A011"]:
            log_result(result, "err")

    # Test getBoundingBoxByBuildId
    print("\n  --- getBoundingBoxByBuildId ---")
    for bid in build_ids[:5]:
        result = get_api("buildinfo.htm", "getBoundingBoxByBuildId",
                         {"buildId": bid, "proj": "EPSG:4326"},
                         f"getBBox buildId={bid}")
        time.sleep(DELAY)
        if log_result(result):
            save(f"buildinfo_bbox_{bid}.json", result["data"])
            results[f"bbox_{bid}"] = result["data"]
        elif bid == "Y001":
            log_result(result, "err")

    # Test loadPublicData
    print("\n  --- loadPublicData ---")
    for bid in build_ids[:10]:
        for locale in ["zh-TW", "en"]:
            result = post_api("buildinfo.htm", "loadPublicData",
                              {"buildId": bid, "locale": locale},
                              f"loadPublicData buildId={bid} locale={locale}")
            time.sleep(DELAY)
            if log_result(result):
                save(f"buildinfo_publicData_{bid}_{locale.replace('-', '_')}.json", result["data"])
                results[f"publicData_{bid}_{locale}"] = result["data"]
            elif locale == "zh-TW" and bid in ["Y001", "A011"]:
                log_result(result, "err")

    # Test loadImage
    print("\n  --- loadImage ---")
    for bid in build_ids[:10]:
        result = post_api("buildinfo.htm", "loadImage",
                          {"buildId": bid, "naviKey": "building_estate"},
                          f"loadImage buildId={bid}")
        time.sleep(DELAY)
        if log_result(result):
            save(f"buildinfo_image_{bid}.json", result["data"])
            results[f"image_{bid}"] = result["data"]
        elif bid in ["Y001", "A011"]:
            log_result(result, "err")

    save("_buildinfo_results.json", {"hit_count": len(results), "labels": list(results.keys())})
    print(f"\n  buildinfo total hits: {len(results)}")
    return results


# ============================================================
# PHASE 3: roominfo.htm
# ============================================================
def phase3_roominfo():
    print("\n" + "=" * 70)
    print("PHASE 3: roominfo.htm - room info, floor plans")
    print("=" * 70)

    results = {}

    build_ids = ["Y001", "Y002", "Y003", "B001", "A011", "1"]
    floors = ["1F", "2F", "3F", "B1", "RF"]

    # findByFloor
    print("\n  --- findByFloor ---")
    for bid in build_ids:
        for floor in floors[:3]:
            for locale in ["zh-TW"]:
                result = post_api("roominfo.htm", "findByFloor",
                                  {"buildId": bid, "floor": floor, "locale": locale},
                                  f"findByFloor buildId={bid} floor={floor}")
                time.sleep(DELAY)
                if log_result(result):
                    save(f"roominfo_floor_{bid}_{floor}.json", result["data"])
                    results[f"floor_{bid}_{floor}"] = result["data"]
                elif bid in ["Y001", "A011"] and floor == "1F":
                    log_result(result, "err")

    # queryRoomExists
    print("\n  --- queryRoomExists ---")
    for bid in build_ids:
        for floor in floors[:3]:
            result = post_api("roominfo.htm", "queryRoomExists",
                              {"bid": bid, "floor": floor},
                              f"queryRoomExists bid={bid} floor={floor}")
            time.sleep(DELAY)
            if log_result(result):
                save(f"roominfo_exists_{bid}_{floor}.json", result["data"])
                results[f"exists_{bid}_{floor}"] = result["data"]
            elif bid in ["Y001", "A011"] and floor == "1F":
                log_result(result, "err")

    # getBound
    print("\n  --- getBound ---")
    for bid in build_ids[:3]:
        for gid in ["1", "2", "3"]:
            result = post_api("roominfo.htm", "getBound",
                              {"tableName": "building_estate", "gid": gid, "roomId": bid, "proj": "EPSG:4326"},
                              f"getBound tableName=building_estate gid={gid} roomId={bid}")
            time.sleep(DELAY)
            if log_result(result):
                save(f"roominfo_bound_{bid}_gid{gid}.json", result["data"])
                results[f"bound_{bid}_{gid}"] = result["data"]
            elif gid == "1" and bid == "Y001":
                log_result(result, "err")

    save("_roominfo_results.json", {"hit_count": len(results)})
    print(f"\n  roominfo total hits: {len(results)}")
    return results


# ============================================================
# PHASE 4: userrole.htm - findAllLayers
# ============================================================
def phase4_userrole():
    print("\n" + "=" * 70)
    print("PHASE 4: userrole.htm - findAllLayers (layer discovery)")
    print("=" * 70)

    results = {}

    # This is crucial - it should list all available layers!
    result = get_api("userrole.htm", "findAllLayers", label="userrole findAllLayers")
    time.sleep(DELAY)
    if log_result(result, "info"):
        save("userrole_findAllLayers.json", result["data"])
        results["findAllLayers"] = result["data"]
    else:
        log_result(result, "err")

    # Try POST too
    result = post_api("userrole.htm", "findAllLayers", {}, label="POST userrole findAllLayers")
    time.sleep(DELAY)
    if log_result(result, "info"):
        save("userrole_findAllLayers_POST.json", result["data"])
        results["findAllLayers_POST"] = result["data"]

    # Try other userrole actions
    for action in ["list", "findAll", "getLayers", "getLayerList"]:
        result = get_api("userrole.htm", action, label=f"userrole {action}")
        time.sleep(DELAY)
        if log_result(result):
            save(f"userrole_{action}.json", result["data"])
            results[action] = result["data"]

    save("_userrole_results.json", {"hit_count": len(results)})
    print(f"\n  userrole total hits: {len(results)}")
    return results


# ============================================================
# PHASE 5: dataTransmissionAPI with target + action in JSON
# ============================================================
def phase5_dataTransmission():
    print("\n" + "=" * 70)
    print("PHASE 5: dataTransmissionAPI with target + action in JSON")
    print("=" * 70)

    results = {}

    targets = ["route", "building", "building_estate", "node", "edge",
               "floor", "room", "navi", "indoor", "campus", "ap", "layer"]

    actions = [
        "findAll", "list", "getAll", "get", "find", "search",
        "getHeaders", "getSchema", "describe",
        "getNetwork", "getGraph", "getTopology",
        "findGeom", "getGeom",
    ]

    for target in targets[:6]:
        for act in actions:
            q = json.dumps({"target": target, "action": act})
            label = f"dataTransmission target={target} action={act}"
            result = get_api("route.htm", "dataTransmissionAPI", {"query": q}, label)
            time.sleep(DELAY)

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

            if result.get("is_hit"):
                preview = json.dumps(data, ensure_ascii=False)[:300] if not isinstance(data, str) else data[:300]
                print(f"  [HIT] {label} -> {preview}")
                safe = re.sub(r'[^\w]', '_', f"{target}_{act}")[:50]
                save(f"dataTrans_{safe}.json", data)
                results[label] = data
            elif msg and "action must be" not in msg and "target must be" not in msg:
                print(f"  [info] {label}: {msg[:100]}")

    # Try with additional params
    print("\n  --- With extra params ---")
    extra_queries = [
        {"target": "building_estate", "action": "findAll", "limit": 10},
        {"target": "building_estate", "action": "findAll", "buildId": "Y001"},
        {"target": "route", "action": "findAll", "from": "Y001", "to": "Y002"},
        {"target": "node", "action": "findAll", "buildId": "Y001"},
        {"target": "building", "action": "get", "id": "Y001"},
        {"target": "building", "action": "get", "buildId": "Y001"},
    ]

    for q_dict in extra_queries:
        q_str = json.dumps(q_dict)
        label = f"dataTransmission {q_dict}"
        result = get_api("route.htm", "dataTransmissionAPI", {"query": q_str}, label)
        time.sleep(DELAY)

        data = result.get("data", {})
        msg = str(data.get("message", "")) if isinstance(data, dict) else ""
        if result.get("is_hit"):
            print(f"  [HIT] {label}")
            save(f"dataTrans_extra_{hash(q_str) % 10000}.json", data)
            results[label] = data
        elif msg:
            print(f"  [info] {label}: {msg[:100]}")

    save("_dataTransmission_results.json", {"hit_count": len(results)})
    print(f"\n  dataTransmission total hits: {len(results)}")
    return results


# ============================================================
# PHASE 6: searchByDistance with srcProj
# ============================================================
def phase6_searchByDistance():
    print("\n" + "=" * 70)
    print("PHASE 6: searchByDistance with srcProj")
    print("=" * 70)

    results = {}

    # YM campus center (WGS84)
    ym_lon = 121.5169
    ym_lat = 25.0218

    # YM campus center (TWD97/TM2 - EPSG:3826)
    ym_x = 304500
    ym_y = 2767700

    navi_keys = ["building_estate", "building", "route", "indoor", "navi", "node"]

    tests = [
        # WGS84
        {"naviKey": "building_estate", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326"},
        {"naviKey": "building_estate", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326", "distance": "500"},
        {"naviKey": "building_estate", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326", "distance": "1000"},
        # TWD97
        {"naviKey": "building_estate", "lon": str(ym_x), "lat": str(ym_y), "srcProj": "EPSG:3826"},
        {"naviKey": "building_estate", "lon": str(ym_x), "lat": str(ym_y), "srcProj": "EPSG:3826", "distance": "500"},
        # Other naviKeys
        {"naviKey": "building", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326"},
        {"naviKey": "route", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326"},
        {"naviKey": "indoor", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326"},
        {"naviKey": "navi", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326"},
        {"naviKey": "node", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:4326"},
        # Google/OSM projection
        {"naviKey": "building_estate", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:900913"},
        {"naviKey": "building_estate", "lon": str(ym_lon), "lat": str(ym_lat), "srcProj": "EPSG:3857"},
    ]

    # POST
    for params in tests:
        label = f"searchByDistance nk={params['naviKey']} proj={params['srcProj']}"
        result = post_api("route.htm", "searchByDistance", params, label)
        time.sleep(DELAY)

        if log_result(result, "info"):
            nk = params["naviKey"]
            proj = params["srcProj"].replace(":", "_")
            save(f"searchDist_{nk}_{proj}.json", result["data"])
            results[label] = result["data"]
        else:
            log_result(result, "err")

    # GET
    print("\n  --- GET ---")
    for params in tests[:5]:
        label = f"GET searchByDistance nk={params['naviKey']} proj={params['srcProj']}"
        result = get_api("route.htm", "searchByDistance", params, label)
        time.sleep(DELAY)
        if log_result(result, "info"):
            save(f"searchDist_GET_{params['naviKey']}.json", result["data"])
            results[label] = result["data"]

    save("_searchByDistance_results.json", {"hit_count": len(results)})
    print(f"\n  searchByDistance total hits: {len(results)}")
    return results


# ============================================================
# PHASE 7: loadImageByApKey with sourceType
# ============================================================
def phase7_loadImage():
    print("\n" + "=" * 70)
    print("PHASE 7: loadImageByApKey with sourceType")
    print("=" * 70)

    results = {}

    # Error was "sourceType is null"
    source_types = ["building", "floor", "room", "ap", "wifi", "buildinfo", "image", "photo"]
    navi_keys = ["building_estate", "building"]
    ap_keys = ["1", "2", "3", "Y001", "AP001"]

    for st in source_types:
        for nk in navi_keys[:1]:
            for apk in ap_keys[:3]:
                params = {"naviKey": nk, "apKey": apk, "sourceType": st}
                label = f"loadImage nk={nk} apKey={apk} sourceType={st}"
                result = post_api("route.htm", "loadImageByApKey", params, label)
                time.sleep(DELAY)

                if log_result(result, "info" if apk == "1" and st in source_types[:2] else ""):
                    save(f"loadImage_{nk}_{apk}_{st}.json", result["data"])
                    results[label] = result["data"]

    save("_loadImage_results.json", {"hit_count": len(results)})
    print(f"\n  loadImage total hits: {len(results)}")
    return results


# ============================================================
# PHASE 8: getCentroid with src param
# ============================================================
def phase8_getCentroid():
    print("\n" + "=" * 70)
    print("PHASE 8: getCentroid with src/srcProj")
    print("=" * 70)

    results = {}

    navi_keys = ["building_estate", "building", "route", "node"]
    gids = ["1", "2", "3"]
    src_projs = ["EPSG:3826", "EPSG:4326", "EPSG:900913"]

    for nk in navi_keys:
        for gid in gids[:2]:
            for src in src_projs[:2]:
                # Try with 'src'
                params = {"naviKey": nk, "gid": gid, "src": src}
                label = f"getCentroid nk={nk} gid={gid} src={src}"
                result = post_api("route.htm", "getCentroid", params, label)
                time.sleep(DELAY)
                if log_result(result, "info" if gid == "1" and nk == navi_keys[0] else ""):
                    save(f"centroid_{nk}_{gid}_{src.replace(':', '_')}.json", result["data"])
                    results[label] = result["data"]

                # Try with 'srcProj'
                params = {"naviKey": nk, "gid": gid, "srcProj": src}
                label = f"getCentroid nk={nk} gid={gid} srcProj={src}"
                result = post_api("route.htm", "getCentroid", params, label)
                time.sleep(DELAY)
                if log_result(result):
                    save(f"centroid_srcProj_{nk}_{gid}.json", result["data"])
                    results[label] = result["data"]

    save("_getCentroid_results.json", {"hit_count": len(results)})
    print(f"\n  getCentroid total hits: {len(results)}")
    return results


def main():
    print(f"Route.htm Final Exploitation v5 - {datetime.now().isoformat()}")
    print(f"Testing all GIPS endpoints (route, buildinfo, roominfo, campus, userrole)")
    print(f"Output: {OUTPUT_DIR}")
    print()

    # Phase 1: campus.htm
    campus_results = phase1_campus()

    # Phase 2: buildinfo.htm
    buildinfo_results = phase2_buildinfo()

    # Phase 3: roominfo.htm
    roominfo_results = phase3_roominfo()

    # Phase 4: userrole.htm (layer discovery)
    userrole_results = phase4_userrole()

    # Phase 5: dataTransmissionAPI with action in JSON
    datatrans_results = phase5_dataTransmission()

    # Phase 6: searchByDistance with srcProj
    searchdist_results = phase6_searchByDistance()

    # Phase 7: loadImageByApKey with sourceType
    loadimage_results = phase7_loadImage()

    # Phase 8: getCentroid with src
    centroid_results = phase8_getCentroid()

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

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


if __name__ == "__main__":
    main()
