import { HttpException, HttpStatus, Injectable } from "@nestjs/common"
import { InjectRepository } from "@nestjs/typeorm"
import slugify from "slugify"
import { PlanPermission } from "src/entities/plan-permission.entity"
import { Plan } from "src/entities/plan.entity"
import { Repository } from "typeorm"
import { PlanRepository } from "./plan.reporitory"
import { StripeServices } from "src/common/services/payment-gateway/stripe/stripe.service"
import { responseMessages } from "src/messages/response-messages"
import { Constants } from "src/common/constants"
import { Users } from "src/entities/users.entity"
import { request } from "http"

@Injectable()
export class PlanService {
    constructor(
        private planRepository: PlanRepository,
        private stripeService: StripeServices,

        @InjectRepository(Plan)
        private readonly Plan: Repository<Plan>,
        @InjectRepository(PlanPermission)
        private readonly PlanPermission: Repository<PlanPermission>,
        @InjectRepository(Users)
        private readonly Users: Repository<Users>
    ) { }

    /**
     * Create a plan in our database and in Stripe
     * @param planData the data of the plan to create
     * @param authUser the user who is creating the plan
     * @returns the created plan
     * @throws {HttpException} if the plan could not be created in Stripe
     */
    async createPlan(planData: any, authUser: any) {
        const { name, description, year_amount, month_amount, currency, interval, parent_id, most_popular, priority, trial_days } = planData
        // Generate a unique slug

        const uniqueSlug = await this.generateSlug(name);

        await this.checkExists(null, uniqueSlug);
        const product = await this.stripeService.createProduct(name, description)
        if (product.id) {
            const year_price = await this.stripeService.createPrice(product.id, year_amount, currency, 'year')
            const month_price = await this.stripeService.createPrice(product.id, month_amount, currency, 'month')
            if (most_popular) {
                await this.Plan.update({ most_popular: true }, { most_popular: false });
            }
            const newPlan = {
                ...planData,
                slug: uniqueSlug,
                product_id: product.id,
                product_response: product,
                year_price_id: year_price.id,
                year_price_response: year_price,
                month_price_id: month_price.id,
                month_price_response: month_price,
                created_by: authUser.id,
                parent_plan_id: parent_id ? { id: parent_id } : null,
            }
            return await this.planRepository.createPlan(newPlan)
        }
        throw new HttpException(responseMessages.en.plan.stripe_product_not_created, HttpStatus.BAD_REQUEST)
    }

    async getPlanPriorityByPrice(price: number): Promise<number> {
        let count = await this.Plan
            .createQueryBuilder('plan')
            .where('plan.month_amount <= :price', { price })
            .getCount();
        count++;
        return count;
    }
    // Function to generate slug
    async generateSlug(name) {
        // Generate initial slug from the name
        let slug = slugify(name, { lower: true, strict: true })
        return slug
    }

    async checkExists(id, slug) {
        // Check if the slug exists
        let query = await this.Plan.createQueryBuilder("plan").where("slug = :slug", { slug })
        if (id) {
            query.andWhere("id != :id", { id })
        }
        let existing = await query.getCount()
        // If slug exists, append a 4-character random string
        if (existing) {
            throw new HttpException(responseMessages.en.plan.plan_already_exists, HttpStatus.BAD_REQUEST)
        }
    }

    async listPlan(queryParam, authUser) {
        const query = this.Plan.createQueryBuilder("plan")
            .leftJoinAndSelect("plan.permission", "permission")
            .leftJoinAndSelect("plan.parent", "parent")
            .orderBy("plan.priority", "ASC")
            .addOrderBy("permission.order_by", "ASC")
        if (queryParam.status != -1) {
            const user: any = await this.Users.createQueryBuilder("users")
                .leftJoinAndSelect("users.subscription", "subscription")
                .leftJoinAndSelect("subscription.plan", "plan")
                .where("users.id = :id", { id: authUser.id })
                .andWhere("subscription.is_active = true")
                .getOne();
            if (user?.subscription[0]?.plan) {
                console.log(user.subscription[0].plan.priority)
                query.andWhere("plan.priority >= :priority", { priority: user.subscription[0].plan.priority });
            }

            query.andWhere('plan.plan_type = :type', { type: 'premium' })
                .andWhere("plan.status = :status", { status: queryParam.status })
                .andWhere('permission.description IS NOT NULL')
        }
        const plan = await query.getMany()

        return {
            data: plan
        }
    }

    async details(queryParam) {
        const { slug } = queryParam
        const planExists = await this.Plan.findOne({ where: { slug: slug } })
        if (!planExists) {
            throw new HttpException(responseMessages.en.plan.plan_not_found, HttpStatus.NOT_FOUND)
        }
        const plan = await this.Plan.createQueryBuilder("plan")
            .leftJoinAndSelect("plan.permission", "permission")
            .orderBy("permission.order_by", "ASC")
            .where("slug = :slug", { slug })
            .getOne();
        return {
            data: plan
        }
    }

    async updatePlan(planIdParam: any, planData: any, authUser: any) {
        const { name, description, year_amount, month_amount, currency, most_popular, parent_id } = planData

        const { id } = planIdParam
        let updatePlan = {}
        // Generate a unique slug

        const planExists = await this.Plan.findOne({ where: { id: id } })
        if (!planExists) {
            throw new HttpException(responseMessages.en.plan.plan_not_found, HttpStatus.NOT_FOUND)
        }

        const uniqueSlug = await this.generateSlug(name)
        await this.checkExists(id, uniqueSlug)

        // UPDATE STRIPE PRODUCT TITLE & DESCRIPTION IF CHANGE FOUND
        if (planExists.name != name || planExists.description != description) {
            this.stripeService.updateProduct(planExists.product_id, name, description)
        }
        if (most_popular) {
            await this.Plan.update({ most_popular: true }, { most_popular: false });
        }
        // UPDATE STRIPE YEARLY PRODUCT PRICE IF CHANGE FOUND
        if (planExists.year_amount != year_amount) {
            let price = await this.stripeService.createPrice(planExists.product_id, year_amount, currency, 'year')
            planData["year_price_id"] = price.id
            planData["year_price_response"] = price
        }

        if (planExists.month_amount != month_amount) {
            let price = await this.stripeService.createPrice(planExists.product_id, month_amount, currency, 'month')
            planData["month_price_id"] = price.id
            planData["month_price_response"] = price
        }

        updatePlan = {
            ...planData,
            id: id,
            slug: uniqueSlug,
            updated_by: authUser.id,
            parent_plan_id: parent_id ? { id: parent_id } : null,
        }
        return await this.planRepository.updatePlan(updatePlan)
    }

    async chageStatus(planIdParam, authUser) {
        const { id } = planIdParam

        const planExists = await this.Plan.findOne({ where: { id: id } })
        if (!planExists) {
            throw new HttpException(responseMessages.en.plan.plan_not_found, HttpStatus.NOT_FOUND)
        }
        await this.Plan.update({ id: id }, { status: !planExists.status })
        if (planExists.plan_type === 'premium') {
            this.planRepository.updateCustomerBillingPortal();
        }
        return
    }

    async permissions(authUser) {
        //TEST
        return { permissions: Constants.PERMISSIONS };
    }
}
