"""complyform-rotation-reminder — monthly secret rotation age check.

Trigger: Monthly Cloud Scheduler.

Reads rotation_tracker from Firestore, creates GitHub issues for secrets
approaching or past their 365-day rotation deadline.

Contract: always returns 200. Duplicate issues are skipped.
"""

from __future__ import annotations

import logging
from datetime import UTC, datetime
from typing import Any

import httpx

from backend.ops._shared import cloud_function_wrapper, log_action, send_alert_email
from backend.shared.firestore import get_db

_log = logging.getLogger(__name__)

_GITHUB_API_BASE = "https://api.github.com"
_REPO = "complyform/complyform"
_ROTATION_MAX_DAYS = 365
_ADVANCE_WARNING_DAYS = 330

_TRACKED_SECRETS = (
    "paddle-webhook-secret",
    "paddle-api-key",
    "postmark-server-token",
    "profile-bundle-signing-key",
    "airgap-license-signing-key",
    "google-oauth-client-secret",
    "session-encryption-key",
    "homebrew-tap-token",
)


async def _get_github_token() -> str:
    """Retrieve GitHub token from Secret Manager."""
    from google.cloud import secretmanager

    client = secretmanager.SecretManagerServiceClient()
    name = "projects/complyform-prod/secrets/github-token/versions/latest"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("utf-8")


async def _get_open_issues(token: str) -> set[str]:
    """Fetch open issue titles with security/rotation label."""
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json",
    }
    titles: set[str] = set()
    page = 1
    async with httpx.AsyncClient(timeout=15.0) as client:
        while True:
            resp = await client.get(
                f"{_GITHUB_API_BASE}/repos/{_REPO}/issues",
                headers=headers,
                params={
                    "labels": "security/rotation",
                    "state": "open",
                    "per_page": 100,
                    "page": page,
                },
            )
            if resp.status_code != 200:
                _log.warning("GitHub issues list returned %s", resp.status_code)
                break
            items = resp.json()
            if not items:
                break
            for item in items:
                titles.add(item.get("title", ""))
            page += 1
    return titles


async def _create_issue(
    token: str,
    title: str,
    body: str,
    labels: list[str],
) -> bool:
    """Create a GitHub issue.  Returns True on success."""
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json",
    }
    async with httpx.AsyncClient(timeout=15.0) as client:
        resp = await client.post(
            f"{_GITHUB_API_BASE}/repos/{_REPO}/issues",
            headers=headers,
            json={"title": title, "body": body, "labels": labels},
        )
    if resp.status_code == 201:
        return True
    _log.warning("Failed to create issue '%s': %s", title, resp.status_code)
    return False


@cloud_function_wrapper("complyform-rotation-reminder")
async def rotation_reminder(request: Any) -> dict[str, Any]:
    """Check secret rotation ages, create GitHub issues for upcoming/overdue.

    Reads ``founder_metrics/rotation_tracker`` singleton from Firestore.
    Creates issues with ``security/rotation`` label (and ``priority/p2``
    for overdue secrets).
    """
    db = get_db()
    now = datetime.now(UTC)

    tracker_ref = db.collection("founder_metrics").document(
        "rotation_tracker",
    )
    tracker_snap = await tracker_ref.get()
    if not tracker_snap.exists:
        _log.warning("rotation_tracker document not found")
        return {"issues_created": 0}

    tracker = tracker_snap.to_dict() or {}
    secrets: dict[str, Any] = tracker.get("secrets", {})

    token = await _get_github_token()
    existing_titles = await _get_open_issues(token)

    issues_created = 0
    alerts_sent = 0

    for secret_name in _TRACKED_SECRETS:
        info = secrets.get(secret_name)
        if not info:
            _log.warning("No rotation data for %s", secret_name)
            continue

        last_rotated_str = info.get("last_rotated_at", "")
        if not last_rotated_str:
            continue

        last_rotated = datetime.fromisoformat(last_rotated_str)
        if last_rotated.tzinfo is None:
            last_rotated = last_rotated.replace(tzinfo=UTC)

        age_days = (now - last_rotated).days

        if age_days >= _ROTATION_MAX_DAYS:
            # Overdue
            overdue_days = age_days - _ROTATION_MAX_DAYS
            title = f"Rotate {secret_name} — overdue by {overdue_days} days"
            if title not in existing_titles:
                body = (
                    f"Secret `{secret_name}` was last rotated {age_days} days ago "
                    f"({last_rotated.date().isoformat()}).\n\n"
                    f"**Rotation is overdue.** Maximum rotation interval is "
                    f"{_ROTATION_MAX_DAYS} days."
                )
                created = await _create_issue(
                    token,
                    title,
                    body,
                    labels=["security/rotation", "priority/p2"],
                )
                if created:
                    issues_created += 1
                    existing_titles.add(title)

            # Send email alert for overdue
            await send_alert_email(
                subject=f"[ComplyForm] Secret rotation overdue: {secret_name}",
                body=(
                    f"Secret '{secret_name}' was last rotated {age_days} days ago.\n"
                    f"Maximum rotation interval: {_ROTATION_MAX_DAYS} days.\n\n"
                    f"Please rotate immediately."
                ),
            )
            alerts_sent += 1

        elif age_days >= _ADVANCE_WARNING_DAYS:
            # Upcoming
            days_remaining = _ROTATION_MAX_DAYS - age_days
            title = f"Rotate {secret_name} — due in {days_remaining} days"
            if title not in existing_titles:
                body = (
                    f"Secret `{secret_name}` was last rotated {age_days} days ago "
                    f"({last_rotated.date().isoformat()}).\n\n"
                    f"Rotation is due in **{days_remaining} days**."
                )
                created = await _create_issue(
                    token,
                    title,
                    body,
                    labels=["security/rotation"],
                )
                if created:
                    issues_created += 1
                    existing_titles.add(title)

    log_action(
        customer_id=None,
        action="rotation_reminder_summary",
        issues_created=issues_created,
        alerts_sent=alerts_sent,
    )

    return {"issues_created": issues_created, "alerts_sent": alerts_sent}


entry_point = rotation_reminder
