"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const core_1 = require("@nestjs/core");
const jwt_1 = require("@nestjs/jwt");
const swagger_1 = require("@nestjs/swagger");
const client_1 = require("@prisma/client");
const helmet_1 = require("helmet");
const app_module_1 = require("./app.module");
const api_paths_1 = require("./common/api-paths");
const CONSOLE_COOKIE_NAME = 'smart_estate_console_token';
async function bootstrap() {
    const app = await core_1.NestFactory.create(app_module_1.AppModule);
    const config = app.get(config_1.ConfigService);
    const jwt = app.get(jwt_1.JwtService);
    app.use((0, helmet_1.default)({
        contentSecurityPolicy: {
            directives: {
                defaultSrc: ["'self'"],
                baseUri: ["'self'"],
                fontSrc: ["'self'", 'https:', 'data:'],
                formAction: ["'self'"],
                frameAncestors: ["'self'"],
                imgSrc: ["'self'", 'data:', 'validator.swagger.io'],
                objectSrc: ["'none'"],
                scriptSrc: ["'self'", "'unsafe-inline'"],
                scriptSrcAttr: ["'none'"],
                styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
                connectSrc: ["'self'", 'http://localhost:*', 'http://127.0.0.1:*'],
                upgradeInsecureRequests: null
            }
        }
    }));
    app.enableCors({
        origin: parseCorsOrigins(config.get('CORS_ORIGINS')),
        credentials: true
    });
    const globalPrefix = (0, api_paths_1.apiPrefix)(config);
    if (globalPrefix) {
        app.setGlobalPrefix(globalPrefix);
    }
    app.enableVersioning({
        type: common_1.VersioningType.URI,
        defaultVersion: (0, api_paths_1.apiVersion)(config)
    });
    app.useGlobalPipes(new common_1.ValidationPipe({
        whitelist: true,
        transform: true,
        forbidNonWhitelisted: true
    }));
    protectConsoleRoutes(app, jwt, config);
    const swaggerBuilder = new swagger_1.DocumentBuilder()
        .setTitle('Smart Estate API')
        .setDescription('Multi-tenant estate access and visitor management platform API')
        .setVersion('1.0')
        .addServer((0, api_paths_1.versionedApiPath)(config), 'Current host API base')
        .addBearerAuth({
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        description: 'JWT Bearer token for authentication'
    }, 'bearer')
        .addTag('Auth', 'Authentication endpoints (OTP-based)')
        .addTag('Residents', 'Resident management endpoints')
        .addTag('Guards', 'Guard management endpoints')
        .addTag('Visitors', 'Visitor and visitor pass management')
        .addTag('Checkin', 'Visitor check-in and check-out')
        .addTag('Incidents', 'Incident reporting and tracking')
        .addTag('Offline', 'Offline sync and data bootstrap')
        .addTag('Estates', 'Estate management (super admin only)')
        .addTag('Notifications', 'Push notification endpoints')
        .addTag('Admin', 'Super admin dashboard and control endpoints')
        .addTag('Onboarding', 'Public estate admin signup, plan selection, hosted sandbox checkout, and payment callback capture')
        .addTag('Sandbox', 'Super admin browser sandbox for trying API requests');
    const publicApiBase = publicApiBaseUrl(config);
    if (publicApiBase) {
        swaggerBuilder.addServer(`${publicApiBase}/v${(0, api_paths_1.apiVersion)(config)}`, 'Configured public API base');
    }
    const swaggerConfig = swaggerBuilder.build();
    const swaggerDocument = swagger_1.SwaggerModule.createDocument(app, swaggerConfig);
    swagger_1.SwaggerModule.setup((0, api_paths_1.docsRoute)(config), app, swaggerDocument, {
        swaggerOptions: {
            persistAuthorization: true
        }
    });
    const port = resolvePort(config);
    await app.listen(port);
    console.log(`API listening on http://localhost:${port}${(0, api_paths_1.versionedApiPath)(config)}`);
    console.log(`Super admin docs available at http://localhost:${port}${(0, api_paths_1.docsPath)(config)}`);
}
function protectConsoleRoutes(app, jwt, config) {
    app.use(async (req, res, next) => {
        if (!isProtectedConsolePath(req.path, config)) {
            return next();
        }
        const token = extractBearerToken(req.headers.authorization) ?? parseCookie(req.headers.cookie)[CONSOLE_COOKIE_NAME];
        if (token && (await isSuperAdminToken(token, jwt, config))) {
            return next();
        }
        if (req.method === 'GET' && acceptsHtml(req)) {
            return res.redirect(`${(0, api_paths_1.consoleLoginPath)(config)}?returnTo=${encodeURIComponent(req.originalUrl || req.url)}`);
        }
        return res.status(401).json({
            statusCode: 401,
            message: 'Super admin console login required',
            error: 'Unauthorized'
        });
    });
}
function isProtectedConsolePath(path, config) {
    const docs = (0, api_paths_1.docsPath)(config);
    const docsJson = (0, api_paths_1.docsJsonPath)(config);
    const docsYaml = (0, api_paths_1.docsYamlPath)(config);
    const sandbox = (0, api_paths_1.sandboxPath)(config);
    return (path === docs ||
        path.startsWith(`${docs}/`) ||
        path === docsJson ||
        path === docsYaml ||
        path === sandbox ||
        path.startsWith(`${sandbox}/`));
}
async function isSuperAdminToken(token, jwt, config) {
    try {
        const payload = await jwt.verifyAsync(token, {
            secret: config.get('JWT_ACCESS_SECRET')
        });
        return payload.role === client_1.UserRole.SUPER_ADMIN;
    }
    catch {
        return false;
    }
}
function extractBearerToken(authorization) {
    const value = Array.isArray(authorization) ? authorization[0] : authorization;
    const match = value?.trim().match(/^Bearer\s+(.+)$/i);
    return match?.[1]?.trim();
}
function parseCookie(cookieHeader) {
    return Object.fromEntries((cookieHeader ?? '')
        .split(';')
        .map((part) => part.trim())
        .filter(Boolean)
        .map((part) => {
        const index = part.indexOf('=');
        if (index === -1) {
            return [part, ''];
        }
        return [part.slice(0, index), decodeURIComponent(part.slice(index + 1))];
    }));
}
function acceptsHtml(req) {
    return req.accepts(['html', 'json']) === 'html';
}
function resolvePort(config) {
    return Number(config.get('PORT') ?? config.get('API_PORT') ?? 4000);
}
function parseCorsOrigins(value) {
    if (!value || value.trim() === '*') {
        return true;
    }
    return value
        .split(',')
        .map((origin) => origin.trim())
        .filter(Boolean);
}
function publicApiBaseUrl(config) {
    const configured = config.get('PUBLIC_API_BASE_URL')?.trim();
    return configured ? configured.replace(/\/+$/, '') : null;
}
void bootstrap();
//# sourceMappingURL=main.js.map