import express, { type Express } from "express";
import cors from "cors";
import pinoHttp from "pino-http";
import session from "express-session";
import connectPgSimple from "connect-pg-simple";
import router from "./routes";
import { logger } from "./lib/logger";
import { pool } from "@workspace/db";

const PgSession = connectPgSimple(session);

const app: Express = express();

app.set("trust proxy", 1);

// Self-hosted deployments are commonly fronted by a TLS-terminating
// proxy that does NOT forward `X-Forwarded-Proto: https` to the
// upstream (e.g. `tailscale serve`, ngrok free tier, some Cloudflare
// Tunnel configs). Without that header, `req.secure` stays false and
// the session cookie (when marked `secure: true`) is silently dropped
// — login appears to succeed but the very next request is unauth'd.
//
// Setting `TRUST_PROXY_HTTPS=true` tells us to treat every incoming
// request as if it arrived over HTTPS. Only enable this when the
// edge proxy you control actually terminates TLS in front of Tx OS.
const trustProxyHttps =
  (process.env.TRUST_PROXY_HTTPS ?? "").toLowerCase() === "true";
if (trustProxyHttps) {
  app.use((req, _res, next) => {
    req.headers["x-forwarded-proto"] = "https";
    next();
  });
}

app.use(
  pinoHttp({
    logger,
    serializers: {
      req(req) {
        return {
          id: req.id,
          method: req.method,
          url: req.url?.split("?")[0],
        };
      },
      res(res) {
        return {
          statusCode: res.statusCode,
        };
      },
    },
  }),
);

const allowedOrigins = process.env.ALLOWED_ORIGINS
  ? process.env.ALLOWED_ORIGINS.split(",").map((o) => o.trim())
  : null;

app.use(
  cors({
    origin: allowedOrigins
      ? (origin, callback) => {
          if (!origin || allowedOrigins.includes(origin)) {
            callback(null, true);
          } else {
            callback(new Error("Not allowed by CORS"));
          }
        }
      : true,
    credentials: true,
  }),
);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// The session cookie should only be marked `secure` when the user is
// actually reaching us over HTTPS. We derive that from PUBLIC_BASE_URL
// instead of NODE_ENV so a self-hosted production install that's only
// reachable over plain HTTP (e.g. internal LAN, no TLS yet) still
// works without operator surgery.
const publicBaseUrl = process.env.PUBLIC_BASE_URL ?? "";
const sessionCookieSecure =
  publicBaseUrl.startsWith("https://") || trustProxyHttps;

export const sessionMiddleware = session({
  store: new PgSession({
    pool,
    tableName: "user_sessions",
    // Auto-create the session table on first boot. Drizzle's migrations
    // intentionally exclude `user_sessions` (see lib/db/drizzle.config.ts)
    // because the schema is owned by connect-pg-simple, not by us. Without
    // this flag, the very first login on a fresh install 500s.
    createTableIfMissing: true,
  }),
  // Honour `X-Forwarded-Proto` from upstream proxies when deciding
  // whether to set the secure cookie. Combined with `app.set("trust
  // proxy", 1)` above, this makes the cookie work end-to-end behind
  // Caddy / nginx / Cloudflare / Tailscale.
  proxy: true,
  secret: process.env.SESSION_SECRET ?? (() => {
    if (process.env.NODE_ENV === "production") {
      throw new Error("SESSION_SECRET must be set in production");
    }
    return "tx-dev-secret-change-before-deploy";
  })(),
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: sessionCookieSecure,
    httpOnly: true,
    maxAge: 7 * 24 * 60 * 60 * 1000,
    sameSite: "lax",
  },
});

app.use(sessionMiddleware);

app.use("/api", router);

export default app;
