server {
    listen       8080;
    listen       [::]:8080;
    server_name  _;

    root  /usr/share/nginx/html;
    index index.html;

    # We sit behind Traefik on a different scheme/port. nginx defaults to
    # `absolute_redirect on`, which would rewrite a `return 302 /es/;` to an
    # absolute `http://<server_name>:8080/es/` — and the browser would then
    # follow that to the internal port, bypassing the proxy.
    # Sending only the path lets the browser resolve it against the public
    # URL it actually used (https://familia.example.com/...).
    absolute_redirect off;

    # Bigger proxy buffers for the API responses. The /signin-oidc 302 returns
    # a session cookie chunked across `familynido.session*` cookies, easily
    # 6–10 KB of headers. With the default 4–8 KB buffer nginx would abort
    # with `upstream sent too big header` and surface a 502.
    proxy_buffer_size       16k;
    proxy_buffers           8 16k;
    proxy_busy_buffers_size 32k;

    # Bigger *request* header buffers. The OIDC callback receives a long
    # `?state=...` plus the browser's Cookie header (session + correlation
    # cookies + sometimes orphan nonces from prior failed attempts). The
    # default 4×8K trips the moment any of those grow, returning a stark
    # 400 "Request Header Or Cookie Too Large" before the request even
    # reaches the backend.
    large_client_header_buffers 8 32k;

    # Allow image uploads up to ~12 MB. The API enforces its own 10 MB cap
    # (FilesOptions.MaxImageBytes); we pad slightly so a borderline file gets
    # nginx out of the way and surfaces the API's clean validation error
    # instead of a blunt 413 with no body. iOS HEIC photos routinely sit
    # in the 3–8 MB range and would otherwise fail silently.
    client_max_body_size    12m;

    # Gzip text assets; PWA manifest explicitly included.
    gzip              on;
    gzip_comp_level   6;
    gzip_min_length   1024;
    gzip_types        text/plain text/css application/javascript application/json
                      application/manifest+json image/svg+xml text/xml;

    # Aggressive caching for hashed bundle files (Angular outputHashing=all).
    location ~* \.(?:js|css|woff2?|ttf|otf|eot|ico|svg|png|jpg|jpeg|gif|webp|avif)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

    # Service worker registration script and config must never be cached.
    location ~* (ngsw-worker\.js|ngsw\.json|safety-worker\.js|worker-basic\.min\.js|manifest\.webmanifest)$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        try_files $uri =404;
    }

    # Pass /api through to the backend container (name "api" in compose).
    location /api/ {
        proxy_pass              http://api:8080;
        proxy_http_version      1.1;
        proxy_set_header        Host               $host;
        proxy_set_header        X-Real-IP          $remote_addr;
        proxy_set_header        X-Forwarded-For    $proxy_add_x_forwarded_for;
        # Preserve the scheme Traefik announced to the outside world
        # (https) instead of overwriting it with nginx's own $scheme (http).
        proxy_set_header        X-Forwarded-Proto  $http_x_forwarded_proto;
        proxy_read_timeout      60s;
    }

    # OIDC callback paths handled by the backend. Need the same forwarded
    # headers as /api/ so the OIDC middleware validates the callback against
    # the public host/scheme and the AuthenticationProperties round-trip works.
    location = /signin-oidc {
        proxy_pass              http://api:8080;
        proxy_http_version      1.1;
        proxy_set_header        Host               $host;
        proxy_set_header        X-Real-IP          $remote_addr;
        proxy_set_header        X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto  $http_x_forwarded_proto;
    }
    location = /signout-callback-oidc {
        proxy_pass              http://api:8080;
        proxy_http_version      1.1;
        proxy_set_header        Host               $host;
        proxy_set_header        X-Real-IP          $remote_addr;
        proxy_set_header        X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto  $http_x_forwarded_proto;
    }

    location = /healthz {
        access_log off;
        return 200 "ok\n";
        add_header Content-Type text/plain;
    }

    # ── Locale routing (i18n subpath layout) ────────────────────────────────
    # Build emits two bundles: dist/.../browser/es/ and dist/.../browser/en/.
    # The Dockerfile copies the entire `browser/` tree to /usr/share/nginx/html
    # so the container ends up with /usr/share/nginx/html/{es,en}/index.html.
    #
    # Root visit: pick es or en from Accept-Language and redirect. Anyone
    # bookmarked at /es/... or /en/... hits the SPA below directly.

    location = / {
        # Negotiate locale from Accept-Language. Defaults to es-ES (the
        # source locale) for any browser that doesn't explicitly prefer
        # English. Add more `if` branches as locales grow.
        if ($http_accept_language ~* "^en") {
            return 302 /en/;
        }
        return 302 /es/;
    }

    # SPA fallback per locale: any unknown path under /es/ or /en/ falls
    # through to that bundle's index.html so the Angular router takes over.
    location /es/ {
        try_files $uri $uri/ /es/index.html;
    }
    location /en/ {
        try_files $uri $uri/ /en/index.html;
    }

    # Catch-all for unprefixed paths. Anything we didn't match above (and
    # that isn't /api/ or one of the OIDC callbacks) is a stray client
    # request — typically a stale `returnUrl` like `/home` from before the
    # /es//en/ split. Redirect it through to the Spanish bundle so the
    # SPA can take over instead of nginx returning its default 404.
    location / {
        if ($http_accept_language ~* "^en") { return 302 /en$request_uri; }
        return 302 /es$request_uri;
    }
}
