#!/usr/bin/env python3
"""
app.py
------
Flask webhook server for automated HoverDesk license issuance.

Flow: Lemon Squeezy fires a webhook on `order_created` -> this server
verifies the signature -> extracts the buyer's email -> generates a
license key -> emails it via Resend -> logs the issuance (email +
timestamp only, never the key or secret).

Run locally:
    pip install -r requirements.txt
    cp .env.example .env   # fill in real values
    flask --app app run

This is a scaffold: the Lemon Squeezy payload shape below reflects their
documented `order_created` event as of the key_issuance_plan.md writeup,
but should be verified against a real test webhook before going live.
"""

from __future__ import annotations

import datetime
import hashlib
import hmac
import os

from flask import Flask, jsonify, request

import keygen_server
import mailer

app = Flask(__name__)

ISSUANCE_LOG_PATH = os.environ.get("ISSUANCE_LOG_PATH", "issuance_log.csv")


def _log_issuance(email: str) -> None:
    """Append an issuance record: email + timestamp only. Never the key/secret."""
    file_exists = os.path.exists(ISSUANCE_LOG_PATH)
    with open(ISSUANCE_LOG_PATH, "a", encoding="utf-8") as fh:
        if not file_exists:
            fh.write("issued_at_utc,email\n")
        timestamp = datetime.datetime.utcnow().isoformat(timespec="seconds") + "Z"
        fh.write(f"{timestamp},{email.strip().lower()}\n")


def _verify_lemon_squeezy_signature(raw_body: bytes, signature_header: "str | None") -> bool:
    """Verify the X-Signature header against LEMON_SQUEEZY_WEBHOOK_SECRET.

    Lemon Squeezy signs the raw request body with HMAC-SHA256 using the
    webhook signing secret configured in their dashboard, sent as a hex
    digest in the X-Signature header.
    """
    secret = os.environ.get("LEMON_SQUEEZY_WEBHOOK_SECRET")
    if not secret or not signature_header:
        return False
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header)


def _extract_buyer_email(payload: dict) -> "str | None":
    """Pull the buyer's email out of a Lemon Squeezy order_created payload.

    TODO: confirm this path against a real test webhook payload before
    going live — Lemon Squeezy's JSON:API shape nests attributes here as
    of the plan writeup, but should not be trusted blindly.
    """
    try:
        return payload["data"]["attributes"]["user_email"]
    except (KeyError, TypeError):
        return None


@app.route("/webhook", methods=["POST"])
def webhook():
    raw_body = request.get_data()
    signature_header = request.headers.get("X-Signature")

    if not _verify_lemon_squeezy_signature(raw_body, signature_header):
        return jsonify({"error": "invalid signature"}), 401

    payload = request.get_json(silent=True) or {}

    # Only act on order_created; ignore other event types Lemon Squeezy
    # may send to the same endpoint (refunds, subscription events, etc.).
    event_name = request.headers.get("X-Event-Name", "")
    if event_name != "order_created":
        return jsonify({"status": "ignored", "event": event_name}), 200

    email = _extract_buyer_email(payload)
    if not email:
        return jsonify({"error": "could not extract buyer email"}), 400

    try:
        key = keygen_server.generate_key(email)
    except keygen_server.KeyGenError as exc:
        return jsonify({"error": str(exc)}), 500

    try:
        mailer.send_license_key_email(email, key)
    except mailer.MailError as exc:
        return jsonify({"error": str(exc)}), 500

    _log_issuance(email)
    return jsonify({"status": "ok"}), 200


@app.route("/health", methods=["GET"])
def health():
    return jsonify({"status": "ok"}), 200


if __name__ == "__main__":
    app.run(debug=True)
