#!/usr/bin/env python3
"""
HAR Privacy & Telemetry Analyzer for AI Chatbots
Systematically scans HTTP Archive (.har) files for:
- Canary tokens (e.g. MED-PRIVACY-7F39X2)
- Sensitive keywords (medical terms, titles, etc.)
- Known tracking, analytics, and observability endpoints
- Decodes URL-encoded query strings and JSON POST bodies
"""

import sys
import os
import json
import urllib.parse
import re
from collections import defaultdict

DEFAULT_KEYWORDS = [
    "MED-PRIVACY-7F39X2",
    "multiple sclerosis",
    "ocrelizumab",
    "prognosis",
]

TRACKER_CATEGORIES = {
    "Advertising & Ad Measurement": [
        "analytics.twitter.com",
        "t.co/i/adsct",
        "ads-twitter.com",
        "googleads",
        "doubleclick.net",
        "facebook.com/tr",
        "connect.facebook.net",
        "linkedin.com/px",
        "bat.bing.com",
    ],
    "Analytics & Tag Management": [
        "google-analytics.com",
        "googletagmanager.com",
        "segment.io",
        "amplitude.com",
        "mixpanel.com",
        "posthog.com",
        "hotjar.com",
    ],
    "Observability & RUM (Application Monitoring)": [
        "datadoghq.com",
        "browser-intake-datadoghq.com",
        "sentry.io",
        "events.data.microsoft.com",
        "OneCollector",
        "newrelic.com",
        "bugsnag.com",
    ],
    "Customer Support & Messaging": [
        "intercom.io",
        "api-iam.intercom.io",
        "zendesk.com",
        "crisp.chat",
    ],
    "Payments & Infrastructure": [
        "stripe.com",
        "m.stripe.com",
        "cloudflare.com",
        "launchdarkly.com",
    ],
}


def categorize_url(url: str) -> str:
    for cat, domains in TRACKER_CATEGORIES.items():
        if any(d in url for d in domains):
            return cat
    return "Other / First-Party / Uncategorized"


def search_in_text(text: str, keywords: list[str]) -> list[str]:
    found = []
    if not text:
        return found
    text_lower = text.lower()
    for kw in keywords:
        if kw.lower() in text_lower:
            found.append(kw)
    return found


def analyze_har(file_path: str, keywords: list[str]):
    print(f"\n=======================================================")
    print(f"Analyzing HAR: {file_path}")
    print(f"Searching for keywords: {keywords}")
    print(f"=======================================================\n")

    with open(file_path, "r", encoding="utf-8", errors="replace") as f:
        try:
            har_data = json.load(f)
        except Exception as e:
            print(f"Error reading JSON from HAR: {e}")
            return

    entries = har_data.get("log", {}).get("entries", [])
    print(f"Total requests in HAR: {len(entries)}")

    domain_counts = defaultdict(int)
    matches_found = []
    tracker_hits = defaultdict(list)

    for idx, entry in enumerate(entries):
        req = entry.get("request", {})
        res = entry.get("response", {})
        url = req.get("url", "")
        method = req.get("method", "GET")
        status = res.get("status", 0)

        # Parse host
        try:
            parsed = urllib.parse.urlparse(url)
            host = parsed.netloc
            domain_counts[host] += 1
        except Exception:
            host = "unknown"

        category = categorize_url(url)
        if category != "Other / First-Party / Uncategorized":
            tracker_hits[category].append({
                "index": idx,
                "method": method,
                "url": url,
                "status": status
            })

        # Search in URL (raw & decoded)
        decoded_url = urllib.parse.unquote(url)
        found_in_url = search_in_text(decoded_url, keywords)

        # Search in Query parameters
        query_params = req.get("queryString", [])
        found_in_params = []
        for p in query_params:
            p_val = urllib.parse.unquote(p.get("value", ""))
            hits = search_in_text(p_val, keywords)
            if hits:
                found_in_params.extend([(p.get("name"), h) for h in hits])

        # Search in Request Body / PostData
        post_data = req.get("postData", {})
        post_text = post_data.get("text", "")
        decoded_post = urllib.parse.unquote(post_text)
        found_in_post = search_in_text(decoded_post, keywords)

        # Check for Google Analytics 'dt' (document title) parameter
        ga_dt = None
        if "google-analytics.com" in url or "g/collect" in url:
            for p in query_params:
                if p.get("name") == "dt":
                    ga_dt = urllib.parse.unquote(p.get("value", ""))

        if found_in_url or found_in_params or found_in_post or ga_dt:
            matches_found.append({
                "index": idx,
                "method": method,
                "url": url[:120] + ("..." if len(url) > 120 else ""),
                "status": status,
                "category": category,
                "found_in_url": found_in_url,
                "found_in_params": found_in_params,
                "found_in_post": found_in_post,
                "ga_dt": ga_dt,
            })

    # Output Tracker Categories Summary
    print("\n--- Identified Tracking / Telemetry Endpoints ---")
    if not tracker_hits:
        print("None detected matching known categories.")
    for cat, reqs in tracker_hits.items():
        print(f"\n[{cat}] ({len(reqs)} requests):")
        unique_urls = set(r["url"].split("?")[0] for r in reqs)
        for u in unique_urls:
            print(f"  - {u}")

    # Output Keyword Matches
    print("\n--- Keyword & Canary Matches ---")
    if not matches_found:
        print("No keyword matches or canary tokens detected in requests.")
    else:
        for m in matches_found:
            print(f"\n[Entry #{m['index']}] {m['method']} {m['url']} (Status: {m['status']})")
            print(f"  Category: {m['category']}")
            if m["found_in_url"]:
                print(f"  MATCH in URL: {m['found_in_url']}")
            if m["found_in_params"]:
                print(f"  MATCH in Query Params: {m['found_in_params']}")
            if m["found_in_post"]:
                print(f"  MATCH in POST Body: {m['found_in_post']}")
            if m["ga_dt"]:
                print(f"  Google Analytics Document Title ('dt'): {m['ga_dt']}")

    print("\n-------------------------------------------------------")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 scripts/analyze_har.py <path_to_har_file> [keyword1 keyword2 ...]")
        print("       python3 scripts/analyze_har.py <path_to_har_file> --entry <entry_number>")
        sys.exit(1)

    har_path = sys.argv[1]

    if len(sys.argv) >= 4 and sys.argv[2] == "--entry":
        entry_idx = int(sys.argv[3])
        with open(har_path, "r", encoding="utf-8", errors="replace") as f:
            data = json.load(f)
        entries = data.get("log", {}).get("entries", [])
        if entry_idx < 0 or entry_idx >= len(entries):
            print(f"Entry {entry_idx} out of range (0-{len(entries)-1})")
            sys.exit(1)
        e = entries[entry_idx]
        req = e.get("request", {})
        res = e.get("response", {})
        print(f"\n=== Request Entry #{entry_idx} ===")
        print(f"Method: {req.get('method')} {req.get('url')}")
        print(f"Response Status: {res.get('status')} {res.get('statusText', '')}")
        print("\n--- Request Headers ---")
        for h in req.get("headers", []):
            val = h.get("value", "")
            if h.get("name", "").lower() in ["authorization", "cookie"] and len(val) > 40:
                val = val[:25] + f"... [TRUNCATED {len(val)} chars]"
            print(f"  {h.get('name')}: {val}")
        print("\n--- Query Parameters ---")
        for q in req.get("queryString", []):
            print(f"  {q.get('name')}: {q.get('value')}")
        post_data = req.get("postData", {}).get("text", "")
        if post_data:
            print(f"\n--- Request Body (length {len(post_data)}) ---")
            print(post_data[:1500] + ("\n...[truncated]" if len(post_data) > 1500 else ""))
        sys.exit(0)

    user_keywords = sys.argv[2:] if len(sys.argv) > 2 else DEFAULT_KEYWORDS
    analyze_har(har_path, user_keywords)
