import { PrismaClient } from '@typebot.io/prisma'
import { promptAndSetEnvironment } from './utils'
import { Stripe } from 'stripe'
import { createId } from '@paralleldrive/cuid2'
import { writeFileSync } from 'fs'

const migrateSubscriptionsToUsageBased = async () => {
  await promptAndSetEnvironment()
  const prisma = new PrismaClient()

  if (
    !process.env.STRIPE_STARTER_CHATS_PRICE_ID ||
    !process.env.STRIPE_PRO_CHATS_PRICE_ID ||
    !process.env.STRIPE_SECRET_KEY ||
    !process.env.STRIPE_STARTER_PRICE_ID ||
    !process.env.STRIPE_PRO_PRICE_ID
  )
    throw new Error('Missing some env variables')

  const {
    starterChatsPriceId,
    proChatsPriceId,
    secretKey,
    starterPriceId,
    proPriceId,
    starterYearlyPriceId,
    proYearlyPriceId,
  } = {
    starterChatsPriceId: process.env.STRIPE_STARTER_CHATS_PRICE_ID,
    proChatsPriceId: process.env.STRIPE_PRO_CHATS_PRICE_ID,
    secretKey: process.env.STRIPE_SECRET_KEY,
    starterPriceId: process.env.STRIPE_STARTER_PRICE_ID,
    proPriceId: process.env.STRIPE_PRO_PRICE_ID,
    starterYearlyPriceId: process.env.STRIPE_STARTER_YEARLY_PRICE_ID,
    proYearlyPriceId: process.env.STRIPE_PRO_YEARLY_PRICE_ID,
  }

  const workspacesWithPaidPlan = await prisma.workspace.findMany({
    where: {
      plan: {
        in: ['PRO', 'STARTER'],
      },
      isSuspended: false,
    },
    select: {
      plan: true,
      name: true,
      id: true,
      stripeId: true,
      isQuarantined: true,
      members: {
        select: {
          user: {
            select: { email: true },
          },
        },
      },
    },
  })

  writeFileSync(
    './workspacesWithPaidPlan.json',
    JSON.stringify(workspacesWithPaidPlan, null, 2)
  )

  const stripe = new Stripe(secretKey, {
    apiVersion: '2022-11-15',
  })

  const todayMidnight = new Date()
  todayMidnight.setUTCHours(0, 0, 0, 0)

  const failedWorkspaces = []
  const workspacesWithoutSubscription = []
  const workspacesWithoutStripeId = []

  let i = 0
  for (const workspace of workspacesWithPaidPlan) {
    i += 1
    console.log(
      `(${i} / ${workspacesWithPaidPlan.length})`,
      'Migrating workspace:',
      workspace.id,
      workspace.name,
      workspace.stripeId,
      JSON.stringify(workspace.members.map((member) => member.user.email))
    )
    if (!workspace.stripeId) {
      console.log('No stripe ID, skipping...')
      workspacesWithoutStripeId.push(workspace)
      writeFileSync(
        './workspacesWithoutStripeId.json',
        JSON.stringify(workspacesWithoutStripeId, null, 2)
      )
      continue
    }

    const subscriptions = await stripe.subscriptions.list({
      customer: workspace.stripeId,
    })

    const currentSubscription = subscriptions.data
      .filter((sub) => ['past_due', 'active'].includes(sub.status))
      .sort((a, b) => a.created - b.created)
      .shift()

    if (!currentSubscription) {
      console.log('No current subscription in workspace:', workspace.id)
      workspacesWithoutSubscription.push(workspace)
      writeFileSync(
        './workspacesWithoutSubscription.json',
        JSON.stringify(workspacesWithoutSubscription)
      )
      continue
    }

    if (
      currentSubscription.items.data.find(
        (item) =>
          item.price.id === starterChatsPriceId ||
          item.price.id === proChatsPriceId
      )
    ) {
      console.log(
        'Already migrated to usage based billing for workspace. Skipping...'
      )
      continue
    }
    if (
      !currentSubscription.items.data.find(
        (item) =>
          item.price.id === starterPriceId ||
          item.price.id === proPriceId ||
          item.price.id === starterYearlyPriceId ||
          item.price.id === proYearlyPriceId
      )
    ) {
      console.log(
        'Could not find STARTER or PRO plan in items for workspace:',
        workspace.id
      )
      failedWorkspaces.push(workspace)
      writeFileSync(
        './failedWorkspaces.json',
        JSON.stringify(failedWorkspaces, null, 2)
      )
      continue
    }

    const newSubscription = await stripe.subscriptions.update(
      currentSubscription.id,
      {
        items: [
          ...currentSubscription.items.data.flatMap<Stripe.SubscriptionUpdateParams.Item>(
            (item) => {
              if (
                item.price.id === starterPriceId ||
                item.price.id === proPriceId
              )
                return {
                  id: item.id,
                  price: item.price.id,
                  quantity: item.quantity,
                }
              if (
                item.price.id === starterYearlyPriceId ||
                item.price.id === proYearlyPriceId
              )
                return [
                  {
                    id: item.id,
                    price: item.price.id,
                    quantity: item.quantity,
                    deleted: true,
                  },
                  {
                    price:
                      workspace.plan === 'STARTER'
                        ? starterPriceId
                        : proPriceId,
                    quantity: 1,
                  },
                ]
              return {
                id: item.id,
                price: item.price.id,
                quantity: item.quantity,
                deleted: true,
              }
            }
          ),
          {
            price:
              workspace.plan === 'STARTER'
                ? starterChatsPriceId
                : proChatsPriceId,
          },
        ],
      }
    )

    const totalResults = await prisma.result.count({
      where: {
        typebot: { workspaceId: workspace.id },
        hasStarted: true,
        createdAt: {
          gte: new Date(newSubscription.current_period_start * 1000),
          lt: todayMidnight,
        },
      },
    })

    if (workspace.plan === 'STARTER' && totalResults >= 4000) {
      console.log(
        'Workspace has more than 4000 chats, automatically upgrading to PRO plan'
      )
      const currentPlanItemId = newSubscription?.items.data.find((item) =>
        [starterPriceId, proPriceId].includes(item.price.id)
      )?.id

      if (!currentPlanItemId)
        throw new Error(`Could not find current plan item ID for workspace`)

      await stripe.subscriptions.update(newSubscription.id, {
        items: [
          {
            id: currentPlanItemId,
            price: proPriceId,
            quantity: 1,
          },
          {
            id: newSubscription.items.data.find(
              (item) =>
                item.price.id === starterChatsPriceId ||
                item.price.id === proChatsPriceId
            )?.id,
            price: proChatsPriceId,
          },
        ],
      })

      await prisma.workspace.update({
        where: { id: workspace.id },
        data: {
          plan: 'PRO',
        },
      })
    }

    const subscriptionItem = newSubscription.items.data.find(
      (item) =>
        item.price.id === starterChatsPriceId ||
        item.price.id === proChatsPriceId
    )

    if (!subscriptionItem)
      throw new Error(
        `Could not find subscription item for workspace ${workspace.id}`
      )

    const idempotencyKey = createId()

    console.log('Reporting total results:', totalResults)
    await stripe.subscriptionItems.createUsageRecord(
      subscriptionItem.id,
      {
        quantity: totalResults,
        timestamp: 'now',
      },
      {
        idempotencyKey,
      }
    )

    if (workspace.isQuarantined) {
      await prisma.workspace.update({
        where: { id: workspace.id },
        data: {
          isQuarantined: false,
        },
      })
    }
  }
}

migrateSubscriptionsToUsageBased()
