import { IsBillingEnabled } from "../EnvironmentConfig";
import UserMiddleware from "../Middleware/UserAuthorization";
import ProjectService from "../Services/ProjectService";
import ResellerService from "../Services/ResellerService";
import TeamMemberService from "../Services/TeamMemberService";
import QueryHelper from "../Types/Database/QueryHelper";
import Response from "../Utils/Response";
import BaseAPI from "./BaseAPI";
import BillingService from "../Services/BillingService";
import Errors from "../Utils/Errors";
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
import PositiveNumber from "../../Types/PositiveNumber";
import Project from "../../Models/DatabaseModels/Project";
import BadDataException from "../../Types/Exception/BadDataException";
import Permission from "../../Types/Permission";
import ObjectID from "../../Types/ObjectID";
export default class ProjectAPI extends BaseAPI {
    constructor() {
        var _a, _b;
        super(Project, ProjectService);
        this.router.put(`${(_a = new this.entityType().getCrudApiPath()) === null || _a === void 0 ? void 0 : _a.toString()}/:id/change-plan`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
            var _a;
            try {
                if (!IsBillingEnabled) {
                    throw new BadDataException("Billing is not enabled for this server");
                }
                const projectId = new ObjectID(req.params["id"]);
                const body = req.body || {};
                const data = body["data"] || {};
                const paymentProviderPlanId = data["paymentProviderPlanId"];
                if (!paymentProviderPlanId) {
                    throw new BadDataException("Plan ID is required to change plan");
                }
                // Check for payment methods early before making any Stripe API calls
                const project = await ProjectService.findOneById({
                    id: projectId,
                    select: {
                        paymentProviderCustomerId: true,
                    },
                    props: {
                        isRoot: true,
                    },
                });
                if (!project) {
                    throw new BadDataException("Project not found");
                }
                if (!project.paymentProviderCustomerId) {
                    throw new BadDataException("Payment Provider customer not found");
                }
                const hasPaymentMethods = await BillingService.hasPaymentMethods(project.paymentProviderCustomerId);
                if (!hasPaymentMethods) {
                    throw new BadDataException(Errors.BillingService.NO_PAYMENTS_METHODS);
                }
                const permissions = await this.getPermissionsForTenant(req);
                const hasBillingPermission = permissions.filter((permission) => {
                    return (permission.permission.toString() ===
                        Permission.ProjectOwner.toString() ||
                        permission.permission.toString() ===
                            Permission.ManageProjectBilling.toString());
                }).length > 0;
                if (!hasBillingPermission &&
                    !((_a = req.userAuthorization) === null || _a === void 0 ? void 0 : _a.isMasterAdmin)) {
                    throw new BadDataException(`You need ${Permission.ProjectOwner} or ${Permission.ManageProjectBilling} permission to change project plan`);
                }
                await ProjectService.changePlan({
                    projectId: projectId,
                    paymentProviderPlanId: paymentProviderPlanId,
                });
                return Response.sendEmptySuccessResponse(req, res);
            }
            catch (err) {
                next(err);
            }
        });
        /*
         * This API lists all the projects where user is its team member.
         * This API is usually used to show project selector dropdown in the UI
         */
        this.router.post(`${(_b = new this.entityType()
            .getCrudApiPath()) === null || _b === void 0 ? void 0 : _b.toString()}/list-user-projects`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
            var _a, _b;
            try {
                if (!((_a = req.userAuthorization) === null || _a === void 0 ? void 0 : _a.userId)) {
                    throw new NotAuthenticatedException("User should be logged in to access this API");
                }
                const projectSelect = {
                    _id: true,
                    name: true,
                    trialEndsAt: true,
                    paymentProviderPlanId: true,
                    resellerId: true,
                    isFeatureFlagMonitorGroupsEnabled: true,
                    paymentProviderMeteredSubscriptionStatus: true,
                    paymentProviderSubscriptionStatus: true,
                };
                const teamMembers = await TeamMemberService.findBy({
                    query: {
                        userId: req.userAuthorization.userId,
                        hasAcceptedInvitation: true,
                    },
                    select: {
                        project: projectSelect,
                    },
                    limit: LIMIT_PER_PROJECT,
                    skip: 0,
                    props: {
                        isRoot: true,
                    },
                });
                const projects = [];
                // if billing enabled and is master admin then get all the projects with customer support enabled.
                if (IsBillingEnabled &&
                    ((_b = req.userAuthorization) === null || _b === void 0 ? void 0 : _b.isMasterAdmin)) {
                    const customerSupportProjects = await ProjectService.findBy({
                        query: {
                            letCustomerSupportAccessProject: true,
                        },
                        select: projectSelect,
                        limit: LIMIT_PER_PROJECT,
                        skip: 0,
                        props: {
                            isRoot: true,
                        },
                    });
                    for (const customerSupportProject of customerSupportProjects) {
                        if (!customerSupportProject) {
                            continue;
                        }
                        if (!customerSupportProject._id) {
                            continue;
                        }
                        if (projects.findIndex((project) => {
                            var _a, _b;
                            return (((_a = project._id) === null || _a === void 0 ? void 0 : _a.toString()) ===
                                ((_b = customerSupportProject._id) === null || _b === void 0 ? void 0 : _b.toString()));
                        }) === -1) {
                            projects.push(customerSupportProject);
                        }
                    }
                }
                for (const teamMember of teamMembers) {
                    if (!teamMember.project) {
                        continue;
                    }
                    if (projects.findIndex((project) => {
                        var _a, _b;
                        return (((_a = project._id) === null || _a === void 0 ? void 0 : _a.toString()) ===
                            ((_b = teamMember.project._id) === null || _b === void 0 ? void 0 : _b.toString()));
                    }) === -1) {
                        projects.push(teamMember.project);
                    }
                }
                /*
                 * Batch-fetch resellers for every project in one query instead of
                 * one findOneById per project.
                 */
                const resellerIds = [];
                const seenResellerIds = new Set();
                for (const project of projects) {
                    if (project.resellerId &&
                        !seenResellerIds.has(project.resellerId.toString())) {
                        seenResellerIds.add(project.resellerId.toString());
                        resellerIds.push(project.resellerId);
                    }
                }
                if (resellerIds.length > 0) {
                    const resellers = await ResellerService.findBy({
                        query: {
                            _id: QueryHelper.any(resellerIds.map((id) => {
                                return id.toString();
                            })),
                        },
                        select: {
                            _id: true,
                            enableTelemetryFeatures: true,
                        },
                        limit: LIMIT_PER_PROJECT,
                        skip: 0,
                        props: {
                            isRoot: true,
                        },
                    });
                    const resellersById = new Map();
                    for (const reseller of resellers) {
                        if (reseller._id) {
                            resellersById.set(reseller._id.toString(), reseller);
                        }
                    }
                    for (const project of projects) {
                        if (project.resellerId) {
                            const reseller = resellersById.get(project.resellerId.toString());
                            if (reseller) {
                                project.reseller = reseller;
                            }
                        }
                    }
                }
                return Response.sendEntityArrayResponse(req, res, projects, new PositiveNumber(projects.length), Project);
            }
            catch (err) {
                next(err);
            }
        });
    }
}
//# sourceMappingURL=ProjectAPI.js.map