import Express from "../Utils/Express";
import Response from "../Utils/Response";
import SlackAuthorization from "../Middleware/SlackAuthorization";
import BadRequestException from "../../Types/Exception/BadRequestException";
import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
import BadDataException from "../../Types/Exception/BadDataException";
import { AppApiClientUrl, DashboardClientUrl, Host, HttpProtocol, SlackAppClientId, SlackAppClientSecret, } from "../EnvironmentConfig";
import SlackAppManifest from "../Utils/Workspace/Slack/app-manifest.json";
import URL from "../../Types/API/URL";
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
import API from "../../Utils/API";
import WorkspaceProjectAuthTokenService from "../Services/WorkspaceProjectAuthTokenService";
import ObjectID from "../../Types/ObjectID";
import WorkspaceUserAuthTokenService from "../Services/WorkspaceUserAuthTokenService";
import WorkspaceType from "../../Types/Workspace/WorkspaceType";
import SlackAuthAction from "../Utils/Workspace/Slack/Actions/Auth";
import SlackIncidentActions from "../Utils/Workspace/Slack/Actions/Incident";
import SlackAlertActions from "../Utils/Workspace/Slack/Actions/Alert";
import SlackAlertEpisodeActions from "../Utils/Workspace/Slack/Actions/AlertEpisode";
import SlackIncidentEpisodeActions from "../Utils/Workspace/Slack/Actions/IncidentEpisode";
import SlackScheduledMaintenanceActions from "../Utils/Workspace/Slack/Actions/ScheduledMaintenance";
import LIMIT_MAX from "../../Types/Database/LimitMax";
import SlackMonitorActions from "../Utils/Workspace/Slack/Actions/Monitor";
import SlackOnCallDutyActions from "../Utils/Workspace/Slack/Actions/OnCallDutyPolicy";
import UserMiddleware from "../Middleware/UserAuthorization";
import CommonAPI from "./CommonAPI";
import SlackUtil from "../Utils/Workspace/Slack/Slack";
import { PrivateNoteEmojis, PublicNoteEmojis, } from "../Utils/Workspace/Slack/Actions/ActionTypes";
export default class SlackAPI {
    getRouter() {
        const router = Express.getRouter();
        router.get("/slack/app-manifest", (req, res) => {
            // return app manifest for slack app
            let ServerURL = new URL(HttpProtocol, Host).toString();
            //remove trailing slash if present.
            if (ServerURL.endsWith("/")) {
                ServerURL = ServerURL.slice(0, -1);
            }
            // replace SERVER_URL in the manifest with the actual server url.
            const manifestInString = JSON.stringify(SlackAppManifest).replace(/{{SERVER_URL}}/g, ServerURL.toString());
            // convert it back to json.
            const manifest = JSON.parse(manifestInString);
            return Response.sendJsonObjectResponse(req, res, manifest);
        });
        // this is project specific auth endpoint.
        router.get("/slack/auth/:projectId/:userId", async (req, res) => {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
            if (!SlackAppClientId) {
                return Response.sendErrorResponse(req, res, new BadDataException("Slack App Client ID is not set"));
            }
            if (!SlackAppClientSecret) {
                return Response.sendErrorResponse(req, res, new BadDataException("Slack App Client Secret is not set"));
            }
            const projectId = (_a = req.params["projectId"]) === null || _a === void 0 ? void 0 : _a.toString();
            const userId = (_b = req.params["userId"]) === null || _b === void 0 ? void 0 : _b.toString();
            if (!projectId) {
                return Response.sendErrorResponse(req, res, new BadDataException("Invalid ProjectID in request"));
            }
            if (!userId) {
                return Response.sendErrorResponse(req, res, new BadDataException("Invalid UserID in request"));
            }
            // if there's an error query param.
            const error = (_c = req.query["error"]) === null || _c === void 0 ? void 0 : _c.toString();
            const slackIntegrationPageUrl = URL.fromString(DashboardClientUrl.toString() +
                `/${projectId.toString()}/settings/slack-integration`);
            if (error) {
                return Response.redirect(req, res, slackIntegrationPageUrl.addQueryParam("error", error));
            }
            // slack returns the code on successful auth.
            const code = (_d = req.query["code"]) === null || _d === void 0 ? void 0 : _d.toString();
            if (!code) {
                return Response.sendErrorResponse(req, res, new BadRequestException("Invalid request"));
            }
            // get access token from slack api.
            const redirectUri = URL.fromString(`${AppApiClientUrl.toString()}/slack/auth/${projectId}/${userId}`);
            const requestBody = {
                code: code,
                client_id: SlackAppClientId,
                client_secret: SlackAppClientSecret,
                redirect_uri: redirectUri.toString(),
            };
            logger.debug("Slack Auth Request Body: ", getLogAttributesFromRequest(req));
            logger.debug(requestBody, getLogAttributesFromRequest(req));
            // send the request to slack api to get the access token https://slack.com/api/oauth.v2.access
            const response = await API.post({
                url: URL.fromString("https://slack.com/api/oauth.v2.access"),
                data: requestBody,
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                },
            });
            if (response instanceof HTTPErrorResponse) {
                throw response;
            }
            const responseBody = response.data;
            logger.debug("Slack Auth Request Body: ", getLogAttributesFromRequest(req));
            logger.debug(responseBody, getLogAttributesFromRequest(req));
            let slackTeamId = undefined;
            let slackBotAccessToken = undefined;
            let slackUserId = undefined;
            let slackTeamName = undefined;
            let botUserId = undefined;
            let slackUserAccessToken = undefined;
            /*
             * ReponseBody is in this format.
             *   {
             *     "ok": true,
             *     "access_token": "sample-token",
             *     "token_type": "bot",
             *     "scope": "commands,incoming-webhook",
             *     "bot_user_id": "U0KRQLJ9H",
             *     "app_id": "A0KRD7HC3",
             *     "team": {
             *         "name": "Slack Pickleball Team",
             *         "id": "T9TK3CUKW"
             *     },
             *     "enterprise": {
             *         "name": "slack-pickleball",
             *         "id": "E12345678"
             *     },
             *     "authed_user": {
             *         "id": "U1234",
             *         "scope": "chat:write",
             *         "access_token": "sample-token",
             *         "token_type": "user"
             *     }
             * }
             */
            if (responseBody["ok"] !== true) {
                return Response.sendErrorResponse(req, res, new BadRequestException("Invalid request"));
            }
            if (responseBody["team"] &&
                responseBody["team"]["id"]) {
                slackTeamId = (_e = responseBody["team"]["id"]) === null || _e === void 0 ? void 0 : _e.toString();
            }
            if (responseBody["access_token"]) {
                slackBotAccessToken = (_f = responseBody["access_token"]) === null || _f === void 0 ? void 0 : _f.toString();
            }
            if (responseBody["authed_user"] &&
                responseBody["authed_user"]["id"]) {
                slackUserId = (_g = responseBody["authed_user"]["id"]) === null || _g === void 0 ? void 0 : _g.toString();
            }
            if (responseBody["authed_user"] &&
                responseBody["authed_user"]["access_token"]) {
                slackUserAccessToken = (_h = responseBody["authed_user"]["access_token"]) === null || _h === void 0 ? void 0 : _h.toString();
            }
            if (responseBody["team"] &&
                responseBody["team"]["name"]) {
                slackTeamName = (_j = responseBody["team"]["name"]) === null || _j === void 0 ? void 0 : _j.toString();
            }
            if (responseBody["bot_user_id"]) {
                botUserId = (_k = responseBody["bot_user_id"]) === null || _k === void 0 ? void 0 : _k.toString();
            }
            await WorkspaceProjectAuthTokenService.refreshAuthToken({
                projectId: new ObjectID(projectId),
                workspaceType: WorkspaceType.Slack,
                authToken: slackBotAccessToken || "",
                workspaceProjectId: slackTeamId || "",
                miscData: {
                    teamId: slackTeamId || "",
                    teamName: slackTeamName || "",
                    botUserId: botUserId || "",
                },
            });
            await WorkspaceUserAuthTokenService.refreshAuthToken({
                projectId: new ObjectID(projectId),
                userId: new ObjectID(userId),
                workspaceType: WorkspaceType.Slack,
                authToken: slackUserAccessToken || "",
                workspaceUserId: slackUserId || "",
                miscData: {
                    userId: slackUserId || "",
                },
            });
            // return back to dashboard after successful auth.
            Response.redirect(req, res, slackIntegrationPageUrl);
        });
        // this is user specific auth endpoint to sign in to slack.
        router.get("/slack/auth/:projectId/:userId/user", async (req, res) => {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
            if (!SlackAppClientId) {
                return Response.sendErrorResponse(req, res, new BadDataException("Slack App Client ID is not set"));
            }
            if (!SlackAppClientSecret) {
                return Response.sendErrorResponse(req, res, new BadDataException("Slack App Client Secret is not set"));
            }
            const projectId = (_a = req.params["projectId"]) === null || _a === void 0 ? void 0 : _a.toString();
            const userId = (_b = req.params["userId"]) === null || _b === void 0 ? void 0 : _b.toString();
            if (!projectId) {
                return Response.sendErrorResponse(req, res, new BadDataException("Invalid ProjectID in request"));
            }
            if (!userId) {
                return Response.sendErrorResponse(req, res, new BadDataException("Invalid UserID in request"));
            }
            // if there's an error query param.
            const error = (_c = req.query["error"]) === null || _c === void 0 ? void 0 : _c.toString();
            const slackIntegrationPageUrl = URL.fromString(DashboardClientUrl.toString() +
                `/${projectId.toString()}/settings/slack-integration`);
            if (error) {
                return Response.redirect(req, res, slackIntegrationPageUrl.addQueryParam("error", error));
            }
            // slack returns the code on successful auth.
            const code = (_d = req.query["code"]) === null || _d === void 0 ? void 0 : _d.toString();
            if (!code) {
                return Response.sendErrorResponse(req, res, new BadRequestException("Invalid request"));
            }
            // get access token from slack api.
            const redirectUri = URL.fromString(`${AppApiClientUrl.toString()}/slack/auth/${projectId}/${userId}/user`);
            const requestBody = {
                code: code,
                client_id: SlackAppClientId,
                client_secret: SlackAppClientSecret,
                redirect_uri: redirectUri.toString(),
            };
            logger.debug("Slack Auth Request Body: ", getLogAttributesFromRequest(req));
            logger.debug(requestBody, getLogAttributesFromRequest(req));
            // send the request to slack api to get the access token https://slack.com/api/oauth.v2.access
            const response = await API.post({
                url: URL.fromString("https://slack.com/api/openid.connect.token"),
                data: requestBody,
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                },
            });
            if (response instanceof HTTPErrorResponse) {
                throw response;
            }
            const responseBody = response.data;
            logger.debug("Slack User Auth Request Body: ", getLogAttributesFromRequest(req));
            logger.debug(responseBody, getLogAttributesFromRequest(req));
            if (responseBody["id_token"] &&
                typeof responseBody["id_token"] === "string" &&
                responseBody["id_token"].split(".").length > 0) {
                const idToken = responseBody["id_token"];
                const decodedIdToken = JSON.parse(Buffer.from(((_e = idToken.split(".")) === null || _e === void 0 ? void 0 : _e[1]) || "", "base64").toString("utf8"));
                logger.debug("Decoded ID Token: ", getLogAttributesFromRequest(req));
                logger.debug(decodedIdToken, getLogAttributesFromRequest(req));
                responseBody["id_token"] = decodedIdToken;
            }
            const idToken = responseBody["id_token"];
            /*
             * Example of Response Body
             * {
             *   "iss": "https://slack.com",
             *   "sub": "U123ABC456",
             *   "aud": "25259531569.1115258246291",
             *   "exp": 1626874955,
             *   "iat": 1626874655,
             *   "auth_time": 1626874655,
             *   "nonce": "abcd",
             *   "at_hash": "abc...123",
             *   "https://slack.com/team_id": "T0123ABC456",
             *   "https://slack.com/user_id": "U123ABC456",
             *   "email": "alice@example.com",
             *   "email_verified": true,
             *   "date_email_verified": 1622128723,
             *   "locale": "en-US",
             *   "name": "Alice",
             *   "given_name": "",
             *   "family_name": "",
             *   "https://slack.com/team_image_230": "https://secure.gravatar.com/avatar/bc.png",
             *   "https://slack.com/team_image_default": true
             * }
             */
            /*
             * check if the team id matches the project id.
             * get project auth.
             */
            const projectAuth = await WorkspaceProjectAuthTokenService.findOneBy({
                query: {
                    projectId: new ObjectID(projectId),
                    workspaceType: WorkspaceType.Slack,
                },
                select: {
                    workspaceProjectId: true,
                    miscData: true,
                },
                props: {
                    isRoot: true,
                },
            });
            // cehck if the workspace project id is same as the team id.
            if (projectAuth) {
                logger.debug("Project Auth: ", getLogAttributesFromRequest(req));
                logger.debug(projectAuth.workspaceProjectId, getLogAttributesFromRequest(req));
                logger.debug("Response Team ID: ", getLogAttributesFromRequest(req));
                logger.debug(idToken["https://slack.com/team_id"], getLogAttributesFromRequest(req));
                logger.debug("Response User ID: ", getLogAttributesFromRequest(req));
                logger.debug(idToken["https://slack.com/user_id"], getLogAttributesFromRequest(req));
                if (((_f = projectAuth.workspaceProjectId) === null || _f === void 0 ? void 0 : _f.toString()) !==
                    ((_g = idToken["https://slack.com/team_id"]) === null || _g === void 0 ? void 0 : _g.toString())) {
                    const teamName = (_h = projectAuth.miscData) === null || _h === void 0 ? void 0 : _h.teamName;
                    // send error response.
                    return Response.redirect(req, res, slackIntegrationPageUrl.addQueryParam("error", "Looks like you are trying to sign in to a different slack workspace. Please try again and sign in to the workspace " +
                        teamName));
                }
            }
            else {
                // send error response.
                return Response.redirect(req, res, slackIntegrationPageUrl.addQueryParam("error", "Looks like this OneUptime project is not connected to any slack workspace. Please try again and sign in to the workspace"));
            }
            const authToken = (_j = responseBody["access_token"]) === null || _j === void 0 ? void 0 : _j.toString();
            const slackUserId = (_k = idToken["https://slack.com/user_id"]) === null || _k === void 0 ? void 0 : _k.toString();
            if (!slackUserId) {
                return Response.redirect(req, res, slackIntegrationPageUrl.addQueryParam("error", "Unfortunately, we were unable to get your slack user id. Please try again."));
            }
            await WorkspaceUserAuthTokenService.refreshAuthToken({
                projectId: new ObjectID(projectId),
                userId: new ObjectID(userId),
                workspaceType: WorkspaceType.Slack,
                authToken: authToken || "",
                workspaceUserId: slackUserId || "",
                miscData: {
                    userId: slackUserId || "",
                },
            });
            // return back to dashboard after successful auth.
            Response.redirect(req, res, slackIntegrationPageUrl);
        });
        router.post("/slack/interactive", SlackAuthorization.isAuthorizedSlackRequest, async (req, res) => {
            logger.debug("Slack Interactive Request: ", getLogAttributesFromRequest(req));
            const authResult = await SlackAuthAction.isAuthorized({
                req: req,
            });
            logger.debug("Slack Interactive Auth Result: ", getLogAttributesFromRequest(req));
            logger.debug(authResult, getLogAttributesFromRequest(req));
            // if slack uninstall app then,
            if (authResult.payloadType === "app_uninstall") {
                logger.debug("Slack App Uninstall Request: ", getLogAttributesFromRequest(req));
                // remove the project auth and user auth.
                // delete all user auth tokens for this project.
                await WorkspaceUserAuthTokenService.deleteBy({
                    query: {
                        projectId: authResult.projectId,
                        workspaceType: WorkspaceType.Slack,
                    },
                    limit: LIMIT_MAX,
                    skip: 0,
                    props: {
                        isRoot: true,
                    },
                });
                await WorkspaceProjectAuthTokenService.deleteBy({
                    query: {
                        projectId: authResult.projectId,
                        workspaceType: WorkspaceType.Slack,
                    },
                    limit: 1,
                    skip: 0,
                    props: {
                        isRoot: true,
                    },
                });
                logger.debug("Slack App Uninstall Request: Deleted all auth tokens.", getLogAttributesFromRequest(req));
                // return empty response.
                return Response.sendTextResponse(req, res, "");
            }
            if (authResult.isAuthorized === false) {
                // return empty response if not authorized. Do nothing in this case.
                return Response.sendTextResponse(req, res, "");
            }
            for (const action of authResult.actions || []) {
                if (!action.actionType) {
                    return Response.sendErrorResponse(req, res, new BadRequestException("Invalid request"));
                }
                if (SlackIncidentActions.isIncidentAction({
                    actionType: action.actionType,
                })) {
                    return SlackIncidentActions.handleIncidentAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
                if (SlackOnCallDutyActions.isOnCallDutyAction({
                    actionType: action.actionType,
                })) {
                    return SlackOnCallDutyActions.handleOnCallDutyAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
                if (SlackAlertActions.isAlertAction({
                    actionType: action.actionType,
                })) {
                    return SlackAlertActions.handleAlertAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
                if (SlackAlertEpisodeActions.isAlertEpisodeAction({
                    actionType: action.actionType,
                })) {
                    return SlackAlertEpisodeActions.handleAlertEpisodeAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
                if (SlackIncidentEpisodeActions.isIncidentEpisodeAction({
                    actionType: action.actionType,
                })) {
                    return SlackIncidentEpisodeActions.handleIncidentEpisodeAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
                if (SlackMonitorActions.isMonitorAction({
                    actionType: action.actionType,
                })) {
                    return SlackMonitorActions.handleMonitorAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
                if (SlackScheduledMaintenanceActions.isScheduledMaintenanceAction({
                    actionType: action.actionType,
                })) {
                    return SlackScheduledMaintenanceActions.handleScheduledMaintenanceAction({
                        slackRequest: authResult,
                        action: action,
                        req: req,
                        res: res,
                    });
                }
            }
            return Response.sendErrorResponse(req, res, new BadRequestException("Invalid request"));
        });
        // Fetch and cache all Slack channels for current tenant's project
        router.get("/slack/get-all-channels", UserMiddleware.getUserMiddleware, async (req, res) => {
            var _a, _b;
            const props = await CommonAPI.getDatabaseCommonInteractionProps(req);
            if (!props.tenantId) {
                return Response.sendErrorResponse(req, res, new BadRequestException("ProjectId (tenant) not found in request"));
            }
            // Get Slack project auth
            const projectAuth = await WorkspaceProjectAuthTokenService.getProjectAuth({
                projectId: props.tenantId,
                workspaceType: WorkspaceType.Slack,
            });
            if (!projectAuth || !projectAuth.authToken) {
                return Response.sendErrorResponse(req, res, new BadRequestException("Slack is not connected for this project. Please connect Slack first."));
            }
            // Fetch all channels (also updates cache under miscData.channelCache)
            let updatedProjectAuth = projectAuth;
            if (!((_a = projectAuth.miscData) === null || _a === void 0 ? void 0 : _a.channelCache)) {
                await SlackUtil.getAllWorkspaceChannels({
                    authToken: projectAuth.authToken,
                    projectId: props.tenantId,
                });
                // Re-fetch to return the latest cached object
                updatedProjectAuth =
                    await WorkspaceProjectAuthTokenService.getProjectAuth({
                        projectId: props.tenantId,
                        workspaceType: WorkspaceType.Slack,
                    });
            }
            const channelCache = ((_b = ((updatedProjectAuth === null || updatedProjectAuth === void 0 ? void 0 : updatedProjectAuth.miscData) || {})) === null || _b === void 0 ? void 0 : _b.channelCache) || {};
            return Response.sendJsonObjectResponse(req, res, channelCache);
        });
        // Slack Events API endpoint for handling events like emoji reactions
        router.post("/slack/events", SlackAuthorization.isAuthorizedSlackRequest, async (req, res) => {
            var _a, _b;
            logger.debug("Slack Events API Request received", getLogAttributesFromRequest(req));
            logger.debug(req.body, getLogAttributesFromRequest(req));
            const payload = req.body;
            // Handle URL verification challenge from Slack
            if (payload["type"] === "url_verification") {
                logger.debug("Slack URL verification challenge received", getLogAttributesFromRequest(req));
                return Response.sendJsonObjectResponse(req, res, {
                    challenge: payload["challenge"],
                });
            }
            // Handle event callbacks
            if (payload["type"] === "event_callback") {
                const event = payload["event"];
                if (!event) {
                    logger.debug("No event found in payload", getLogAttributesFromRequest(req));
                    return Response.sendTextResponse(req, res, "ok");
                }
                // Handle reaction_added events
                if (event["type"] === "reaction_added") {
                    logger.debug("Reaction added event received", getLogAttributesFromRequest(req));
                    /*
                     * Respond immediately to Slack to prevent retry
                     * Process the event asynchronously
                     */
                    Response.sendTextResponse(req, res, "ok");
                    const reactionData = {
                        teamId: payload["team_id"],
                        reaction: event["reaction"],
                        userId: event["user"],
                        channelId: (_a = event["item"]) === null || _a === void 0 ? void 0 : _a["channel"],
                        messageTs: (_b = event["item"]) === null || _b === void 0 ? void 0 : _b["ts"],
                    };
                    // OPTIMIZATION: Quick check if this is a supported emoji before any DB queries
                    const isSupportedEmoji = PrivateNoteEmojis.includes(reactionData.reaction) ||
                        PublicNoteEmojis.includes(reactionData.reaction);
                    if (!isSupportedEmoji) {
                        logger.debug(`Emoji "${reactionData.reaction}" is not supported. Skipping.`, getLogAttributesFromRequest(req));
                        return;
                    }
                    /*
                     * Process emoji reactions for Incidents, Alerts, and Scheduled Maintenance
                     * Each handler will silently ignore if the channel is not linked to their resource type
                     */
                    try {
                        await SlackIncidentActions.handleEmojiReaction(reactionData);
                    }
                    catch (err) {
                        logger.error("Error handling incident emoji reaction:", getLogAttributesFromRequest(req));
                        logger.error(err, getLogAttributesFromRequest(req));
                    }
                    try {
                        await SlackAlertActions.handleEmojiReaction(reactionData);
                    }
                    catch (err) {
                        logger.error("Error handling alert emoji reaction:", getLogAttributesFromRequest(req));
                        logger.error(err, getLogAttributesFromRequest(req));
                    }
                    try {
                        await SlackAlertEpisodeActions.handleEmojiReaction(reactionData);
                    }
                    catch (err) {
                        logger.error("Error handling alert episode emoji reaction:", getLogAttributesFromRequest(req));
                        logger.error(err, getLogAttributesFromRequest(req));
                    }
                    try {
                        await SlackIncidentEpisodeActions.handleEmojiReaction(reactionData);
                    }
                    catch (err) {
                        logger.error("Error handling incident episode emoji reaction:", getLogAttributesFromRequest(req));
                        logger.error(err, getLogAttributesFromRequest(req));
                    }
                    try {
                        await SlackScheduledMaintenanceActions.handleEmojiReaction(reactionData);
                    }
                    catch (err) {
                        logger.error("Error handling scheduled maintenance emoji reaction:", getLogAttributesFromRequest(req));
                        logger.error(err, getLogAttributesFromRequest(req));
                    }
                    return;
                }
            }
            // For any other event types, just acknowledge
            return Response.sendTextResponse(req, res, "ok");
        });
        // options load endpoint.
        router.post("/slack/options-load", SlackAuthorization.isAuthorizedSlackRequest, (req, res) => {
            return Response.sendJsonObjectResponse(req, res, {
                response_action: "clear",
            });
        });
        return router;
    }
}
//# sourceMappingURL=SlackAPI.js.map