"""SEO Score API — Live monitoring dashboard.

A self-hosted single-file Flask dashboard that polls a list of URLs through
the SEO Score API on a schedule, renders score cards with sparklines, and
auto-refreshes in the browser.

Setup:
    pip install flask seoscoreapi
    export SEOSCORE_API_KEY=sk_your_key_here
    export DASHBOARD_URLS="https://yoursite.com,https://yoursite.com/pricing"
    python monitor_dashboard.py

Then open http://localhost:8080.

Customize:
    POLL_INTERVAL_SECONDS — how often each URL is re-audited (default 900 = 15 min)

Live demo: https://seoscoreapi.com/demo/dashboard
Blog post: https://seoscoreapi.com/blog/seo-monitoring-dashboard
"""

import os
import threading
import time
from datetime import datetime

from flask import Flask, render_template_string
from seoscoreapi import audit, history

API_KEY = os.environ["SEOSCORE_API_KEY"]
URLS = [u.strip() for u in os.environ["DASHBOARD_URLS"].split(",") if u.strip()]
POLL_INTERVAL = int(os.getenv("POLL_INTERVAL_SECONDS", "900"))

app = Flask(__name__)
state = {url: {"loading": True} for url in URLS}
state_lock = threading.Lock()


def status_for(delta):
    if delta is None:
        return "neutral"
    if delta <= -5:
        return "critical"
    if delta < 0:
        return "warning"
    return "ok"


def refresh_one(url):
    """Run an audit + fetch history for one URL, store result in `state`."""
    try:
        result = audit(url, api_key=API_KEY)
        hist = history(url, api_key=API_KEY, limit=30).get("series", [])
        prev = state.get(url, {}).get("score")
        delta = (result["score"] - prev) if prev is not None else None
        with state_lock:
            state[url] = {
                "score": result["score"],
                "grade": result["grade"],
                "categories": {k: v.get("score") for k, v in result.get("audit", {}).items()},
                "delta": delta,
                "status": status_for(delta),
                "spark": [h["score"] for h in hist[-30:]],
                "fetched_at": datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC"),
                "error": None,
            }
    except Exception as e:
        with state_lock:
            state[url] = {**state.get(url, {}), "error": str(e), "status": "error"}


def poll_loop():
    while True:
        for url in URLS:
            refresh_one(url)
        time.sleep(POLL_INTERVAL)


threading.Thread(target=poll_loop, daemon=True).start()


TEMPLATE = """
<!doctype html>
<html><head>
<meta charset="utf-8"><meta http-equiv="refresh" content="60">
<title>SEO Monitor</title>
<style>
  body { font: 14px system-ui, sans-serif; background:#0a0a0a; color:#e5e5e5; margin:0; padding:24px; }
  h1 { font-size:18px; color:#888; font-weight:500; margin:0 0 16px; }
  .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(320px,1fr)); gap:16px; }
  .card { background:#141414; border:1px solid #222; border-radius:8px; padding:16px; }
  .card.ok { border-left:4px solid #10b981; }
  .card.warning { border-left:4px solid #f59e0b; }
  .card.critical { border-left:4px solid #ef4444; }
  .card.error { border-left:4px solid #6b7280; }
  .url { color:#888; font-size:12px; word-break:break-all; margin-bottom:8px; }
  .score { font-size:42px; font-weight:600; }
  .grade { color:#888; font-size:14px; margin-left:8px; }
  .delta { display:inline-block; margin-left:12px; padding:2px 8px; border-radius:4px; font-size:12px; }
  .delta.up { background:#064e3b; color:#10b981; }
  .delta.down { background:#7f1d1d; color:#fca5a5; }
  .cats { display:flex; gap:8px; margin-top:12px; flex-wrap:wrap; font-size:11px; color:#aaa; }
  .cats span { background:#1f1f1f; padding:3px 8px; border-radius:3px; }
  .spark { margin-top:12px; height:32px; }
  .ts { color:#555; font-size:11px; margin-top:8px; }
  .err { color:#fca5a5; font-size:12px; }
</style>
</head><body>
<h1>SEO Monitor — refreshes every 60s</h1>
<div class="grid">
{% for url, data in state.items() %}
  <div class="card {{ data.get('status', 'neutral') }}">
    <div class="url">{{ url }}</div>
    {% if data.get('error') %}
      <div class="err">{{ data.error }}</div>
    {% elif data.get('loading') %}
      <div class="ts">Awaiting first audit…</div>
    {% else %}
      <div>
        <span class="score">{{ data.score }}</span>
        <span class="grade">{{ data.grade }}</span>
        {% if data.delta is not none %}
          <span class="delta {{ 'up' if data.delta >= 0 else 'down' }}">
            {{ '+' if data.delta >= 0 else '' }}{{ '%.0f' % data.delta }}
          </span>
        {% endif %}
      </div>
      <div class="cats">
        {% for cat, score in data.categories.items() %}<span>{{ cat }} {{ score }}</span>{% endfor %}
      </div>
      {% if data.spark and data.spark|length > 1 %}
      <svg class="spark" viewBox="0 0 100 30" preserveAspectRatio="none" width="100%">
        <polyline fill="none" stroke="#60a5fa" stroke-width="1.5"
          points="{% for s in data.spark %}{{ loop.index0 * (100 / (data.spark|length - 1)) }},{{ 30 - (s * 0.3) }} {% endfor %}" />
      </svg>
      {% endif %}
      <div class="ts">{{ data.fetched_at }}</div>
    {% endif %}
  </div>
{% endfor %}
</div>
</body></html>
"""


@app.route("/")
def home():
    with state_lock:
        return render_template_string(TEMPLATE, state=dict(state))


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)
