import { BadRequestException, HttpException, HttpStatus, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import * as dto from './dto/index'
import { InjectStripe } from 'nestjs-stripe';
import Stripe from 'stripe'
import { ModelService } from '../model/model.service';
import { on } from 'events';
import moment from 'moment'
// import { CommonServices } from 'src/common/common.services';
import { CommonService } from '../common/common.service';
import * as mongoose from 'mongoose'
import * as jwt from 'jsonwebtoken'
import { error } from 'console';
import { JwtService } from '@nestjs/jwt';
import { Types } from 'mongoose'

@Injectable()
export class StripeService {
  constructor(
    @InjectStripe() private readonly stripeClient: Stripe,
    private readonly model: ModelService,
    private readonly CommonServices: CommonService,
    private JwtService: JwtService,

  ) { }

  async createCustomer(body: any) {
    try {
      let { email, first_name } = body;
      //  let name= 
      //     console.log('name,',name);
      let data = {
        name: first_name ? first_name : email.split('@')[0],
        email: email.toLowerCase()
      }
      let customer = await this.stripeClient.customers.create(data)
      // console.log('create........',customer);
      return customer;
    } catch (error) {
      throw error
    }
  }

  async create_token(req: any) {
    try {
      let { _id: user_id } = req.user_data;
      let query = { _id: user_id }
      let option = { lean: true };

      const cardToken: any = 'tok_visa'
      const paymentMethod = await this.stripeClient.paymentMethods.create({
        type: 'card',
        card: {
          token: cardToken,
        },
      });

      return paymentMethod;

    } catch (error) {
      throw error;
    }
  }



  async update_customer(payment_method: string, customer_id: string) {
    try {
      let result = await this.stripeClient.customers.update(
        customer_id,
        {
          invoice_settings: {
            default_payment_method: payment_method
          }
        }
      );
      console.log("update custmer ", result);

    }
    catch (err) {
      throw err;
    }
  }
  //  async gen_token(body:any,user_id:string,customer_id:any){
  //       try {
  //           let {card_no, exp_month, exp_year, cvc} = body;

  //           let data: any = {
  //             card: {
  //                 number: card_no,
  //                 exp_month: Number(exp_month),
  //                 exp_year: Number(exp_year),
  //                 cvc: cvc,
  //             },
  //         };



  //               const token = await this.stripeClient.tokens.create(data);
  //              console.log('tokendata......',token);

  //                let{id:token_id}=token;

  //               let id = customer_id

  //               let data1 = {
  //                   source:token_id
  //               }

  //               const card = await this.stripeClient.customers.createSource(id, data1)
  //              console.log('cardDatat......',card);

  //               let{id:card_id}=card
  //              console.log('carddata.1.....',card_id);
  //              let data_to_save:any={
  //               user_id,  
  //               card_id,
  //               card_no,
  //               exp_month,
  //               exp_year,
  //               token_id,
  //               cvc,
  //               brand:token.card.brand
  //             }
  //               let add_card= await this.model.cardModel.create(data_to_save)
  //                console.log('create_customer.......', card);
  //                return add_card;
  //       } catch (error) {
  //           throw error
  //       }
  //     }

  async create_plan(body: dto.plan.plan) {
    try {
      let { name, description, amount, interval, interval_count, currency, highlights } = body;

      let add_product = await this.stripeClient.products.create({
        name: name,
        description: description
      })
      if (add_product) {
        let { id: product_id } = add_product;
        let data: any = {
          amount: amount * 100,
          currency: currency,
          interval: interval,
          interval_count: interval_count || 1, // Default to 1 if not provided in req.body
          product: product_id,
        }

        let create_plan = await this.stripeClient.plans.create(data)
        if (create_plan) {
          let { id: plan_id } = create_plan;
          let data_to_save = {
            name,
            description,
            amount,
            product_id,
            interval,
            interval_count,
            currency,
            plan_id,
            is_delete: false,
            highlights,
            created_at: moment().utc().valueOf()
          }
          let plan = await this.model.planModel.create(data_to_save)
          return plan
        } else {
          throw new InternalServerErrorException("Error in creating plan")
        }
      } else {
        throw new InternalServerErrorException("Error in creating plan")
      }
    } catch (error) {
      throw error
    }
  }

  async access_token_decode(req: any) {
    try {

      const token = req.headers.authorization;
      console.log('token,token', token);

      if (!!token) {
        let new_token = token.split(' ')[1]
        const payload = await this.JwtService.verifyAsync(new_token, { secret: 'admin_super_seckret_key' })
        let { scope, _id } = payload
        console.log('payload', payload);
        // let user_data= await this.model.userModel.find({_id:_id})
        return _id
      }
    } catch (error) {
      throw error
    }
  }

  async list_plan(req: any) {
    try {
      let user_id = await this.access_token_decode(req)
      let query = { is_delete: false };
      let projection = { __v: 0 };
      let options = { lean: true, sort: { amount: 1 } }
      let plans: any = await this.model.planModel.find(query, projection, options)
      for (let i = 0; i < plans.length; i++) {
        const plan = plans[i];
        let query1 = {
          user_id: new mongoose.Types.ObjectId(user_id),
          plan_id: plan._id
        }
        let data1 = await this.model.subscriptionModel.find(query1, { __v: 0 }, { lean: true })
        if (data1.length) {
          plans[i].is_buy_subscription = true
        } else {
          plans[i].is_buy_subscription = false
        }
      }
      return plans;
    } catch (error) {
      throw error
    }
  }

  async get_plan(_id: any) {
    try {
      let query = { _id: _id };
      let projection = { _v: 0 };
      let options = { lean: true }
      let plan = await this.model.planModel.findOne(query, projection, options)
      return plan
    } catch (error) {
      throw error
    }
  }

  async delete_plan(_id: any) {
    try {
      let query = { _id: _id };
      let projection = { _v: 0 };
      let options = { lean: true }
      let plan = await this.model.planModel.findOne(query, projection, options)
      let { plan_id } = plan
      let delete_plan = await this.stripeClient.plans.del(plan_id)
      let update_plan = await this.model.planModel.findOneAndUpdate(query, { is_delete: true }, { new: true })
      return update_plan
    } catch (error) {
      throw error
    }
  }

  async create_subscription(body: dto.plan.subscription, req: any) {
    try {
      let { plan_id, payment_method_id } = body;
      let { _id: vendor_id, customer_id } = req.user_data;

      if (!customer_id) {
        let user_detail = await this.model.UserModel.findOne({ _id: new Types.ObjectId(vendor_id) }, {}, { lean: true })

        let stripe_customer = await this.stripeClient.customers.create({
          name: user_detail.name,
          email: user_detail.email,
          description: "Customer",
        });

        customer_id = stripe_customer.id
        let sub_data = await this.model.UserModel.findOneAndUpdate({ _id: new Types.ObjectId(vendor_id) }, { customer_id: stripe_customer.id }, { new: true })
      }


      let sub_data = await this.model.subscriptionModel.find({ vendor_id: vendor_id, status: 'active', is_active: true }, { __v: 0 }, { lean: true })
      if (sub_data.length) {
        throw new HttpException('you already have subscription', HttpStatus.BAD_REQUEST)
      }
      let query = { _id: plan_id }
      let projection = { _v: 0 };
      let options = { lean: true };
      let retrive_plan = await this.model.planModel.findOne(query, projection, options)
      // console.log('retrive_plan', retrive_plan);

      let attach_pm = await this.stripeClient.paymentMethods.attach(payment_method_id, { customer: customer_id })
      let { plan_id: stripe_plan_id } = retrive_plan
      // let stripe_retrive_plan = await this.stripeClient.plans.list()
      // console.log(stripe_retrive_plan);

      let data = {
        default_payment_method: payment_method_id,
        customer: customer_id,
        items: [{ plan: stripe_plan_id }]
      }
      let subscriptions = await this.stripeClient.subscriptions.create(data)
      // console.log('subscriptions', subscriptions);
      const plan = await this.get_plan(plan_id)

      let { id: subscription_id } = subscriptions;
      let product_id = null

      let data_to_save = {
        vendor_id: vendor_id,
        plan_id,
        subscription_id,
        product_id: product_id,
        start_date: subscriptions.current_period_start * 1000,
        renewal_date: subscriptions.current_period_end * 1000,
        status: subscriptions.status,
        amount: plan.amount,
        is_active: true,
        created_at: moment().utc().valueOf()
      }
      let saveData = await this.model.subscriptionModel.create(data_to_save)

      await this.model.paymentModel.create({
        vendor_id: vendor_id,
        amount: plan.amount,
        currency: plan.currency,
        payment_method: 'Card',
        status: 'Success',
        created_at: moment().utc().valueOf(),
      })
      return saveData;

    } catch (error) {
      throw error
    }
  }


  async upgradeSubscription(body: dto.plan.upgradeSubscription, req: any) {
    try {
      // let { _id: expert_id } = req.expert_data;
      let { plan_id } = body;
      let { _id: vendor_id, customer_id } = req.user_data
      // let sub_query = { _id: subscription_id, status: 'active' };
      let subscription: any = await this.model.subscriptionModel.findOne({ user_id: vendor_id, status: 'active' }, { _v: 0 }, { lean: true })

      if (!subscription) {
        throw new HttpException("subscription expire buy new subscription", HttpStatus.NOT_FOUND)
      }
      else {
        const { plan_id: new_plan_id } = await this.get_plan(plan_id)
        const { subscription_id: subscriptionId } = subscription
        const retrieve_subscription = await this.stripeClient.subscriptions.retrieve(subscriptionId);

        const updatedSubscription = await this.stripeClient.subscriptions.update(subscriptionId, {
          cancel_at_period_end: false,
          items: [{
            id: retrieve_subscription.items.data[0].id,
            plan: new_plan_id
          }],
          proration_behavior: 'none'
        });
        const plan = await this.get_plan(plan_id)
        let data_to_update = {
          plan_id: plan_id,
          subscription_id: updatedSubscription.id,
          start_date: updatedSubscription.current_period_start * 1000,
          renewal_date: updatedSubscription.current_period_end * 1000,
          status: updatedSubscription.status,
          amount: plan.amount,
          created_at: moment().utc().valueOf()
        }

        let updated_data = await this.model.subscriptionModel.findOneAndUpdate({ _id: subscription._id }, data_to_update, { new: true })

        return updated_data;
      }
    } catch (error) {
      throw error
    }
  }

  async cancel_subscription(req: any) {
    try {
      let { _id: user_id } = req.user_data
      // let query = { _id: user_id }
      // let projection = { _v: 0 };
      // let options = { lean: true };
      // console.log("data_subs");

      // let fetch_expert = await this.model.userModel.findOne(query, projection, options)
      let query_sub = { user_id: user_id, status: 'active' };
      let data_subs: any = await this.model.subscriptionModel.findOne(query_sub, { _v: 0 }, { lean: true, sort: { _id: -1 } })
      console.log(data_subs);

      if (!data_subs) {
        throw new NotFoundException("you don't have any subscription")
      }
      // if(data_subs.status ==subs_status.FREE){
      //   throw new BadRequestException("You can't cancel a free subscription")
      // }
      const deleted = await this.stripeClient.subscriptions.update(data_subs.subscription_id, { cancel_at_period_end: true });
      // console.log(deleted);

      const retrive_subscription = await this.stripeClient.subscriptions.retrieve(data_subs.subscription_id)
      // if (retrive_subscription.status != "canceled") {
      //   throw new InternalServerErrorException()
      // }
      let update = {
        status: retrive_subscription.status,
        is_active: retrive_subscription.status != "active" ? false : true
      }
      await this.model.subscriptionModel.findOneAndUpdate({ _id: data_subs._id }, update)
      return {
        success: true,
        message: "Your Subscription is Cancelled"
      }
    } catch (error) {
      console.log(error);

      throw error
    }
  }

  async get_user_subscription(req: any) {
    try {
      let { _id: user_id, customer_id } = req.user_data;
      let query = { user_id: user_id, status: 'active' }
      let projection = { _v: 0 };
      let options = { lean: true };
      let retrive_subscription = await this.model.subscriptionModel.find(query, projection, options).populate('plan_id')
      if (!retrive_subscription.length) {
        throw new HttpException("You don't have any subscription", HttpStatus.BAD_REQUEST)
      }

      return retrive_subscription[0]
    } catch (error) {
      throw error
    }
  }

}
