import { Injectable, Logger } from "@nestjs/common"
import { SubscriptionRepository } from "./repository/subscription.repository"
import { SubscriptionHistoryRepository } from "../subscription-history/repository/subscription-history.repository"
import { AuthService } from "../auth/auth.service"
import { StripeService } from "../stripe/stripe.service"
import { successResponse, failureResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import {
  successMessage,
  errorMessage,
  validationMessage,
  isEmpty,
} from "src/utils/helpers"
import { messageKey } from "src/constants/message-keys"
import { Subscription } from "./entities/subscription.entity"
import { SubscriptionHistory } from "../subscription-history/entities/subscription-history.entity"
import moment from "moment"

const TRIAL_DAYS = 3

@Injectable()
export class SubscriptionService {
  private readonly logger = new Logger(SubscriptionService.name)

  constructor(
    private readonly subscriptionRepository: SubscriptionRepository,
    private readonly subscriptionHistoryRepository: SubscriptionHistoryRepository,
    private readonly authService: AuthService,
    private readonly stripeService: StripeService,
  ) {}

  async getSubscription(token: string) {
    try {
      const loggedInUser: any = await this.authService.getUserByToken(token)

      if (isEmpty(loggedInUser)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.user_not_found),
        )
      }

      const subscription: any = await this.subscriptionRepository.getByParams({
        where: { user_id: loggedInUser.user_id },
        relations: ["plan", "next_plan"],
        orderBy: { created_at: "DESC" },
        findOne: true,
      })

      if (isEmpty(subscription)) {
        return successResponse(
          code.SUCCESS,
          successMessage(messageKey.detail_found, {
            ":data": "Subscription",
          }),
          {
            status: "none",
            plan_name: null,
            trial_start: null,
            trial_end: null,
            current_period_start: null,
            current_period_end: null,
            cancel_at_period_end: false,
            next_plan_id: null,
            next_plan_name: null,
            next_plan_start_date: null,
          } as any,
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.detail_found, { ":data": "Subscription" }),
        {
          id: subscription.id,
          status: subscription.status,
          plan_id: subscription.plan_id,
          plan_name: subscription.plan?.name || null,
          stripe_customer_id: subscription.stripe_customer_id,
          stripe_subscription_id: subscription.stripe_subscription_id,
          trial_start: subscription.trial_start,
          trial_end: subscription.trial_end,
          current_period_start: subscription.current_period_start,
          current_period_end: subscription.current_period_end,
          cancel_at_period_end: !!subscription.cancel_at_period_end,
          cancelled_at: subscription.cancelled_at,
          created_at: subscription.created_at,
          next_plan_id: subscription.next_plan_id || null,
          next_plan_name: subscription.next_plan?.name || null,
          next_plan_start_date: subscription.next_plan_start_date || null,
        } as any,
      )
    } catch (err) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.exception, { ":data": "Subscription" }),
      )
    }
  }

  async startTrial(token: string) {
    try {
      const loggedInUser: any = await this.authService.getUserByToken(token)
      if (isEmpty(loggedInUser)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.user_not_found),
        )
      }

      // Check if user already has/had a trial
      const existingSubscription: any =
        await this.subscriptionRepository.getByParams({
          where: { user_id: loggedInUser.user_id },
          findOne: true,
        })

      if (existingSubscription) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.trial_already_used),
        )
      }

      const trialStart = moment.utc().toDate()
      const trialEnd = moment.utc().add(TRIAL_DAYS, "days").toDate()

      const subscription = new Subscription()
      subscription.user_id = loggedInUser.user_id
      subscription.status = "trialing"
      subscription.trial_start = trialStart
      subscription.trial_end = trialEnd
      subscription.current_period_start = trialStart
      subscription.current_period_end = trialEnd

      await this.subscriptionRepository.save(subscription)

      // Log subscription history
      const history = new SubscriptionHistory()
      history.user_id = loggedInUser.user_id
      history.old_plan_id = null
      history.new_plan_id = null
      history.action = "trial_started"
      await this.subscriptionHistoryRepository.save(history)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.trial_started),
        {
          id: subscription.id,
          status: subscription.status,
          trial_start: subscription.trial_start,
          trial_end: subscription.trial_end,
        } as any,
      )
    } catch (err) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.exception, { ":data": "Trial" }),
      )
    }
  }

  async cancelSubscription(token: string) {
    try {
      const loggedInUser: any = await this.authService.getUserByToken(token)
      if (isEmpty(loggedInUser)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.user_not_found),
        )
      }

      const subscription: any = await this.subscriptionRepository.getByParams({
        where: { user_id: loggedInUser.user_id },
        orderBy: { created_at: "DESC" },
        findOne: true,
      })

      if (isEmpty(subscription)) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.subscription_not_found),
        )
      }

      if (subscription.cancel_at_period_end) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.subscription_already_cancelled),
        )
      }

      // Cancel in Stripe at the end of the current billing period
      if (subscription.stripe_subscription_id) {
        try {
          await this.stripeService.cancelStripeSubscription(
            subscription.stripe_subscription_id,
          )
        } catch (err) {
          this.logger.error("Failed to cancel subscription in Stripe:", err)
          return failureResponse(
            code.ERROR,
            errorMessage(messageKey.exception, {
              ":data": "Stripe Cancellation",
            }),
          )
        }
      }

      subscription.cancel_at_period_end = 1
      subscription.cancelled_at = moment.utc().toDate()
      await this.subscriptionRepository.save(subscription, {
        id: subscription.id,
      })

      // Log subscription history
      const history = new SubscriptionHistory()
      history.user_id = loggedInUser.user_id
      history.old_plan_id = subscription.plan_id
      history.new_plan_id = null
      history.action = "cancelled"
      await this.subscriptionHistoryRepository.save(history)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.subscription_cancelled),
        {
          id: subscription.id,
          status: subscription.status,
          cancel_at_period_end: true,
          cancelled_at: subscription.cancelled_at,
          current_period_end: subscription.current_period_end,
        } as any,
      )
    } catch (err) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.exception, { ":data": "Subscription" }),
      )
    }
  }

  async findActiveByUserId(userId: number): Promise<Subscription | null> {
    const subscription: any = await this.subscriptionRepository.getByParams({
      where: { user_id: userId },
      orderBy: { created_at: "DESC" },
      findOne: true,
    })
    return subscription || null
  }
}
