#!/usr/bin/env python3
"""
Google Maps Lead Generator — find businesses without websites.

Usage:
    python3 scrape.py --query "plumbers" --location "Austin TX"
    python3 scrape.py --query "restaurants" --location "Round Rock TX" --limit 30
    python3 scrape.py -o leads.csv --query "auto repair" --location "Cedar Park TX"

Output: CSV/JSON with name, phone, rating, address, Google Maps URL
        (only businesses that have NO website listed)
"""

import argparse
import asyncio
import csv
import json
import re
import sys
from pathlib import Path
from urllib.parse import quote

from playwright.async_api import async_playwright

SCRIPT_DIR = Path(__file__).parent

async def scrape(query: str, location: str, max_results: int = 20, headless: bool = True) -> list[dict]:
    """Scrape Google Maps search results and return businesses without websites."""

    # Load JS extractor
    with open(SCRIPT_DIR / "extract.js", encoding="utf-8") as f:
        extract_js = f.read()

    pw = await async_playwright().start()
    browser = await pw.firefox.launch(headless=headless)
    context = await browser.new_context(
        viewport={"width": 1920, "height": 1080},
        locale="en-US",
        timezone_id="America/Chicago",
    )

    # Bypass consent dialog
    await context.add_cookies([
        {
            "name": "CONSENT",
            "value": "YES+cb.20240101-01-p0.en+FX+430",
            "domain": ".google.com",
            "path": "/",
        }
    ])

    # Stealth
    await context.add_init_script("""
        Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
        const orig = window.navigator.permissions.query;
        window.navigator.permissions.query = (params) =>
            params.name === 'notifications'
                ? Promise.resolve({state: Notification.permission})
                : orig(params);
    """)

    page = await context.new_page()

    # Block heavy resources
    async def block_resources(route):
        if route.request.resource_type in {"font", "media"}:
            await route.abort()
        else:
            await route.continue_()
    await page.route("**/*", block_resources)

    url = f"https://www.google.com/maps/search/{quote(f'{query}+{location}')}"
    print(f"[*] Searching: {query} in {location}")
    print(f"[*] URL: {url}")

    await page.goto(url, wait_until="domcontentloaded", timeout=30000)
    print("[*] Waiting for results to load...")
    await page.wait_for_timeout(3000)

    # Extract all business listings
    all_results = await page.evaluate(extract_js)

    # Filter: keep only businesses WITHOUT real websites
    leads = [r for r in all_results if not r.get("website")]

    print(f"\n[+] Total businesses: {len(all_results)}")
    print(f"[+] Without websites: {len(leads)}")

    await browser.close()
    await pw.stop()

    return leads[:max_results]


def save_csv(leads: list[dict], path: str):
    """Save leads to CSV."""
    fields = ["name", "phone", "rating", "reviews", "address", "maps_url"]
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
        writer.writeheader()
        writer.writerows(leads)
    print(f"[+] Saved {len(leads)} leads to {path}")


def save_json(leads: list[dict], path: str):
    """Save leads to JSON."""
    with open(path, "w", encoding="utf-8") as f:
        json.dump(leads, f, indent=2, ensure_ascii=False)
    print(f"[+] Saved {len(leads)} leads to {path}")


def main():
    parser = argparse.ArgumentParser(description="Find businesses without websites on Google Maps")
    parser.add_argument("--query", "-q", required=True, help='Business type: "plumbers"')
    parser.add_argument("--location", "-l", required=True, help='Location: "Austin TX"')
    parser.add_argument("--limit", "-n", type=int, default=20, help="Max results (default: 20)")
    parser.add_argument("--output", "-o", default=None, help="Output file path")
    parser.add_argument("--json", dest="as_json", action="store_true", help="Output as JSON")
    parser.add_argument("--visible", action="store_true", help="Show browser window")
    args = parser.parse_args()

    leads = asyncio.run(scrape(
        query=args.query,
        location=args.location,
        max_results=args.limit,
        headless=not args.visible,
    ))

    if not leads:
        print("\n[!] No leads found (all results have websites or Google blocked us)")
        sys.exit(0)

    # Print results
    print(f"\n{'='*60}")
    print(f"  LEADS: {args.query} near {args.location}")
    print(f"  {len(leads)} businesses without websites")
    print(f"{'='*60}\n")

    for i, lead in enumerate(leads, 1):
        rating = f"⭐ {lead['rating']}" if lead['rating'] else "no rating"
        reviews = f"({lead['reviews']})" if lead['reviews'] else ""
        print(f"  {i}. {lead['name']}")
        print(f"     {rating} {reviews}")
        print(f"     📍 {lead['address'] or 'N/A'}")
        print(f"     📞 {lead['phone'] or 'N/A'}")
        print(f"     🔗 {lead['maps_url']}")
        print()

    # Save
    if not args.output:
        safe_query = args.query.replace(" ", "_")
        safe_loc = args.location.replace(" ", "_").lower()
        ext = "json" if args.as_json else "csv"
        args.output = f"leads_{safe_query}_{safe_loc}.{ext}"

    if args.as_json:
        save_json(leads, args.output)
    else:
        save_csv(leads, args.output)


if __name__ == "__main__":
    main()