"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var MicrosoftEntraIdProvider_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MicrosoftEntraIdProvider = void 0;
const axios_1 = require("@nestjs/axios");
const common_1 = require("@nestjs/common");
const setting_service_1 = require("../../setting/setting.service");
const abstract_provider_1 = require("./abstract.provider");
let MicrosoftEntraIdProvider = MicrosoftEntraIdProvider_1 = class MicrosoftEntraIdProvider extends abstract_provider_1.BaseOAuthProvider {
    constructor(http, setting) {
        super(http);
        this.setting = setting;
        this.logger = new common_1.Logger(MicrosoftEntraIdProvider_1.name);
    }
    normalizeBaseUrl(url) {
        return String(url).trim().replace(/\/+$/, '');
    }
    buildRedirectUri(baseUrl, callbackPath) {
        const normalizedBaseUrl = this.normalizeBaseUrl(baseUrl);
        const normalizedCallbackPath = callbackPath.startsWith('/')
            ? callbackPath
            : `/${callbackPath}`;
        return new URL(normalizedCallbackPath, `${normalizedBaseUrl}/`).toString();
    }
    getProviderType() {
        return 'MICROSOFT-ENTRA-ID';
    }
    async getAuthUrl(callbackPath) {
        const settings = await this.setting.getSettingValues([
            'microsoft_entra_id_client_id',
            'microsoft_entra_id_client_secret',
            'microsoft_entra_id_scopes',
            'microsoft_entra_id_tenant_id',
            'url',
        ]);
        if (!settings['microsoft_entra_id_client_id'] ||
            !settings['microsoft_entra_id_client_secret'] ||
            !settings['microsoft_entra_id_scopes'] ||
            !settings['microsoft_entra_id_tenant_id'] ||
            !settings['url']) {
            this.logger.error('Microsoft Entra ID OAuth is missing required settings. Verify client id, client secret, scopes, tenant id, and url.');
            throw new Error('Microsoft Entra ID OAuth is not configured properly. Please configure microsoft_entra_id_client_id, microsoft_entra_id_client_secret, microsoft_entra_id_scopes, microsoft_entra_id_tenant_id, and url settings.');
        }
        const tenantId = settings['microsoft_entra_id_tenant_id'];
        const redirectURI = this.buildRedirectUri(settings['url'], callbackPath);
        const baseScopes = Array.isArray(settings['microsoft_entra_id_scopes'])
            ? settings['microsoft_entra_id_scopes']
            : [];
        const scopes = baseScopes.includes('offline_access')
            ? baseScopes
            : [...baseScopes, 'offline_access'];
        const isConnectFlow = callbackPath.includes('/connect');
        const params = new URLSearchParams({
            client_id: settings['microsoft_entra_id_client_id'],
            redirect_uri: redirectURI,
            response_type: 'code',
            scope: scopes.join(' '),
            response_mode: 'query',
        });
        this.logger.log(`OAuth auth URL requested: provider=MICROSOFT-ENTRA-ID redirectUri=${redirectURI} hasOfflineAccess=${scopes.includes('offline_access') ? 'yes' : 'no'} connectFlow=${isConnectFlow ? 'yes' : 'no'} tenantConfigured=${tenantId ? 'yes' : 'no'}`);
        // Force consent only for explicit account connect, not for routine login.
        if (isConnectFlow) {
            params.set('prompt', 'consent');
        }
        const authUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?${params.toString()}`;
        return authUrl;
    }
    async getProfile(code, type) {
        var _a, _b, _c, _d, _e;
        let redirectUri = '';
        try {
            const settings = await this.setting.getSettingValues([
                'microsoft_entra_id_client_id',
                'microsoft_entra_id_client_secret',
                'microsoft_entra_id_scopes',
                'microsoft_entra_id_tenant_id',
                'url',
            ]);
            const tenantId = settings['microsoft_entra_id_tenant_id'];
            const scopes = (_a = settings['microsoft_entra_id_scopes']) !== null && _a !== void 0 ? _a : [];
            redirectUri = this.buildRedirectUri(settings['url'], `/callback/microsoft-entra-id/${type}`);
            this.logger.log(`OAuth token exchange started: provider=MICROSOFT-ENTRA-ID type=${type} redirectUri=${redirectUri} hasCode=${code ? 'yes' : 'no'} codeLength=${(_b = code === null || code === void 0 ? void 0 : code.length) !== null && _b !== void 0 ? _b : 0}`);
            const token = await this.fetchToken({
                code,
                url: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
                clientId: settings['microsoft_entra_id_client_id'],
                clientSecret: settings['microsoft_entra_id_client_secret'],
                redirectUri,
            });
            const profile = await this.fetchProfile(token.access_token, 'https://graph.microsoft.com/v1.0/me');
            this.logger.log(`OAuth profile fetched: provider=MICROSOFT-ENTRA-ID type=${type} profileIdPresent=${(profile === null || profile === void 0 ? void 0 : profile.id) ? 'yes' : 'no'} emailPresent=${(profile === null || profile === void 0 ? void 0 : profile.mail) || (profile === null || profile === void 0 ? void 0 : profile.userPrincipalName) ? 'yes' : 'no'}`);
            const pictureUrl = 'https://graph.microsoft.com/v1.0/me/photo/$value';
            return {
                id: profile.id,
                email: profile.mail || profile.userPrincipalName,
                name: profile.displayName,
                picture: pictureUrl,
                oauth_scopes: scopes.join(','),
                oauth_tokens: {
                    access_token: token.access_token,
                    refresh_token: token.refresh_token,
                    expires_in: token.expires_in,
                    token_type: token.token_type,
                },
            };
        }
        catch (error) {
            const mappedError = this.mapProviderError('Microsoft Entra ID', error);
            this.logger.warn(`OAuth callback failed: provider=MICROSOFT-ENTRA-ID status=${(_c = mappedError.status) !== null && _c !== void 0 ? _c : 'unknown'} error=${(_d = mappedError.providerError) !== null && _d !== void 0 ? _d : 'unknown'} providerCode=${(_e = mappedError.providerErrorCode) !== null && _e !== void 0 ? _e : 'n/a'} reason=${mappedError.reason} redirectUri=${redirectUri || 'unresolved'}`);
            throw mappedError;
        }
    }
};
exports.MicrosoftEntraIdProvider = MicrosoftEntraIdProvider;
exports.MicrosoftEntraIdProvider = MicrosoftEntraIdProvider = MicrosoftEntraIdProvider_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => setting_service_1.SettingService))),
    __metadata("design:paramtypes", [axios_1.HttpService,
        setting_service_1.SettingService])
], MicrosoftEntraIdProvider);
//# sourceMappingURL=microsoft-entra-id.provider.js.map