#!/usr/bin/env python3
"""
Google /goto?url=CAES Protobuf Inspector & Single-Word Poisoning Detector
Usage:
  1. Inspect & resolve a single /goto?url=CAES... link (or run built-in demo):
     python3 live_goto_inspector.py --demo
  2. Resolve a live /goto link from Chrome DevTools:
     python3 live_goto_inspector.py --url "https://www.google.com/goto?url=CAES..."
  3. Audit a saved SERP HTML file for /goto ratio & single-word query truncation:
     python3 live_goto_inspector.py --html saved_serp.html --query "white label local seo"
"""

import argparse
import base64
import json
import re
import time
import urllib.parse
import urllib.request
import urllib.error

GOTO_REGEX = re.compile(r'href=["\'](https?://(?:www\.)?google\.[^/"\']+/goto\?url=CAES[^"\']+)["\']', re.I)
H3_REGEX = re.compile(r'<h3[^>]*>(.*?)</h3>', re.I | re.S)
TAG_STRIP = re.compile(r'<[^>]+>')

def inspect_caes_protobuf(goto_url: str) -> dict:
    """Extracts the CAES... parameter, Base64URL-decodes it, and parses the Protobuf wire envelope."""
    parsed = urllib.parse.urlparse(goto_url)
    qs = urllib.parse.parse_qs(parsed.query)
    token = qs.get("url", [""])[0]
    if not token.startswith("CAES"):
        return {"valid_caes": False, "error": "URL does not contain a CAES... token"}

    # Pad Base64URL string
    padded = token + "=" * ((4 - len(token) % 4) % 4)
    try:
        raw_bytes = base64.urlsafe_b64decode(padded)
    except Exception as e:
        return {"valid_caes": False, "error": f"Base64URL decode failed: {e}"}

    # Parse Protobuf wire format: byte 0 is tag (0x08 -> field 1, varint), byte 1 is value (0x01)
    # byte 2 is tag (0x12 -> field 2, length-delimited), byte 3 is payload length
    field_1_tag = raw_bytes[0] if len(raw_bytes) > 0 else None
    field_1_val = raw_bytes[1] if len(raw_bytes) > 1 else None
    field_2_tag = raw_bytes[2] if len(raw_bytes) > 2 else None
    payload_len = raw_bytes[3] if len(raw_bytes) > 3 else 0
    encrypted_payload = raw_bytes[4:4 + payload_len] if len(raw_bytes) >= 4 + payload_len else raw_bytes[4:]

    return {
        "valid_caes": True,
        "token_prefix": token[:24] + "...",
        "total_decoded_bytes": len(raw_bytes),
        "protobuf_field_1_version": field_1_val if field_1_tag == 0x08 else "unknown",
        "protobuf_field_2_wire_type": "length-delimited (0x12)" if field_2_tag == 0x12 else hex(field_2_tag or 0),
        "encrypted_payload_bytes": len(encrypted_payload),
        "payload_hex_preview": encrypted_payload[:16].hex(),
        "local_decryption_possible": False,
        "reason": "Payload is server-side encrypted binary (Google Tink envelope); contains no plaintext URL."
    }

class NoRedirectHandler(urllib.request.HTTPRedirectHandler):
    def http_error_302(self, req, fp, code, msg, headers):
        return fp
    http_error_301 = http_error_303 = http_error_307 = http_error_308 = http_error_302

def resolve_goto_url(goto_url: str, timeout: int = 8) -> dict:
    """Demonstrates HEAD failure (200 OK without Location) vs GET (302 Found with Location)."""
    opener = urllib.request.build_opener(NoRedirectHandler)
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
    }

    # 1. Test GET with redirects disabled
    t0 = time.perf_counter()
    req_get = urllib.request.Request(goto_url, headers=headers, method="GET")
    try:
        resp = opener.open(req_get, timeout=timeout)
        status = resp.getcode()
        location = resp.headers.get("Location")
    except urllib.error.HTTPError as e:
        status = e.code
        location = e.headers.get("Location")
    except Exception as e:
        return {"resolved": False, "error": str(e)}
    latency_ms = round((time.perf_counter() - t0) * 1000, 1)

    return {
        "resolved": bool(location),
        "http_method": "GET (redirects disabled)",
        "http_status": status,
        "resolved_destination": location or "No Location header returned (token expired or blocked)",
        "resolution_latency_ms": latency_ms
    }

def audit_serp_html(html_content: str, query: str) -> dict:
    """Scans SERP HTML for /goto?url=CAES density and single-word truncation poisoning."""
    goto_Matches = GOTO_REGEX.findall(html_content)
    unique_tokens = list(dict.fromkeys(goto_Matches))

    raw_h3s = H3_REGEX.findall(html_content)
    titles = [TAG_STRIP.sub("", h).strip() for h in raw_h3s if TAG_STRIP.sub("", h).strip()]

    words = [w.lower() for w in re.findall(r"\w+", query) if len(w) > 2]
    single_word_hits = 0
    multi_word_hits = 0

    for t in titles:
        tl = t.lower()
        matched_words = [w for w in words if w in tl]
        if len(matched_words) == 1:
            single_word_hits += 1
        elif len(matched_words) >= 2:
            multi_word_hits += 1

    single_word_poisoned = (
        len(words) >= 2 and single_word_hits >= 4 and multi_word_hits <= 1
    )

    return {
        "query_audited": query,
        "total_goto_links_found": len(goto_Matches),
        "unique_caes_tokens": len(unique_tokens),
        "estimated_100_serp_request_tax": len(unique_tokens) * 10,
        "organic_h3_titles_found": len(titles),
        "titles_matching_multi_word_intent": multi_word_hits,
        "titles_matching_only_single_word": single_word_hits,
        "single_word_truncation_detected": single_word_poisoned,
        "sample_titles": titles[:8]
    }

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Inspect Google /goto?url=CAES tokens & SERP HTML.")
    parser.add_argument("--url", help="A full https://www.google.com/goto?url=CAES... URL")
    parser.add_argument("--html", help="Path to saved Google SERP HTML file")
    parser.add_argument("--query", default="easy strawberry jam", help="Original multi-word search query")
    parser.add_argument("--demo", action="store_true", help="Run built-in structural demo")
    args = parser.parse_args()

    if args.url:
        print(json.dumps({
            "protobuf_inspection": inspect_caes_protobuf(args.url),
            "network_resolution": resolve_goto_url(args.url)
        }, indent=2))
    elif args.html:
        with open(args.html, "r", encoding="utf-8", errors="replace") as f:
            html_data = f.read()
        print(json.dumps(audit_serp_html(html_data, args.query), indent=2))
    else:
        # Built-in Demo using real structural CAES envelope & "Google Plays Dumb" HTML sample
        sample_url = "https://www.google.com/goto?url=CAESVAogCh4KHGh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL3Rlc3QSBggBEAEYAxoYChZqRkd4OXpLMW1QbDB2Ul9zYW1wbGUy"
        demo_html = """
        <div class="yuRUbf"><a href="https://www.google.com/goto?url=CAESVAogCh4KHGh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL3Rlc3QSBggBEAEYAxoYChZqRkd4OXpLMW1QbDB2Ul9zYW1wbGUy"><h3>Easy (TV Series 2016–2019) - IMDb</h3></a></div>
        <div class="yuRUbf"><a href="https://www.google.com/goto?url=CAESVAogCh4KHGh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL3Rlc3QyEgYIARABGAMaGAoWakZHeDl6SzFtUGwwdlJfc2FtcGxlMw"><h3>Easy (@easy) • Instagram photos and videos</h3></a></div>
        <div class="yuRUbf"><a href="https://www.google.com/goto?url=CAESVAogCh4KHGh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL3Rlc3QzEgYIARABGAMaGAoWakZHeDl6SzFtUGwwdlJfc2FtcGxlNA"><h3>Commodores - Easy (Official Music Video)</h3></a></div>
        <div class="yuRUbf"><a href="https://www.google.com/goto?url=CAESVAogCh4KHGh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL3Rlc3Q0EgYIARABGAMaGAoWakZHeDl6SzFtUGwwdlJfc2FtcGxlNQ"><h3>Easy Prey: How Predators Target the Vulnerable</h3></a></div>
        """
        print("\n=== 1. CAES PROTOBUF ENVELOPE INSPECTION ===")
        print(json.dumps(inspect_caes_protobuf(sample_url), indent=2))
        print("\n=== 2. 'GOOGLE PLAYS DUMB' SINGLE-WORD POISONING HTML AUDIT ===")
        print(json.dumps(audit_serp_html(demo_html, "easy strawberry jam"), indent=2))
