from __future__ import annotations

import csv
import html
import json
import re
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List

APP_REPORT_VERSION = "1.4.2"
APP_COPYRIGHT = "Copyright © 2026 tamerserbes.com"
APP_WEBSITE = "https://tamerserbes.com"


def safe_filename(value: str) -> str:
    value = re.sub(r"^https?://", "", value or "site", flags=re.I)
    value = re.sub(r"[^a-zA-Z0-9._-]+", "_", value).strip("_")
    return value[:80] or "site"


def esc(value: Any) -> str:
    return html.escape(str(value if value is not None else ""))


def status_class(status: str) -> str:
    return {"success": "success", "warning": "warning", "error": "error", "info": "info"}.get(status, "info")


def action_plan_items(result: Dict[str, Any], limit: int = 12) -> List[str]:
    pages = result.get("pages") or [result]
    counter: Dict[str, int] = {}
    for page in pages:
        for check in page.get("checks") or []:
            if check.get("status") in {"error", "warning"}:
                rec = str(check.get("recommendation") or check.get("detail") or "").strip()
                if rec:
                    counter[rec] = counter.get(rec, 0) + 1
    ordered = sorted(counter.items(), key=lambda item: item[1], reverse=True)[:limit]
    return [f"{count} sayfa: {text}" if len(pages) > 1 else text for text, count in ordered]


def _action_plan_html(result: Dict[str, Any]) -> str:
    items = action_plan_items(result)
    rows = [f"<li>{esc(item)}</li>" for item in items]
    return f"<div class='card'><h2>Öncelikli Aksiyon Planı</h2><ul>{''.join(rows) if rows else '<li>Kritik aksiyon bulunamadı.</li>'}</ul></div>"


def _report_css() -> str:
    return """
:root{--bg:#0f172a;--card:#111827;--soft:#1f2937;--text:#e5e7eb;--muted:#9ca3af;--success:#22c55e;--warning:#f59e0b;--error:#ef4444;--info:#38bdf8}
*{box-sizing:border-box}body{margin:0;font-family:Arial,Helvetica,sans-serif;background:linear-gradient(135deg,#020617,#111827);color:var(--text)}.container{max-width:1220px;margin:0 auto;padding:28px}.header{display:grid;grid-template-columns:1fr auto;gap:22px;align-items:center;margin-bottom:22px}.brand{color:#93c5fd;letter-spacing:.08em;text-transform:uppercase;font-size:13px;font-weight:700}h1{margin:6px 0 8px;font-size:34px}.url{color:var(--muted);word-break:break-all}.score{width:150px;height:150px;border-radius:50%;display:grid;place-items:center;background:conic-gradient(#22c55e var(--scoreDeg),#334155 0deg);box-shadow:0 15px 40px rgba(0,0,0,.35)}.score-inner{width:118px;height:118px;border-radius:50%;background:#0f172a;display:grid;place-items:center;text-align:center}.score b{font-size:38px}.grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:22px 0}.card{background:rgba(17,24,39,.88);border:1px solid rgba(148,163,184,.18);border-radius:18px;padding:18px;box-shadow:0 15px 30px rgba(0,0,0,.18)}.card .label{color:var(--muted);font-size:13px;margin-bottom:7px}.card .value{font-size:20px;font-weight:700;word-break:break-word}table{width:100%;border-collapse:collapse;overflow:hidden;border-radius:16px}th,td{padding:13px;text-align:left;border-bottom:1px solid rgba(148,163,184,.16);vertical-align:top}th{background:#1e293b;color:#bfdbfe;font-size:13px}tr:hover td{background:rgba(30,41,59,.45)}.badge{display:inline-block;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700}.badge.success{background:rgba(34,197,94,.15);color:var(--success)}.badge.warning{background:rgba(245,158,11,.15);color:var(--warning)}.badge.error{background:rgba(239,68,68,.15);color:var(--error)}.badge.info{background:rgba(56,189,248,.15);color:var(--info)}h2{margin-top:28px}ul{line-height:1.7}.footer{color:var(--muted);text-align:center;margin-top:32px;font-size:13px}.pagebox{margin:18px 0}.small{font-size:13px;color:var(--muted)}@media(max-width:900px){.header,.grid{grid-template-columns:1fr}.score{margin:0 auto}}
"""


def _checks_table(checks: List[Dict[str, Any]]) -> str:
    rows = []
    for item in checks:
        cls = status_class(item.get("status", "info"))
        rows.append(f"""
        <tr>
            <td>{esc(item.get('category'))}</td>
            <td><strong>{esc(item.get('name'))}</strong></td>
            <td><span class="badge {cls}">{esc(item.get('status_label'))}</span></td>
            <td>{esc(item.get('score'))}/{esc(item.get('max_score'))}</td>
            <td>{esc(item.get('detail'))}</td>
            <td>{esc(item.get('recommendation'))}</td>
        </tr>""")
    return "".join(rows) or "<tr><td colspan='6'>Kontrol sonucu yok.</td></tr>"


def _page_summary_cards(result: Dict[str, Any]) -> str:
    return f"""
    <div class="grid">
        <div class="card"><div class="label">HTTP Durumu</div><div class="value">{esc(result.get('status_code'))}</div></div>
        <div class="card"><div class="label">Yüklenme</div><div class="value">{esc(result.get('load_ms'))} ms</div></div>
        <div class="card"><div class="label">Sayfa Boyutu</div><div class="value">{result.get('raw_size', 0) / 1024:.0f} KB</div></div>
        <div class="card"><div class="label">Rapor Tarihi</div><div class="value">{esc(result.get('created_at'))}</div></div>
    </div>"""


def _page_detail_block(result: Dict[str, Any]) -> str:
    missing_alt = result.get("missing_alt_images", [])[:30]
    missing_alt_rows = [f"<li>{esc(img.get('src') or 'src boş')}</li>" for img in missing_alt]
    broken = ((result.get("link_report") or {}).get("broken_links") or [])[:30]
    broken_rows = [f"<li>{esc(link.get('url'))} — HTTP {esc(link.get('status_code'))}</li>" for link in broken]
    return f"""
    <div class="card">
        <h2>Özet</h2>
        <p><strong>Title:</strong> {esc(result.get('title'))}</p>
        <p><strong>Meta Description:</strong> {esc(result.get('meta_description'))}</p>
        <p><strong>H1:</strong> {esc(' | '.join(result.get('h1') or []))}</p>
        <p><strong>Canonical:</strong> {esc(result.get('canonical'))}</p>
        <p><strong>HTML Lang:</strong> {esc(result.get('html_lang') or '-')} — <strong>Charset:</strong> {esc(result.get('charset') or '-')}</p>
        <p><strong>Robots:</strong> {esc(result.get('robots_meta') or '-')} — <strong>X-Robots-Tag:</strong> {esc(result.get('x_robots_tag') or '-')}</p>
        <p><strong>Görsel:</strong> {esc(len(result.get('images') or []))} — <strong>Alt eksik:</strong> {esc(len(result.get('missing_alt_images') or []))}</p>
        <p><strong>Link:</strong> {esc(len(result.get('links') or []))} — <strong>İç link:</strong> {esc(len(result.get('internal_links') or []))} — <strong>Dış link:</strong> {esc(len(result.get('external_links') or []))}</p>
    </div>
    <h2>Kontrol Sonuçları</h2>
    <div class="card" style="padding:0;overflow:auto;"><table><thead><tr><th>Kategori</th><th>Kontrol</th><th>Durum</th><th>Puan</th><th>Detay</th><th>Çözüm</th></tr></thead><tbody>{_checks_table(result.get('checks') or [])}</tbody></table></div>
    <h2>Eksik Alt Etiketli Görseller</h2>
    <div class="card"><ul>{''.join(missing_alt_rows) if missing_alt_rows else '<li>Eksik alt etiketi listesi boş.</li>'}</ul></div>
    <h2>Kırık / Sorunlu Linkler</h2>
    <div class="card"><ul>{''.join(broken_rows) if broken_rows else '<li>Sorunlu link listesi boş veya link kontrolü kapalı.</li>'}</ul></div>
    """


def build_page_html_report(result: Dict[str, Any]) -> str:
    score = int(result.get("score", 0))
    return f"""<!doctype html><html lang="tr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Tamer SEO Analyzer Raporu</title><style>{_report_css()}</style></head>
<body><div class="container">
    <div class="header">
        <div><div class="brand">Tamer SEO Analyzer V{APP_REPORT_VERSION}</div><h1>SEO Analiz Raporu</h1><div class="url">{esc(result.get('final_url') or result.get('url'))}</div></div>
        <div class="score" style="--scoreDeg:{score * 3.6}deg"><div class="score-inner"><div><b>{esc(score)}</b><br>Derece {esc(result.get('grade'))}</div></div></div>
    </div>
    {_page_summary_cards(result)}
    {_action_plan_html(result)}
    {_page_detail_block(result)}
    <div class="footer">Bu rapor Tamer SEO Analyzer V{APP_REPORT_VERSION} tarafından oluşturuldu. • {APP_COPYRIGHT} • <a href="{APP_WEBSITE}" target="_blank" rel="noopener">{APP_WEBSITE}</a></div>
</div></body></html>"""


def build_site_html_report(result: Dict[str, Any]) -> str:
    summary = result.get("summary") or {}
    pages = result.get("pages") or []
    score = int(result.get("score", 0))
    page_rows = []
    for page in pages:
        if page.get("ok"):
            page_rows.append(f"<tr><td>{esc(page.get('final_url'))}</td><td>{esc(page.get('score'))}</td><td>{esc(page.get('grade'))}</td><td>{esc(page.get('status_code'))}</td><td>{esc(page.get('title'))}</td></tr>")
        else:
            page_rows.append(f"<tr><td>{esc(page.get('url'))}</td><td>0</td><td>E</td><td>{esc(page.get('status_code'))}</td><td>{esc(page.get('error'))}</td></tr>")
    recs = [f"<li><strong>{esc(item.get('count'))} sayfa:</strong> {esc(item.get('recommendation'))}</li>" for item in summary.get("top_recommendations") or []]
    page_details = []
    for page in pages:
        if not page.get("ok"):
            continue
        page_details.append(f"""
        <div class="pagebox">
            <h2>{esc(page.get('score'))}/100 — {esc(page.get('final_url'))}</h2>
            {_page_detail_block(page)}
        </div>""")
    return f"""<!doctype html><html lang="tr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Tamer SEO Analyzer Site Raporu</title><style>{_report_css()}</style></head>
<body><div class="container">
    <div class="header">
        <div><div class="brand">Tamer SEO Analyzer V{APP_REPORT_VERSION}</div><h1>Çoklu Sayfa SEO Raporu</h1><div class="url">{esc(result.get('base_site') or result.get('url'))}</div></div>
        <div class="score" style="--scoreDeg:{score * 3.6}deg"><div class="score-inner"><div><b>{esc(score)}</b><br>Derece {esc(result.get('grade'))}</div></div></div>
    </div>
    <div class="grid">
        <div class="card"><div class="label">Analiz Edilen Sayfa</div><div class="value">{esc(summary.get('ok_pages'))}/{esc(summary.get('total_pages'))}</div></div>
        <div class="card"><div class="label">Ortalama Sayfa Puanı</div><div class="value">{esc(summary.get('average_page_score'))}</div></div>
        <div class="card"><div class="label">Hata / Uyarı</div><div class="value">{esc((summary.get('issue_counts') or {}).get('error',0))} / {esc((summary.get('issue_counts') or {}).get('warning',0))}</div></div>
        <div class="card"><div class="label">Rapor Tarihi</div><div class="value">{esc(result.get('created_at'))}</div></div>
    </div>
    {_action_plan_html(result)}
    <div class="card"><h2>Öncelikli Site Önerileri</h2><ul>{''.join(recs) if recs else '<li>Kritik öneri bulunamadı.</li>'}</ul><p class="small">Kopya title cezası: {esc(summary.get('duplicate_title_count'))}; kopya description cezası: {esc(summary.get('duplicate_description_count'))}</p></div>
    <h2>Sayfa Listesi</h2><div class="card" style="padding:0;overflow:auto"><table><thead><tr><th>URL</th><th>Puan</th><th>Derece</th><th>HTTP</th><th>Title / Hata</th></tr></thead><tbody>{''.join(page_rows)}</tbody></table></div>
    {''.join(page_details)}
    <div class="footer">Bu rapor Tamer SEO Analyzer V{APP_REPORT_VERSION} tarafından oluşturuldu. • {APP_COPYRIGHT} • <a href="{APP_WEBSITE}" target="_blank" rel="noopener">{APP_WEBSITE}</a></div>
</div></body></html>"""


def build_html_report(result: Dict[str, Any]) -> str:
    if result.get("mode") == "site" or "pages" in result:
        return build_site_html_report(result)
    return build_page_html_report(result)


def save_html_report(result: Dict[str, Any], output_dir: str | Path) -> Path:
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
    stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    prefix = "site_raporu" if (result.get("mode") == "site" or "pages" in result) else "seo_raporu"
    filename = f"{prefix}_{safe_filename(result.get('base_site') or result.get('url') or 'site')}_{stamp}.html"
    path = output / filename
    path.write_text(build_html_report(result), encoding="utf-8")
    return path


def save_json_report(result: Dict[str, Any], output_dir: str | Path) -> Path:
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
    stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"seo_veri_{safe_filename(result.get('base_site') or result.get('url') or 'site')}_{stamp}.json"
    path = output / filename
    path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
    return path


def save_csv_report(result: Dict[str, Any], output_dir: str | Path) -> Path:
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
    stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"seo_sayfalar_{safe_filename(result.get('base_site') or result.get('url') or 'site')}_{stamp}.csv"
    path = output / filename
    pages = result.get("pages") or [result]
    with path.open("w", newline="", encoding="utf-8-sig") as handle:
        writer = csv.writer(handle, delimiter=";")
        writer.writerow(["URL", "Puan", "Derece", "HTTP", "Title", "Meta Description", "H1", "Gorsel", "Alt Eksik", "Link"])
        for page in pages:
            writer.writerow([
                page.get("final_url") or page.get("url"),
                page.get("score", 0),
                page.get("grade", ""),
                page.get("status_code", ""),
                page.get("title", ""),
                page.get("meta_description", ""),
                " | ".join(page.get("h1") or []),
                len(page.get("images") or []),
                len(page.get("missing_alt_images") or []),
                len(page.get("links") or []),
            ])
    return path


def save_pdf_report(result: Dict[str, Any], output_dir: str | Path) -> Path:
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
    stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"seo_raporu_{safe_filename(result.get('base_site') or result.get('url') or 'site')}_{stamp}.pdf"
    path = output / filename
    try:
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import A4
        from reportlab.lib.styles import getSampleStyleSheet
        from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
    except Exception as exc:  # noqa: BLE001
        raise RuntimeError("PDF rapor için reportlab paketi gerekli. requirements.txt kurulumu yapın.") from exc

    doc = SimpleDocTemplate(str(path), pagesize=A4, rightMargin=28, leftMargin=28, topMargin=28, bottomMargin=28)
    styles = getSampleStyleSheet()
    story: List[Any] = []
    story.append(Paragraph(f"Tamer SEO Analyzer V{APP_REPORT_VERSION} Raporu", styles["Title"]))
    story.append(Spacer(1, 10))
    story.append(Paragraph(f"Site: {esc(result.get('base_site') or result.get('final_url') or result.get('url'))}", styles["Normal"]))
    story.append(Paragraph(f"Puan: {esc(result.get('score'))}/100 - Derece: {esc(result.get('grade'))}", styles["Heading2"]))
    story.append(Paragraph(f"{APP_COPYRIGHT} - {APP_WEBSITE}", styles["Normal"]))
    story.append(Spacer(1, 10))

    if result.get("mode") == "site" or "pages" in result:
        rows = [["URL", "Puan", "HTTP", "Title"]]
        for page in (result.get("pages") or [])[:40]:
            rows.append([str(page.get("final_url") or page.get("url"))[:62], str(page.get("score", 0)), str(page.get("status_code", "")), str(page.get("title") or page.get("error") or "")[:42]])
    else:
        rows = [["Kategori", "Kontrol", "Durum", "Puan"]]
        for item in (result.get("checks") or [])[:60]:
            rows.append([str(item.get("category")), str(item.get("name")), str(item.get("status_label")), f"{item.get('score')}/{item.get('max_score')}"])

    table = Table(rows, repeatRows=1)
    table.setStyle(TableStyle([
        ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1e293b")),
        ("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
        ("GRID", (0, 0), (-1, -1), 0.25, colors.HexColor("#cbd5e1")),
        ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
        ("FONTSIZE", (0, 0), (-1, -1), 7),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]))
    story.append(table)
    doc.build(story)
    return path
