"""Deutschlandweite Lead-Suche: Firmen ohne Website (OpenStreetMap / Overpass)."""

from __future__ import annotations

import csv
import json
import time
import urllib.error
import urllib.request
from dataclasses import dataclass
from typing import Callable
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
LEADS_DIR = ROOT / "data" / "ki-web-studio-marketing"
SITE_URL = "https://www.ki-web-studio.de"

# (Stadt, Lat, Lon, Bundesland)
DEUTSCHLAND_STAEDTE: list[tuple[str, float, float, str]] = [
    ("Berlin", 52.52, 13.405, "Berlin"),
    ("Hamburg", 53.551, 9.994, "Hamburg"),
    ("München", 48.137, 11.576, "Bayern"),
    ("Köln", 50.938, 6.96, "NRW"),
    ("Frankfurt am Main", 50.111, 8.682, "Hessen"),
    ("Stuttgart", 48.775, 9.183, "Baden-Württemberg"),
    ("Düsseldorf", 51.227, 6.773, "NRW"),
    ("Leipzig", 51.34, 12.374, "Sachsen"),
    ("Dortmund", 51.513, 7.465, "NRW"),
    ("Essen", 51.456, 7.011, "NRW"),
    ("Bremen", 53.079, 8.802, "Bremen"),
    ("Dresden", 51.051, 13.738, "Sachsen"),
    ("Hannover", 52.375, 9.732, "Niedersachsen"),
    ("Nürnberg", 49.452, 11.077, "Bayern"),
    ("Duisburg", 51.434, 6.762, "NRW"),
    ("Bochum", 51.481, 7.216, "NRW"),
    ("Wuppertal", 51.256, 7.151, "NRW"),
    ("Bielefeld", 52.02, 8.532, "NRW"),
    ("Bonn", 50.737, 7.098, "NRW"),
    ("Münster", 51.96, 7.626, "NRW"),
    ("Karlsruhe", 49.007, 8.404, "Baden-Württemberg"),
    ("Mannheim", 49.487, 8.466, "Baden-Württemberg"),
    ("Augsburg", 48.371, 10.898, "Bayern"),
    ("Wiesbaden", 50.078, 8.24, "Hessen"),
    ("Chemnitz", 50.832, 12.925, "Sachsen"),
    ("Aachen", 50.776, 6.083, "NRW"),
    ("Halle (Saale)", 51.482, 11.97, "Sachsen-Anhalt"),
    ("Magdeburg", 52.12, 11.627, "Sachsen-Anhalt"),
    ("Freiburg", 47.999, 7.842, "Baden-Württemberg"),
    ("Kiel", 54.323, 10.139, "Schleswig-Holstein"),
    ("Lübeck", 53.866, 10.687, "Schleswig-Holstein"),
    ("Erfurt", 50.978, 11.029, "Thüringen"),
    ("Mainz", 49.992, 8.247, "Rheinland-Pfalz"),
    ("Rostock", 54.092, 12.14, "Mecklenburg-Vorpommern"),
    ("Kassel", 51.312, 9.48, "Hessen"),
    ("Saarbrücken", 49.24, 6.996, "Saarland"),
    ("Potsdam", 52.391, 13.066, "Brandenburg"),
    ("Regensburg", 49.013, 12.102, "Bayern"),
    ("Ingolstadt", 48.763, 11.425, "Bayern"),
    ("Würzburg", 49.794, 9.954, "Bayern"),
    ("Ulm", 48.401, 9.988, "Baden-Württemberg"),
    ("Heilbronn", 49.143, 9.211, "Baden-Württemberg"),
    ("Pforzheim", 48.893, 8.705, "Baden-Württemberg"),
    ("Göttingen", 51.541, 9.936, "Niedersachsen"),
    ("Koblenz", 50.356, 7.589, "Rheinland-Pfalz"),
    ("Jena", 50.928, 11.589, "Thüringen"),
    ("Erlangen", 49.598, 11.004, "Bayern"),
    ("Trier", 49.76, 6.644, "Rheinland-Pfalz"),
    ("Osnabrück", 52.279, 8.047, "Niedersachsen"),
    ("Oldenburg", 53.143, 8.215, "Niedersachsen"),
    ("Heidelberg", 49.398, 8.672, "Baden-Württemberg"),
    ("Darmstadt", 49.872, 8.651, "Hessen"),
    ("Wolfsburg", 52.423, 10.787, "Niedersachsen"),
    ("Reutlingen", 48.492, 9.211, "Baden-Württemberg"),
    ("Cottbus", 51.759, 14.334, "Brandenburg"),
    ("Gütersloh", 51.906, 8.385, "NRW"),
    ("Schwerin", 53.635, 11.401, "Mecklenburg-Vorpommern"),
    ("Konstanz", 47.663, 9.176, "Baden-Württemberg"),
    ("Bamberg", 49.898, 10.902, "Bayern"),
    ("Bayreuth", 49.945, 11.573, "Bayern"),
    ("Landshut", 48.537, 12.152, "Bayern"),
    ("Rosenheim", 47.856, 12.128, "Bayern"),
    ("Passau", 48.566, 13.431, "Bayern"),
    ("Kempten", 47.728, 10.315, "Bayern"),
    ("Memmingen", 47.987, 10.181, "Bayern"),
    ("Donauwörth", 48.718, 10.78, "Bayern"),
    ("Dillingen a.d.Donau", 48.578, 10.495, "Bayern"),
    ("Tapfheim", 48.65, 10.48, "Bayern"),
    ("Höchstädt a.d.Donau", 48.608, 10.807, "Bayern"),
    ("Augsburg", 48.371, 10.898, "Bayern"),
    ("Kaiserslautern", 49.444, 7.769, "Rheinland-Pfalz"),
    ("Flensburg", 54.784, 9.439, "Schleswig-Holstein"),
    ("Bremerhaven", 53.539, 8.581, "Bremen"),
    ("Hildesheim", 52.151, 9.951, "Niedersachsen"),
    ("Fulda", 50.555, 9.681, "Hessen"),
    ("Siegen", 50.875, 8.024, "NRW"),
    ("Gera", 50.878, 12.082, "Thüringen"),
    ("Cottbus", 51.759, 14.334, "Brandenburg"),
    ("Stralsund", 54.309, 13.081, "Mecklenburg-Vorpommern"),
    ("Neubrandenburg", 53.557, 13.261, "Mecklenburg-Vorpommern"),
    ("Celle", 52.623, 10.081, "Niedersachsen"),
    ("Lüneburg", 53.251, 10.414, "Niedersachsen"),
    ("Braunschweig", 52.269, 10.526, "Niedersachsen"),
    ("Gießen", 50.584, 8.678, "Hessen"),
    ("Marburg", 50.809, 8.771, "Hessen"),
    ("Wetzlar", 50.553, 8.504, "Hessen"),
    ("Offenbach", 50.1, 8.766, "Hessen"),
    ("Aschaffenburg", 49.977, 9.149, "Bayern"),
    ("Schweinfurt", 50.05, 10.232, "Bayern"),
    ("Coburg", 50.259, 10.964, "Bayern"),
    ("Weiden", 49.677, 12.157, "Bayern"),
    ("Straubing", 48.881, 12.574, "Bayern"),
    ("Freising", 48.403, 11.749, "Bayern"),
    ("Fürth", 49.477, 10.989, "Bayern"),
    ("Bamberg", 49.898, 10.902, "Bayern"),
]

# Deduplicate cities by name
_seen: set[str] = set()
STAEDTE: list[tuple[str, float, float, str]] = []
for row in DEUTSCHLAND_STAEDTE:
    if row[0] in _seen:
        continue
    _seen.add(row[0])
    STAEDTE.append(row)

RADIUS_M = 6000
OVERPASS_URL = "https://overpass-api.de/api/interpreter"
SLEEP_SEC = 5.0

from jam.ki_studio_marketing import EXTRAS_LINE, PRICE_REGULAR, PRICE_SONDER, sonderangebot_short

PITCH_BY_TYPE: dict[str, str] = {
    "hairdresser": f"Friseur-Website – SONDERANGEBOT {PRICE_SONDER} € statt {PRICE_REGULAR:.2f} €.",
    "bakery": f"Bäckerei-Website – nur {PRICE_SONDER} € statt {PRICE_REGULAR:.2f} €, Impressum inkl.",
    "butcher": f"Metzgerei online – Sonderpreis {PRICE_SONDER} €.",
    "restaurant": f"Restaurant-Website – {PRICE_SONDER} € Sonderangebot.",
    "cafe": f"Café-Website mobiloptimiert – {PRICE_SONDER} € statt {PRICE_REGULAR:.2f} €.",
    "car_repair": f"KFZ-Werkstatt-Website – {PRICE_SONDER} € Sonderangebot.",
    "dentist": f"Zahnarzt-Praxiswebsite DSGVO-konform – ab {PRICE_SONDER} €, Erweiterungen nach Absprache.",
    "craft": f"Handwerker-Website – SONDERANGEBOT {PRICE_SONDER} €.",
}


@dataclass
class Lead:
    name: str
    type: str
    city: str
    street: str
    phone: str
    email: str
    scan_city: str
    bundesland: str

    @property
    def key(self) -> str:
        return f"{self.name.lower()}|{self.city.lower()}|{self.scan_city.lower()}"

    def pitch(self) -> str:
        return PITCH_BY_TYPE.get(
            self.type,
            f"Website Sonderangebot {PRICE_SONDER} € statt {PRICE_REGULAR:.2f} € – ki-web-studio.de",
        )

    def outreach_url(self) -> str:
        from urllib.parse import urlencode

        q = urlencode(
            {
                "utm_source": "outreach",
                "utm_medium": "cold",
                "utm_campaign": "de_scan",
                "utm_content": self.scan_city.replace(" ", "_")[:40],
            }
        )
        return f"{SITE_URL}/?{q}"

    def to_row(self) -> dict[str, str]:
        return {
            "name": self.name,
            "type": self.type,
            "city": self.city,
            "street": self.street,
            "phone": self.phone,
            "email": self.email,
            "scan_city": self.scan_city,
            "bundesland": self.bundesland,
            "pitch": self.pitch(),
            "outreach_url": self.outreach_url(),
        }


def _overpass_query(lat: float, lon: float, radius_m: int = RADIUS_M) -> str:
    return f"""
[out:json][timeout:25];
(
  node["craft"](around:{radius_m},{lat},{lon});
  node["shop"~"hairdresser|bakery|butcher|car_repair"](around:{radius_m},{lat},{lon});
  node["amenity"~"restaurant|cafe|hairdresser|dentist|car_repair"](around:{radius_m},{lat},{lon});
);
out body 50;
"""


def _fetch_overpass(query: str, *, retries: int = 3) -> dict:
    req = urllib.request.Request(
        OVERPASS_URL,
        data=query.encode(),
        method="POST",
        headers={"User-Agent": "ki-web-studio-lead-scanner/1.0", "Accept": "application/json"},
    )
    last_err: Exception | None = None
    for attempt in range(retries):
        try:
            with urllib.request.urlopen(req, timeout=50) as resp:
                return json.loads(resp.read())
        except (urllib.error.HTTPError, TimeoutError, urllib.error.URLError) as e:
            last_err = e
            wait = 8 + attempt * 6
            if isinstance(e, urllib.error.HTTPError) and e.code == 429:
                wait = 30 + attempt * 20
            time.sleep(wait)
    raise RuntimeError(f"Overpass fehlgeschlagen: {last_err}")


def parse_elements(
    data: dict,
    *,
    scan_city: str,
    bundesland: str,
    fallback_city: str,
) -> list[Lead]:
    leads: list[Lead] = []
    for el in data.get("elements", []):
        tags = el.get("tags", {})
        name = tags.get("name")
        if not name:
            continue
        if tags.get("website") or tags.get("contact:website"):
            continue
        kind = tags.get("shop") or tags.get("craft") or tags.get("amenity") or ""
        city = tags.get("addr:city") or tags.get("addr:place") or fallback_city
        street = tags.get("addr:street") or ""
        phone = tags.get("phone") or tags.get("contact:phone") or ""
        email = tags.get("email") or tags.get("contact:email") or ""
        leads.append(
            Lead(
                name=name,
                type=kind,
                city=city,
                street=street,
                phone=phone,
                email=email,
                scan_city=scan_city,
                bundesland=bundesland,
            )
        )
    return leads


def scan_city(city: str, lat: float, lon: float, bundesland: str) -> list[Lead]:
    data = _fetch_overpass(_overpass_query(lat, lon))
    return parse_elements(data, scan_city=city, bundesland=bundesland, fallback_city=city)


def scan_deutschland(
    *,
    limit_cities: int | None = None,
    start_index: int = 0,
    on_progress: Callable[[int, int, str], None] | None = None,
) -> list[Lead]:
    cities = STAEDTE[start_index:]
    if limit_cities is not None:
        cities = cities[:limit_cities]

    all_leads: list[Lead] = []
    seen: set[str] = set()

    for i, (city, lat, lon, state) in enumerate(cities, start=start_index):
        if on_progress:
            on_progress(i, len(STAEDTE), city)
        try:
            batch = scan_city(city, lat, lon, state)
        except RuntimeError:
            time.sleep(8)
            try:
                batch = scan_city(city, lat, lon, state)
            except RuntimeError:
                batch = []
        for lead in batch:
            if lead.key in seen:
                continue
            seen.add(lead.key)
            all_leads.append(lead)
        time.sleep(SLEEP_SEC)
    return all_leads


def write_csv(leads: list[Lead], path: Path | None = None) -> Path:
    LEADS_DIR.mkdir(parents=True, exist_ok=True)
    out = path or LEADS_DIR / "deutschland-leads.csv"
    fields = ["name", "type", "city", "street", "phone", "email", "scan_city", "bundesland", "pitch", "outreach_url"]
    with out.open("w", encoding="utf-8-sig", newline="") as f:
        w = csv.DictWriter(f, fieldnames=fields)
        w.writeheader()
        for lead in sorted(leads, key=lambda x: (x.bundesland, x.scan_city, x.type, x.name)):
            w.writerow(lead.to_row())
    return out


def write_outreach_samples(leads: list[Lead], *, max_samples: int = 30) -> Path:
    """WhatsApp/E-Mail-Vorlagen für Top-Leads mit Telefon."""
    LEADS_DIR.mkdir(parents=True, exist_ok=True)
    out = LEADS_DIR / "outreach-vorlagen.txt"
    with_phone = [l for l in leads if l.phone]
    samples = with_phone[:max_samples] if with_phone else leads[:max_samples]
    lines = [
        "KI WEB STUDIO – Kaltakquise-Vorlagen (Deutschland-Scan)",
        SITE_URL,
        "",
        "=== ALLGEMEIN (WhatsApp / SMS) ===",
        "",
        "Hallo [Name], ich bin David – Webentwickler (ki-web-studio.de).",
        f"Ihr Betrieb [Firma] ist lokal gut auffindbar, aber ohne eigene Website.",
        f"{sonderangebot_short()} – inkl. Impressum & Mobiloptimierung.",
        EXTRAS_LINE,
        "Hätten Sie Interesse an einem kurzen, unverbindlichen Beispiel?",
        "",
        "=== E-MAIL BETREFF ===",
        f"🔥 Website Sonderangebot – nur {PRICE_SONDER} € statt {PRICE_REGULAR:.2f} €",
        "",
    ]
    for lead in samples:
        lines.extend(
            [
                f"--- {lead.name} ({lead.type}) · {lead.city} · {lead.bundesland} ---",
                f"Tel: {lead.phone or '–'}",
                f"Pitch: {lead.pitch()}",
                f"Link: {lead.outreach_url()}",
                "",
                f"Hallo, ich habe gesehen dass {lead.name} in {lead.city} noch keine eigene Website hat.",
                f"{sonderangebot_short()} – inkl. Impressum, mobil & DSGVO-konform.",
                EXTRAS_LINE,
                f"Mehr Infos: {lead.outreach_url()}",
                "",
            ]
        )
    out.write_text("\n".join(lines), encoding="utf-8")
    return out


def load_progress() -> int:
    p = LEADS_DIR / "scan-progress.json"
    if not p.is_file():
        return 0
    try:
        return int(json.loads(p.read_text(encoding="utf-8")).get("next_index", 0))
    except (json.JSONDecodeError, ValueError, OSError):
        return 0


def save_progress(next_index: int, *, leads_count: int) -> None:
    LEADS_DIR.mkdir(parents=True, exist_ok=True)
    p = LEADS_DIR / "scan-progress.json"
    p.write_text(
        json.dumps({"next_index": next_index, "leads_count": leads_count}, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
