#!/usr/bin/env python3
"""
Regenerate docs/diagrams/ukinnect-01-overall-topology.png

Uses mingrammer/diagrams (official AWS icon set) + Graphviz — same *flow* as
docs/diagrams/ukinnect-01-overall-topology.mmd, but **not** Mermaid box shapes.

Requires Graphviz `dot` on PATH, or run via Docker (see bottom of this file).

Flow mirrored from Mermaid:
- App users → Route 53 → ALB → Node API (per AZ); API → Redis, Atlas.
- Stripe + “Webhooks + Connect APIs” on one HTML node (no PNG); `fixedsize=false` avoids diagram-wide 1.4×1.4 squashing the label onto Booking.
- Invisible `auth → stripe` inside the cluster so dot places Stripe between Auth and Booking (otherwise Stripe shares Booking’s x and the real edge is a vertical hairline).
- No Next.js tier, no CloudWatch, no standalone Stripe-webhook-only node.
"""
from pathlib import Path

from diagrams import Cluster, Diagram, Edge, Node
from diagrams.aws.compute import EC2
from diagrams.aws.database import ElasticacheForRedis
from diagrams.aws.engagement import SES
from diagrams.aws.integration import Eventbridge, SimpleNotificationServiceSns as SNS, StepFunctions
from diagrams.aws.network import ALB, Route53
from diagrams.aws.security import Cognito
from diagrams.firebase import Firebase
from diagrams.onprem.client import Users
from diagrams.onprem.database import MongoDB

# Stripe mark blue (https://stripe.com/brand) — HTML label, no PNG (avoids alpha / checkerboard)
STRIPE_BLUE = "#635BFF"
STRIPE_NODE_LABEL = (
    '<<TABLE BORDER="0" CELLBORDER="0" CELLPADDING="4" CELLSPACING="0" BGCOLOR="#ffffff">'
    f'<TR><TD ALIGN="CENTER"><FONT POINT-SIZE="22" COLOR="{STRIPE_BLUE}" FACE="Helvetica">stripe</FONT></TD></TR>'
    '<TR><TD ALIGN="CENTER"><FONT POINT-SIZE="11" COLOR="#475569" FACE="Sans-Serif">'
    "Webhooks + Connect APIs"
    "</FONT></TD></TR></TABLE>>"
)


def main() -> None:
    out_dir = Path(__file__).resolve().parents[1] / "docs" / "diagrams"
    out_dir.mkdir(parents=True, exist_ok=True)
    out_base = str(out_dir / "ukinnect-01-overall-topology")

    # ortho: stepped routes like other infra edges; avoids one long "line" spline along the top
    graph_attr = {
        "fontsize": "14",
        "bgcolor": "white",
        "pad": "0.45",
        "nodesep": "0.35",
        "ranksep": "0.6",
        "dpi": "120",
        "compound": "true",
        "label": "",
        "labelloc": "t",
    }
    node_attr = {"fontsize": "12", "labelloc": "b"}
    edge_attr = {"fontsize": "10", "arrowsize": "0.85", "color": "#7B8894", "penwidth": "1.0"}

    with Diagram(
        "",
        filename=out_base,
        show=False,
        direction="LR",
        curvestyle="ortho",
        graph_attr=graph_attr,
        node_attr=node_attr,
        edge_attr=edge_attr,
        outformat="png",
    ):
        app_users = Users("App users")
        r53 = Route53("Route 53")
        alb = ALB("Application Load\nBalancer")

        app_users >> r53 >> alb

        with Cluster("AWS Region"):
            # Single region pool (no AZ A/B sub-clusters) so API→Redis edges are short
            api_a = EC2("Node API\nExpress /api/v1")
            api_b = EC2("Node API\nExpress /api/v1")
            redis = ElasticacheForRedis("Redis\nBullMQ")
            alb >> api_a
            alb >> api_b
            for api in (api_a, api_b):
                api >> Edge(
                    minlen="0.2",
                    color="#4b5563",
                    penwidth="1.1",
                ) >> redis

        atlas = MongoDB("MongoDB\nAtlas")
        api_a >> atlas
        api_b >> atlas

        # Tighter horizontal spacing in this subgraph only (diagram global nodesep stays modest)
        with Cluster("App flow (/api/v1)", graph_attr={"nodesep": "0.42"}):
            auth = Cognito("Auth + JWT")
            # Diagram defaults apply fixedsize=1.4×1.4 to every node — that squashes HTML labels onto neighbors.
            stripe = Node(
                STRIPE_NODE_LABEL,
                shape="plain",
                margin="0.14",
                fixedsize="false",
                labelloc="c",
            )
            booking = StepFunctions("Booking flow")
            reviews = Eventbridge("Reviews +\nratings")
            disputes = Eventbridge("Disputes")
            notifications = SNS("Notifications")
            # Declare SMTP before Firebase: same x-column stack puts SMTP on top, so the
            # Notifications→SMTP ortho segment is short and reaches the node; was truncating when SMTP was below FCM.
            smtp = SES("SMTP\ntemplates")
            # Taller node + dark label: Firebase class defaults font to white, which breaks spacing/readability
            fcm = Firebase(
                "Firebase FCM",
                height="2.1",
                width="1.25",
                margin="0.2",
                fontsize="12",
                fontcolor="#2D3436",
                imagescale="true",
            )

            # Without a horizontal constraint, dot stacks stripe under booking (same x); the
            # stripe→booking edge becomes a hairline hidden under the Step Functions icon.
            auth >> Edge(style="invis", minlen="0.2", weight="10", constraint="true") >> stripe
            stripe >> Edge(
                color="#7B8894",
                style="solid",
                penwidth="1.0",
                minlen="0.45",
                weight="6",
                constraint="true",
            ) >> booking

        api_a >> Edge(color="#2563eb", style="dashed", label="API traffic") >> auth
        api_b >> Edge(color="#2563eb", style="dashed", label="API traffic") >> auth

        auth >> booking >> reviews
        booking >> disputes
        reviews >> notifications
        disputes >> notifications
        # Connect SMTP before FCM: same x-column stack puts SMTP above Firebase; the lower branch to
        # bottom-node had been a short ortho stub. Do not set headclip=false — image nodes need border clip
        # or the arrow can land in the icon center and look like it “doesn’t reach” the node.
        notifications >> Edge(
            minlen="0.35",
            penwidth="1.0",
        ) >> smtp
        notifications >> fcm

    print(f"Wrote {out_base}.png")


if __name__ == "__main__":
    main()

# --- Regenerate without local Graphviz (from repo root; PowerShell) ---
# docker run --rm -v "${PWD}:/work" -w /work python:3.12-bookworm bash -lc \
#   "apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq graphviz && pip install -q diagrams && python scripts/render_ukinnect_01_topology.py"
