#!/usr/bin/env python3
"""
Generator raportów-ofert SEO (HTML) na podstawie:
- danych Semstorm (clients/<domena>/semstorm_history.json),
- danych planera Google Ads (oferty/<domena>/keyword_ideas.json),
- treści strategicznej zdefiniowanej w OFFERS poniżej.

Ceny celowo pozostawione jako pola [DO USTALENIA] — do uzupełnienia przez Jacka.

Użycie:
    python scripts/reports/generate_offer_html.py                 # generuje wszystkie
    python scripts/reports/generate_offer_html.py kobiecesesje.pl # jedna domena
"""

import io
import json
import sys
from datetime import date
from pathlib import Path

if __name__ == "__main__":
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")

ROOT = Path(__file__).parent.parent.parent

# Etykieta ceny do uzupełnienia (wyróżniona w HTML klasą .todo)
P = '<span class="todo">[do ustalenia]</span>'

# Czy pokazywać sekcję „Zakres abonamentu" + wycenę.
# Wyłączone na życzenie — kod zostaje na przyszłość (oferta cenowa osobno).
SHOW_SCOPE = False

MONTH_PL = {
    "01": "sty", "02": "lut", "03": "mar", "04": "kwi", "05": "maj", "06": "cze",
    "07": "lip", "08": "sie", "09": "wrz", "10": "paź", "11": "lis", "12": "gru",
}

# ---------------------------------------------------------------------------
# Treść strategiczna per domena
# ---------------------------------------------------------------------------
OFFERS = {
    "kobiecesesje.pl": {
        "owner": "Studio Edith — Edyta",
        "branza": "Fotografia kobieca",
        "geo_label": "Kraków — pozycjonowanie lokalne",
        "url": "https://kobiecesesje.pl/",
        "lead": (
            "Strona ma już zbudowaną podstawę widoczności w Google i realny ruch z "
            "wyszukiwarki, ale od kilku miesięcy stoi w miejscu. Branża fotografii "
            "kobiecej w Krakowie ma wysoki potencjał przy niskiej konkurencji — "
            "to dobry moment, by przełamać stagnację i wejść na pozycje TOP3–TOP10 "
            "na kluczowe, sprzedażowe frazy."
        ),
        "services": [
            "Sesje buduarowe i sensualne",
            "Akty artystyczne",
            "Glamour i portrety",
            "Sesje biznesowe dla kobiet",
            "Sesje tematyczne („Super Mama”, „Pokaż Pazur!”)",
            "Vouchery prezentowe",
        ],
        # Grupy fraz — match: podciągi szukane w treści frazy (lowercase)
        "groups": [
            {"name": "Frazy lokalne (Kraków)", "match": ["kraków", "krakow", "krakow"],
             "note": "Cel #1 — intencja lokalna, bezpośrednio sprzedażowa."},
            {"name": "Sesje buduarowe / sensualne", "match": ["buduar", "sensual", "zmysł", "zmysl"],
             "note": "Główna specjalizacja studia, niska konkurencja."},
            {"name": "Sesje kobiece (ogólne)", "match": ["kobiec"],
             "note": "Frazy parasolowe o dużym wolumenie."},
            {"name": "Biznes / portret", "match": ["biznes", "portret"],
             "note": "Dodatkowe segmenty z realnym popytem."},
        ],
        "strategy": [
            ("Optymalizacja lokalna pod Kraków",
             "Dopracowanie Google Business Profile, NAP, treści lokalnych i fraz z dopełnieniem "
             "„Kraków”. To najszybsza droga do ruchu o wysokiej intencji zakupowej."),
            ("Rozbudowa podstron ofertowych",
             "Dla każdego typu sesji (buduar, sensualna, biznesowa, portret) osobna, dopracowana "
             "podstrona docelowa pod konkretną frazę z wolumenem — zamiast jednej ogólnej oferty."),
            ("Treści sprzedażowe i poradnikowe",
             "Artykuły pod frazy transakcyjne i informacyjne (np. „sesja sensualna cena”, "
             "„jak przygotować się do sesji buduarowej”) budujące ruch i zaufanie."),
            ("SEO techniczne i linkowanie",
             "Poprawa szybkości, struktury nagłówków, danych strukturalnych oraz pozyskiwanie "
             "wartościowych linków lokalnych i branżowych."),
        ],
        "scope": [
            ("Audyt SEO + analiza konkurencji lokalnej", "jednorazowo, w cenie pierwszego miesiąca"),
            ("Optymalizacja techniczna on-site", "stała"),
            ("Optymalizacja i rozbudowa podstron sesji", "stała"),
            ("Google Business Profile (lokalne SEO)", "stała"),
            ("Treści / blog", f"{P} artykułów miesięcznie"),
            ("Link building lokalny", "stały"),
            ("Raport miesięczny (Semstorm + pozycje)", "co miesiąc"),
        ],
    },
    "coachingdlakobiet.pl": {
        "owner": "Edyta Szalacha — Coaching dla kobiet",
        "branza": "Coaching / rozwój osobisty",
        "geo_label": "Cała Polska — usługi online",
        "url": "https://coachingdlakobiet.pl/",
        "lead": (
            "Strona jest praktycznie niewidoczna w Google — to start od zera. Z jednej strony "
            "oznacza to dłuższą perspektywę (pierwsze realne efekty zwykle po 4–6 miesiącach, "
            "pełne po 6–12), z drugiej — pełną swobodę w zbudowaniu SEO od fundamentów. Sama "
            "fraza „coaching dla kobiet” jest niszowa, dlatego strategia opiera się na szerszych "
            "frazach parasolowych (rozwój osobisty, pewność siebie, kariera) i długim ogonie."
        ),
        "services": [
            "Sesje coachingowe indywidualne (online i stacjonarnie)",
            "Program „Odkryj siebie” — samopoznanie i wartości",
            "Program „Spełniona zawodowo” — kariera i motywacja",
            "Program „Mocna Mama” — wsparcie dla matek",
            "Program „Re(we)lacje” — budowanie relacji",
            "Pakiety sesji (Rosa / Wicher / Ogień)",
        ],
        "groups": [
            {"name": "Coaching dla kobiet (nisza docelowa)", "match": ["kobiet"],
             "note": "Precyzyjna intencja — niski wolumen, ale wysoka konwersja."},
            {"name": "Rozwój osobisty (frazy parasolowe)", "match": ["rozwój osob", "rozwoj osob", "samorozw"],
             "note": "Duży wolumen — fundament ruchu i autorytetu."},
            {"name": "Coaching kariery / biznes", "match": ["karier", "biznes", "zawodow"],
             "note": "Segment o wysokiej wartości klienta."},
            {"name": "Pewność siebie / motywacja", "match": ["uwierzy", "pewno", "motyw", "siebie"],
             "note": "Frazy informacyjne pod treści blogowe."},
        ],
        "strategy": [
            ("Fundament SEO od zera",
             "Poprawna indeksacja, architektura informacji, optymalizacja techniczna i podstrony "
             "pod każdy program coachingowy — bez tego strona nie zacznie się pozycjonować."),
            ("Content marketing i autorytet (E-E-A-T)",
             "Coaching to temat „wrażliwy” dla Google (rozwój, decyzje życiowe), dlatego kluczowe są "
             "eksperckie treści blogowe pod frazy informacyjne, budujące zaufanie i autorytet autorki."),
            ("Frazy parasolowe + długi ogon",
             "Pozycjonowanie pod szersze frazy o dużym wolumenie (rozwój osobisty, pewność siebie) "
             "oraz dziesiątki fraz długiego ogona o precyzyjnej intencji."),
            ("Budowa profilu linków i marki",
             "Stopniowe pozyskiwanie linków i wzmianek (artykuły gościnne, katalogi, media kobiece) "
             "budujące widoczność marki w skali ogólnopolskiej."),
        ],
        "scope": [
            ("Audyt SEO + analiza konkurencji ogólnopolskiej", "jednorazowo, w cenie pierwszego miesiąca"),
            ("Fundament techniczny i indeksacja", "stała"),
            ("Optymalizacja podstron programów", "stała"),
            ("Content marketing / blog ekspercki", f"{P} artykułów miesięcznie"),
            ("Link building i budowa marki", "stały"),
            ("Raport miesięczny (Semstorm + pozycje)", "co miesiąc"),
        ],
    },
}


# ---------------------------------------------------------------------------
# Wczytywanie danych
# ---------------------------------------------------------------------------
def load_semstorm(domain):
    path = ROOT / "clients" / domain / "semstorm_history.json"
    if not path.exists():
        return []
    return json.loads(path.read_text(encoding="utf-8"))


def load_keywords(domain):
    path = ROOT / "oferty" / domain / "keyword_ideas.json"
    if not path.exists():
        return {"ideas": []}
    return json.loads(path.read_text(encoding="utf-8"))


def fmt(n):
    return f"{n:,}".replace(",", " ")


COMP_CLASS = {"LOW": "low", "MEDIUM": "med", "HIGH": "high", "UNKNOWN": "unk"}
COMP_LABEL = {"LOW": "niska", "MEDIUM": "średnia", "HIGH": "wysoka", "UNKNOWN": "—"}


# ---------------------------------------------------------------------------
# Render sekcji
# ---------------------------------------------------------------------------
def render_semstorm(history):
    if not history:
        return (
            '<div class="callout warn"><strong>Widoczność w Google: praktycznie zerowa.</strong> '
            'Domena nie notuje istotnych pozycji w wynikach wyszukiwania — Semstorm nie zwraca danych '
            'historycznych. To punkt startowy „od zera”: cała widoczność jest dopiero do zbudowania.</div>'
        )
    cur = history[-1]
    prev = history[-2] if len(history) > 1 else None

    def card(label, key, hint=""):
        val = cur.get(key, 0)
        delta = ""
        if prev:
            d = val - prev.get(key, 0)
            cls = "up" if d > 0 else ("down" if d < 0 else "flat")
            sign = "+" if d > 0 else ""
            arrow = "▲" if d > 0 else ("▼" if d < 0 else "▬")
            delta = f'<span class="delta {cls}">{arrow} {sign}{d}</span>'
        return (
            f'<div class="metric"><div class="metric-val">{fmt(val)}{delta}</div>'
            f'<div class="metric-lbl">{label}</div>'
            f'<div class="metric-hint">{hint}</div></div>'
        )

    cards = (
        card("TOP 3", "top3", "frazy na pozycjach 1–3") +
        card("TOP 10", "top10", "pierwsza strona Google") +
        card("TOP 50", "top50", "frazy w zasięgu") +
        card("Ruch / mc", "traffic", "szacowane wizyty z SEO")
    )

    # Tabela historii
    rows = ""
    for e in history:
        y, m = e["month"].split("-")
        rows += (
            f'<tr><td>{MONTH_PL.get(m, m)} {y}</td><td>{e["top3"]}</td><td>{e["top10"]}</td>'
            f'<td>{e["top20"]}</td><td>{e["top50"]}</td><td>{e["top100"]}</td>'
            f'<td>{fmt(e["traffic"])}</td></tr>'
        )
    table = (
        '<table class="data"><thead><tr><th>Miesiąc</th><th>TOP3</th><th>TOP10</th>'
        '<th>TOP20</th><th>TOP50</th><th>TOP100</th><th>Ruch</th></tr></thead>'
        f'<tbody>{rows}</tbody></table>'
    )
    return f'<div class="metrics">{cards}</div>{table}'


def render_keyword_groups(offer, kw):
    ideas = kw.get("ideas", [])
    total = len(ideas)
    total_vol = sum(i["avg_monthly_searches"] for i in ideas)
    used = set()
    blocks = ""

    for g in offer["groups"]:
        matched = []
        for i in ideas:
            t = i["keyword"].lower()
            if i["keyword"] in used:
                continue
            if any(m in t for m in g["match"]):
                matched.append(i)
                used.add(i["keyword"])
        matched = sorted(matched, key=lambda x: x["avg_monthly_searches"], reverse=True)[:12]
        if not matched:
            continue
        rows = ""
        for i in matched:
            bid = ""
            if i["high_top_bid_pln"]:
                bid = f'{i["low_top_bid_pln"]:.2f}–{i["high_top_bid_pln"]:.2f} zł'
            rows += (
                f'<tr><td>{i["keyword"]}</td><td class="num">{fmt(i["avg_monthly_searches"])}</td>'
                f'<td class="num muted">{bid}</td></tr>'
            )
        grp_vol = sum(i["avg_monthly_searches"] for i in matched)
        blocks += (
            f'<div class="kwgroup"><h4>{g["name"]} '
            f'<span class="kwsum">~{fmt(grp_vol)} wyszukiwań/mc</span></h4>'
            f'<p class="note">{g["note"]}</p>'
            '<table class="data kw"><thead><tr><th>Fraza</th><th class="num">Śr. wyszukiwań/mc</th>'
            '<th class="num">Stawka CPC (ref.)</th></tr></thead>'
            f'<tbody>{rows}</tbody></table></div>'
        )

    summary = (
        f'<div class="callout"><strong>{fmt(total)} powiązanych fraz</strong> zidentyfikowanych '
        f'w planerze Google Ads, łącznie <strong>~{fmt(total_vol)} wyszukiwań miesięcznie</strong>. '
        'Poniżej najważniejsze grupy tematyczne (dane: Google Ads Keyword Planner, geolokalizacja Polska).</div>'
    )
    return summary + blocks


def render_list(items):
    return "".join(f"<li>{x}</li>" for x in items)


def render_strategy(strategy):
    out = ""
    for idx, (title, desc) in enumerate(strategy, 1):
        out += (
            f'<div class="step"><div class="step-num">{idx}</div>'
            f'<div><h4>{title}</h4><p>{desc}</p></div></div>'
        )
    return out


def render_scope_section(offer):
    """Sekcja „Zakres abonamentu" + wycena. Renderowana tylko gdy SHOW_SCOPE."""
    if not SHOW_SCOPE:
        return ""
    return f"""
  <section>
    <h2><span class="ic">📦</span> Zakres abonamentu</h2>
    {render_scope(offer['scope'])}
    <div class="pricebox">
      <h3>Wycena</h3>
      <div class="priceline"><span>Abonament miesięczny (netto)</span><span>{P} zł / mc</span></div>
      <div class="priceline"><span>Rabat lojalnościowy (przy obecnych współpracach)</span><span>{P} %</span></div>
      <div class="priceline"><span>Okres umowy / wypowiedzenie</span><span>{P}</span></div>
      <p class="note" style="margin-top:12px;">Efekty pozycjonowania są stopniowe — pierwsze wzrosty zwykle
      po 2–4 miesiącach (strona z widocznością) lub 4–6 miesiącach (start od zera).</p>
    </div>
  </section>"""


def render_scope(scope):
    rows = ""
    for name, detail in scope:
        rows += f'<tr><td>{name}</td><td class="muted">{detail}</td></tr>'
    return (
        '<table class="data scope"><thead><tr><th>Zakres działań</th><th>Częstotliwość</th></tr></thead>'
        f'<tbody>{rows}</tbody></table>'
    )


# ---------------------------------------------------------------------------
# Szablon HTML
# ---------------------------------------------------------------------------
def build_html(domain, offer, history, kw):
    today = date.today().strftime("%d.%m.%Y")
    return f"""<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Oferta pozycjonowania — {domain}</title>
<style>
  :root {{
    --ink:#1f1830; --muted:#7a7290; --line:#ece8f2; --bg:#faf8fd;
    --brand:#b14d8e; --brand2:#7b4d9e; --soft:#f6eef4;
    --low:#1f9d6b; --med:#d18b1f; --high:#c0392b;
  }}
  * {{ box-sizing:border-box; }}
  body {{ margin:0; font-family:-apple-system,'Segoe UI',Roboto,Arial,sans-serif;
    color:var(--ink); background:var(--bg); line-height:1.6; font-size:15px; }}
  .wrap {{ max-width:920px; margin:0 auto; padding:0 22px 80px; }}
  header.hero {{ background:linear-gradient(135deg,var(--brand),var(--brand2));
    color:#fff; padding:54px 22px 46px; }}
  header.hero .wrap {{ padding-bottom:0; }}
  .eyebrow {{ text-transform:uppercase; letter-spacing:.16em; font-size:12px; opacity:.85; margin:0 0 10px; }}
  header.hero h1 {{ margin:0 0 6px; font-size:34px; line-height:1.15; }}
  header.hero .sub {{ font-size:18px; opacity:.95; margin:0 0 18px; }}
  .pills span {{ display:inline-block; background:rgba(255,255,255,.18); border:1px solid rgba(255,255,255,.3);
    padding:5px 12px; border-radius:999px; font-size:13px; margin:0 8px 8px 0; }}
  section {{ margin:40px 0 0; }}
  h2 {{ font-size:23px; margin:0 0 4px; padding-bottom:10px; border-bottom:3px solid var(--soft);
    display:flex; align-items:center; gap:10px; }}
  h2 .ic {{ width:30px;height:30px;border-radius:8px;background:var(--soft);color:var(--brand);
    display:inline-flex;align-items:center;justify-content:center;font-size:16px; }}
  h2 .n {{ color:var(--brand); font-weight:800; }}
  .lead {{ font-size:16px; color:#3a3350; }}
  .metrics {{ display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin:18px 0; }}
  .metric {{ background:#fff; border:1px solid var(--line); border-radius:14px; padding:16px;
    box-shadow:0 2px 10px rgba(123,77,158,.05); }}
  .metric-val {{ font-size:28px; font-weight:800; display:flex; align-items:baseline; gap:8px; }}
  .metric-lbl {{ font-weight:700; margin-top:2px; }}
  .metric-hint {{ font-size:12px; color:var(--muted); }}
  .delta {{ font-size:13px; font-weight:700; }}
  .delta.up {{ color:var(--low); }} .delta.down {{ color:var(--high); }} .delta.flat {{ color:var(--muted); }}
  table.data {{ width:100%; border-collapse:collapse; margin:14px 0; background:#fff;
    border:1px solid var(--line); border-radius:12px; overflow:hidden; font-size:14px; }}
  table.data th {{ background:var(--soft); text-align:left; padding:10px 12px; font-size:12.5px;
    text-transform:uppercase; letter-spacing:.04em; color:#5d5470; }}
  table.data td {{ padding:9px 12px; border-top:1px solid var(--line); }}
  td.num, th.num {{ text-align:right; }}
  .muted {{ color:var(--muted); }}
  .badge {{ display:inline-block; padding:2px 10px; border-radius:999px; font-size:12px; font-weight:700; }}
  .badge.low {{ background:#e6f6ee; color:var(--low); }}
  .badge.med {{ background:#fcf1dd; color:var(--med); }}
  .badge.high {{ background:#fbe7e4; color:var(--high); }}
  .badge.unk {{ background:#eee; color:#888; }}
  .callout {{ background:#fff; border-left:4px solid var(--brand); border-radius:10px;
    padding:14px 16px; margin:14px 0; box-shadow:0 2px 8px rgba(0,0,0,.03); }}
  .callout.warn {{ border-left-color:var(--med); background:#fffdf7; }}
  .kwgroup {{ margin:22px 0; }}
  .kwgroup h4 {{ margin:0 0 2px; font-size:16px; display:flex; justify-content:space-between; align-items:baseline; flex-wrap:wrap; gap:8px; }}
  .kwsum {{ font-size:13px; color:var(--brand); font-weight:700; }}
  .note {{ margin:0 0 8px; font-size:13px; color:var(--muted); }}
  .step {{ display:flex; gap:14px; margin:0 0 16px; }}
  .step-num {{ flex:none; width:34px; height:34px; border-radius:50%;
    background:linear-gradient(135deg,var(--brand),var(--brand2)); color:#fff;
    font-weight:800; display:flex; align-items:center; justify-content:center; }}
  .step h4 {{ margin:4px 0 2px; font-size:16px; }}
  .step p {{ margin:0; color:#4a4360; }}
  ul.svc {{ columns:2; gap:24px; padding-left:18px; }}
  ul.svc li {{ margin:0 0 6px; }}
  .todo {{ background:#fff3cd; color:#8a6d00; border:1px dashed #e0b500;
    padding:1px 8px; border-radius:6px; font-weight:700; font-size:13px; }}
  .pricebox {{ background:#fff; border:2px dashed var(--brand); border-radius:14px; padding:20px; margin:16px 0; }}
  .pricebox h3 {{ margin:0 0 10px; }}
  .priceline {{ display:flex; justify-content:space-between; align-items:center; padding:8px 0;
    border-top:1px dashed var(--line); }}
  .priceline:first-of-type {{ border-top:0; }}
  footer {{ margin-top:50px; padding-top:20px; border-top:1px solid var(--line);
    font-size:13px; color:var(--muted); }}
  @media (max-width:640px) {{ .metrics {{ grid-template-columns:repeat(2,1fr); }} ul.svc {{ columns:1; }} }}
</style>
</head>
<body>
<header class="hero">
  <div class="wrap">
    <p class="eyebrow">Oferta pozycjonowania (SEO) · {today}</p>
    <h1>{domain}</h1>
    <p class="sub">{offer['owner']}</p>
    <div class="pills">
      <span>🎯 {offer['branza']}</span>
      <span>📍 {offer['geo_label']}</span>
      <span>🔗 {offer['url']}</span>
    </div>
  </div>
</header>
<div class="wrap">

  <section>
    <h2><span class="ic">📋</span> Podsumowanie sytuacji</h2>
    <p class="lead">{offer['lead']}</p>
    <h4 style="margin:18px 0 6px;">Usługi serwisu</h4>
    <ul class="svc">{render_list(offer['services'])}</ul>
  </section>

  <section>
    <h2><span class="ic">📊</span> Obecna widoczność w Google <span class="n">(Semstorm)</span></h2>
    {render_semstorm(history)}
  </section>

  <section>
    <h2><span class="ic">🔍</span> Potencjał słów kluczowych <span class="n">(Google Ads)</span></h2>
    {render_keyword_groups(offer, kw)}
  </section>

  <section>
    <h2><span class="ic">🚀</span> Na czym się skupimy — strategia</h2>
    {render_strategy(offer['strategy'])}
  </section>
{render_scope_section(offer)}
  <footer>
    Raport przygotowany na podstawie danych Semstorm oraz Google Ads Keyword Planner (geolokalizacja Polska).
    Wolumeny wyszukiwań i stawki CPC mają charakter orientacyjny. · Dokument wewnętrzny / ofertowy.
  </footer>
</div>
</body>
</html>"""


def generate(domain):
    offer = OFFERS[domain]
    history = load_semstorm(domain)
    kw = load_keywords(domain)
    html = build_html(domain, offer, history, kw)
    out = ROOT / "oferty" / domain / "oferta.html"
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_text(html, encoding="utf-8")
    print(f"Wygenerowano: {out}")


def main():
    targets = sys.argv[1:] or list(OFFERS.keys())
    for d in targets:
        if d not in OFFERS:
            print(f"Nieznana domena: {d} (dostępne: {', '.join(OFFERS)})")
            continue
        generate(d)


if __name__ == "__main__":
    main()
