#!/usr/bin/env python3
"""
Yeah. Right! — Daily Static Site Generator
Reads today's content from a CSV file and publishes a new HTML page.
Regenerates the archive index and search index on every run.
No external dependencies — uses Python standard library only.
"""

import csv
import json
import logging
import os
import sys
from datetime import date

from config import (
    CONTENT_FILE, OUTPUT_DIR, SITE_DESCRIPTION, SITE_NAME, SITE_URL,
)

# ── Logging ───────────────────────────────────────────────────────────────────

logging.basicConfig(
    filename=os.path.join(os.path.dirname(os.path.abspath(__file__)), "generator.log"),
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
log = logging.getLogger(__name__)

# ── HTML templates ────────────────────────────────────────────────────────────

BASE_CSS = """
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    body {
        font-family: Georgia, 'Times New Roman', serif;
        background: #f0f0eb;
        color: #1a1a1a;
        line-height: 1.7;
        padding: 2rem 1rem;
    }
    .site-header {
        max-width: 680px;
        margin: 0 auto 3rem;
        border-bottom: 1px solid #e0e0d8;
        padding-bottom: 1.25rem;
        display: flex;
        align-items: flex-start;
        gap: 1.1rem;
    }
    .logo-badge {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border: 2px solid #1a1a1a;
        border-radius: 4px;
        padding: 0.2rem 0.45rem;
        font-size: 3rem;
        font-weight: bold;
        font-family: Georgia, 'Times New Roman', serif;
        letter-spacing: 0.02em;
        color: #1a1a1a;
        text-decoration: none;
        line-height: 1;
        flex-shrink: 0;
        margin-top: 0.2rem;
    }
    .logo-badge:hover { background: #1a1a1a; color: #f0f0eb; }
    .site-header-text a { text-decoration: none; color: inherit; }
    .site-title { font-size: 1.4rem; font-weight: bold; letter-spacing: -0.01em; }
    .site-desc  { font-size: 0.9rem; color: #666; margin-top: 0.2rem; }
    main { max-width: 680px; margin: 0 auto; }
    footer {
        max-width: 680px;
        margin: 3rem auto 0;
        padding-top: 1.25rem;
        border-top: 1px solid #e0e0d8;
        font-size: 0.85rem;
        color: #888;
    }
    footer a { color: #555; }
    nav { display: flex; gap: 1.2rem; margin-top: 0.5rem; }
    nav a { font-size: 0.88rem; color: #555; text-decoration: none; }
    nav a:hover { text-decoration: underline; }
"""

POST_TEMPLATE = """<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{date_long} \u2014 {site_name}</title>
    <style>
        *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}

        body {{
            font-family: Georgia, 'Times New Roman', serif;
            background: #f0f0eb;
            color: #1a1a1a;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 2rem 1rem;
        }}

        /* Fixed-size card — position and dimensions never change */
        .card {{
            border: 2px solid #646460;
            border-radius: 6px;
            padding: 2.5rem 3.5rem;
            max-width: 600px;
            width: 100%;
            height: 380px;
            background: #fff;
            display: flex;
            flex-direction: column;
        }}

        /* Header row: logo left, date right — fixed at top of card */
        .card-header {{
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-shrink: 0;
        }}

        .logo-badge {{
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border: 2px solid #1a1a1a;
            border-radius: 4px;
            padding: 0.18rem 0.4rem;
            font-size: 2.7rem;
            font-weight: bold;
            font-family: Georgia, 'Times New Roman', serif;
            letter-spacing: 0.02em;
            color: #1a1a1a;
            text-decoration: none;
            line-height: 1;
        }}

        .logo-badge:hover {{ background: #1a1a1a; color: #fafaf8; }}

        .post-date {{
            font-size: 0.78rem;
            color: #999;
            text-transform: uppercase;
            letter-spacing: 0.08em;
        }}

        /* Content area fills remaining space and centres text vertically */
        .card-body {{
            flex: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            text-align: center;
        }}

        .post-content {{
            font-size: 1.3rem;
            line-height: 1.75;
            color: #1a1a1a;
        }}

        .tagline {{
            font-size: 1.3rem;
            font-style: italic;
            font-weight: normal;
            color: #1a1a1a;
            align-self: flex-end;
            margin-top: 1rem;
        }}

        /* Nav fixed at bottom of card */
        .post-nav {{
            display: flex;
            justify-content: space-between;
            flex-shrink: 0;
            padding-top: 1rem;
            border-top: 1px solid #e8e8e0;
            font-size: 0.82rem;
        }}

        .post-nav a {{ color: #888; text-decoration: none; }}
        .post-nav a:hover {{ color: #1a1a1a; }}

        .site-footer {{
            margin-top: 1.5rem;
            font-size: 0.8rem;
            color: #aaa;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 0.6rem;
        }}

        .site-footer-links {{
            display: flex;
            gap: 1.25rem;
        }}

        .site-footer a {{ color: #aaa; text-decoration: none; }}
        .site-footer a:hover {{ color: #555; }}

        .submit-link {{
            font-size: 0.72rem;
            color: #ccc;
            text-decoration: none;
            letter-spacing: 0.02em;
        }}

        .submit-link:hover {{ color: #888; }}

        .tagline {{
            font-size: 1.3rem;
            font-style: italic;
            font-weight: normal;
            color: #1a1a1a;
            text-align: right;
            margin-top: 1.5rem;
        }}
    </style>
</head>
<body>
    <div class="card">
        <div class="card-header">
            <a href="{site_url}/index.html" class="logo-badge">2&#x00D7;2</a>
            <p class="post-date">{date_long}</p>
        </div>
        <div class="card-body">
            <p class="post-content">{content}</p>
            <p class="tagline">&#8230; Yeah. Right!</p>
        </div>
        <div class="post-nav">
            <span>{prev_link}</span>
            <span>{next_link}</span>
        </div>
    </div>
    <footer class="site-footer">
        <div class="site-footer-links">
            <a href="{site_url}/archive.html">Archive</a>
            <a href="{site_url}/search.html">Search</a>
        </div>
        <a class="submit-link" href="mailto:daily@twobytwos.cc?subject=My%20daily%20dose%20of%20Two%20by%20Two%20sarcasm&body=One%20or%20two%20sentences%20of%20your%20best%20daily%20dose%20of%20Two%20by%20Two%20sarcasm.%20Submissions%20will%20be%20reviewed.%20No%20guarantee%20of%20publishing.%0A-------------------------%0APlace%20content%20here%0A-------------------------%0A">Submit your own</a>
    </footer>
</body>
</html>"""

INDEX_TEMPLATE = """<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Archive \u2014 {site_name}</title>
    <style>
{css}
        h1 {{ font-size: 1.5rem; margin-bottom: 1.75rem; }}
        .post-list {{ list-style: none; }}
        .post-list li {{
            display: flex;
            gap: 1.5rem;
            padding: 0.65rem 0;
            border-bottom: 1px solid #eeeee8;
            align-items: baseline;
        }}
        .post-list li:last-child {{ border-bottom: none; }}
        .post-date-label {{
            font-size: 0.82rem;
            color: #888;
            white-space: nowrap;
            min-width: 7rem;
        }}
        .post-excerpt {{ font-size: 0.95rem; }}
        .post-excerpt a {{ color: #1a1a1a; text-decoration: none; }}
        .post-excerpt a:hover {{ text-decoration: underline; }}
    </style>
</head>
<body>
    <header class="site-header">
        <a href="{site_url}/index.html" class="logo-badge">2&#x00D7;2</a>
        <div class="site-header-text">
            <div><a href="{site_url}/index.html" class="site-title">{site_name}</a></div>
            <div class="site-desc">{site_description}</div>
            <nav>
                <a href="{site_url}/archive.html">Archive</a>
                <a href="{site_url}/search.html">Search</a>
            </nav>
        </div>
    </header>
    <main>
        <h1>Archive</h1>
        <ul class="post-list">
{items}
        </ul>
    </main>
    <footer>
        <a href="{site_url}/index.html">{site_name}</a>
    </footer>
</body>
</html>"""

SEARCH_TEMPLATE = """<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Search \u2014 {site_name}</title>
    <style>
{css}
        h1 {{ font-size: 1.5rem; margin-bottom: 1.25rem; }}
        #search-input {{
            width: 100%;
            padding: 0.65rem 0.85rem;
            font-size: 1rem;
            font-family: inherit;
            border: 1px solid #ccc;
            border-radius: 4px;
            margin-bottom: 1.75rem;
            background: #fff;
        }}
        #search-input:focus {{ outline: 2px solid #555; outline-offset: 1px; }}
        #results {{ list-style: none; }}
        #results li {{
            padding: 0.65rem 0;
            border-bottom: 1px solid #eeeee8;
        }}
        #results li:last-child {{ border-bottom: none; }}
        .result-date {{
            font-size: 0.82rem;
            color: #888;
            margin-bottom: 0.2rem;
        }}
        .result-excerpt {{ font-size: 0.95rem; }}
        .result-excerpt a {{ color: #1a1a1a; text-decoration: none; }}
        .result-excerpt a:hover {{ text-decoration: underline; }}
        mark {{ background: #fff3b0; padding: 0 1px; }}
        #status {{ font-size: 0.88rem; color: #888; margin-bottom: 1rem; }}
    </style>
</head>
<body>
    <header class="site-header">
        <a href="{site_url}/index.html" class="logo-badge">2&#x00D7;2</a>
        <div class="site-header-text">
            <div><a href="{site_url}/index.html" class="site-title">{site_name}</a></div>
            <div class="site-desc">{site_description}</div>
            <nav>
                <a href="{site_url}/archive.html">Archive</a>
                <a href="{site_url}/search.html">Search</a>
            </nav>
        </div>
    </header>
    <main>
        <h1>Search</h1>
        <input type="search" id="search-input" placeholder="Type to search\u2026" autofocus>
        <p id="status"></p>
        <ul id="results"></ul>
    </main>
    <footer>
        <a href="{site_url}/index.html">{site_name}</a>
    </footer>
    <script>
    (function () {{
        const input   = document.getElementById('search-input');
        const results = document.getElementById('results');
        const status  = document.getElementById('status');
        let posts     = [];

        fetch('{site_url}/search-index.json')
            .then(r => r.json())
            .then(data => {{ posts = data; }})
            .catch(() => {{ status.textContent = 'Search index could not be loaded.'; }});

        function highlight(text, query) {{
            if (!query) return text;
            const re = new RegExp('(' + query.replace(/[.*+?^${{}}()|[\\]\\\\]/g, '\\\\$&') + ')', 'gi');
            return text.replace(re, '<mark>$1</mark>');
        }}

        input.addEventListener('input', function () {{
            const q = this.value.trim().toLowerCase();
            results.innerHTML = '';
            if (q.length < 2) {{ status.textContent = ''; return; }}

            const matches = posts.filter(p => p.content.toLowerCase().includes(q));
            status.textContent = matches.length
                ? matches.length + ' result' + (matches.length > 1 ? 's' : '')
                : 'No results found.';

            matches.forEach(p => {{
                const li = document.createElement('li');
                li.innerHTML =
                    '<div class="result-date">' + p.date_long + '</div>' +
                    '<div class="result-excerpt"><a href="{site_url}/' + p.filename + '">' +
                    highlight(p.content, this.value.trim()) + '</a></div>';
                results.appendChild(li);
            }});
        }}.bind(input));
    }})();
    </script>
</body>
</html>"""

# ── Helpers ───────────────────────────────────────────────────────────────────

def ensure_output_dir():
    os.makedirs(OUTPUT_DIR, exist_ok=True)


MONTHS = [
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
]

DATE_FORMATS = [
    "%Y-%m-%d",   # 2026-08-01  (intended)
    "%d/%m/%Y",   # 01/08/2026  (Australian Excel)
    "%d/%m/%y",   # 01/08/26
    "%m/%d/%Y",   # 08/01/2026  (US Excel)
    "%m/%d/%y",   # 08/01/26
    "%d-%m-%Y",   # 01-08-2026
    "%d-%m-%y",   # 01-08-26
    "%Y/%m/%d",   # 2026/08/01
]


def parse_date(raw: str):
    """Try every known format and return a date object, or None on failure."""
    from datetime import datetime
    raw = raw.strip()
    for fmt in DATE_FORMATS:
        try:
            return datetime.strptime(raw, fmt).date()
        except ValueError:
            continue
    return None


def format_date_long(d) -> str:
    """Convert a date object to e.g. 'August 1, 2026'."""
    return f"{MONTHS[d.month - 1]} {d.day}, {d.year}"


def load_all_posts() -> list:
    """Load all rows from the CSV that have a date on or before today."""
    today = date.today()
    posts = []
    skipped = 0

    with open(CONTENT_FILE, newline="", encoding="utf-8-sig") as f:
        reader = csv.DictReader(f)
        for row in reader:
            raw_date = row["date"].strip()
            content  = row["content"].strip()
            if not raw_date or not content:
                continue

            d = parse_date(raw_date)
            if d is None:
                log.warning(f"Could not parse date '{raw_date}' — row skipped.")
                skipped += 1
                continue

            if d > today:
                continue

            iso = d.strftime("%Y-%m-%d")
            posts.append({
                "date_iso":  iso,
                "date_long": format_date_long(d),
                "filename":  f"{iso}.html",
                "content":   content,
            })

    if skipped:
        log.warning(f"{skipped} row(s) skipped due to unrecognised date format.")

    posts.sort(key=lambda p: p["date_iso"], reverse=True)
    return posts


def write_post(post: dict, prev_post, next_post):
    prev_link = (
        f'&larr; <a href="{SITE_URL}/{prev_post["filename"]}">{prev_post["date_long"]}</a>'
        if prev_post else ""
    )
    next_link = (
        f'<a href="{SITE_URL}/{next_post["filename"]}">{next_post["date_long"]}</a> &rarr;'
        if next_post else ""
    )
    html = POST_TEMPLATE.format(
        site_name=SITE_NAME,
        site_url=SITE_URL,
        date_long=post["date_long"],
        content=post["content"],
        prev_link=prev_link,
        next_link=next_link,
    )
    path = os.path.join(OUTPUT_DIR, post["filename"])
    with open(path, "w", encoding="utf-8") as f:
        f.write(html)


def write_archive(posts: list):
    items = []
    for p in posts:
        excerpt = p["content"][:120] + ("\u2026" if len(p["content"]) > 120 else "")
        items.append(
            f'            <li>\n'
            f'                <span class="post-date-label">{p["date_long"]}</span>\n'
            f'                <span class="post-excerpt">'
            f'<a href="{SITE_URL}/{p["filename"]}">{excerpt}</a></span>\n'
            f'            </li>'
        )
    html = INDEX_TEMPLATE.format(
        css=BASE_CSS,
        site_name=SITE_NAME,
        site_description=SITE_DESCRIPTION,
        site_url=SITE_URL,
        items="\n".join(items),
    )
    with open(os.path.join(OUTPUT_DIR, "archive.html"), "w", encoding="utf-8") as f:
        f.write(html)


def write_homepage(posts: list):
    """Write index.html as today's post — the first entry in the sorted list."""
    today = posts[0]
    prev_post = posts[1] if len(posts) > 1 else None
    prev_link = (
        f'&larr; <a href="{SITE_URL}/{prev_post["filename"]}">{prev_post["date_long"]}</a>'
        if prev_post else ""
    )
    html = POST_TEMPLATE.format(
        site_name=SITE_NAME,
        site_url=SITE_URL,
        date_long=today["date_long"],
        content=today["content"],
        prev_link=prev_link,
        next_link="",
    )
    with open(os.path.join(OUTPUT_DIR, "index.html"), "w", encoding="utf-8") as f:
        f.write(html)


def write_search_page():
    html = SEARCH_TEMPLATE.format(
        css=BASE_CSS,
        site_name=SITE_NAME,
        site_description=SITE_DESCRIPTION,
        site_url=SITE_URL,
    )
    with open(os.path.join(OUTPUT_DIR, "search.html"), "w", encoding="utf-8") as f:
        f.write(html)


def write_search_index(posts: list):
    index = [
        {"date_long": p["date_long"], "filename": p["filename"], "content": p["content"]}
        for p in posts
    ]
    with open(os.path.join(OUTPUT_DIR, "search-index.json"), "w", encoding="utf-8") as f:
        json.dump(index, f, ensure_ascii=False, indent=2)


# ── Main ──────────────────────────────────────────────────────────────────────

def main():
    log.info("── Daily site generation started ───────────────")
    ensure_output_dir()

    try:
        posts = load_all_posts()
    except FileNotFoundError:
        log.error(f"Content file not found: {CONTENT_FILE}")
        sys.exit(1)
    except Exception as e:
        log.error(f"Failed to load content file: {e}")
        sys.exit(1)

    if not posts:
        log.warning("No posts to publish yet.")
        sys.exit(0)

    # Write all posts with correct prev/next navigation
    for i, post in enumerate(posts):
        prev_post = posts[i + 1] if i + 1 < len(posts) else None
        next_post = posts[i - 1] if i > 0 else None
        write_post(post, prev_post, next_post)

    write_homepage(posts)
    write_archive(posts)
    write_search_page()
    write_search_index(posts)

    log.info(f"Done. {len(posts)} post(s) live. Homepage, archive, and search updated.")


if __name__ == "__main__":
    main()
