import { NestFactory } from '@nestjs/core';
import * as express from 'express';
import { AppModule } from './app.module';
import { shopContext } from './common/shop-context';
import { verifySessionToken } from './auth/session-token';
import { verifyAffiliateToken } from './common/affiliate-token';
import { DEMO_SHOP, isDemoReq } from './common/demo';

const SHOP_RE = /^[a-zA-Z0-9][a-zA-Z0-9-]*\.myshopify\.com$/;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api');
  app.enableCors({ origin: true, credentials: true });
  // Preserve the raw request body so Shopify webhook HMAC signatures can be
  // verified byte-for-byte (JSON.stringify would not reproduce the exact bytes).
  app.use(
    express.json({
      verify: (req: any, _res, buf) => {
        req.rawBody = buf;
      },
    }),
  );
  // Resolve the current shop for this request and run the rest inside that shop
  // context, so getMerchant() serves the right merchant. Resolution order:
  //   1. App Bridge session token (Bearer)  → embedded merchant admin
  //   2. Affiliate portal token (Bearer)     → the shop the affiliate belongs to
  //   3. `x-shop-domain` header              → token-less public affiliate pages
  //      (signup / login / signup-config) tell us which store they're joining
  //   4. demo header                         → shared read-only demo shop
  // Without this, token-less public routes fell back to a single default store,
  // so a public signup was never tied to the store whose admin approves it.
  app.use((req: any, _res: express.Response, next: express.NextFunction) => {
    const auth = req.headers['authorization'];
    let shop: string | undefined;
    if (typeof auth === 'string' && auth.startsWith('Bearer ')) {
      const bearer = auth.slice(7);
      // Try a Shopify session token first (embedded admin) …
      shop = verifySessionToken(bearer)?.shop;
      // … else an affiliate portal token, which carries its own shop.
      if (!shop) shop = verifyAffiliateToken(bearer)?.shop;
    }
    // Public affiliate pages carry no token — trust the store domain they name.
    if (!shop) {
      const hdr = req.headers['x-shop-domain'];
      if (typeof hdr === 'string' && SHOP_RE.test(hdr)) shop = hdr;
    }
    // Public read-only demo: no token, but the demo header maps to the demo shop.
    if (!shop && isDemoReq(req)) shop = DEMO_SHOP;
    shopContext.run({ shop }, () => next());
  });
  const port = process.env.PORT ?? 4000;
  await app.listen(port);
  console.log(`\n  Marcadeo–Shopify API running on http://localhost:${port}/api\n`);
}
bootstrap();
