import 'dotenv/config'
import { Client } from '@botpress/client'
import { z } from '@botpress/sdk'
import axios from 'axios'
import { parseArgs } from 'node:util'

const argsSchema = z.object({
  apiUrl: z.string(),
  workspaceId: z.string(),
  token: z.string(),
  refreshToken: z.string(),
  json: z.boolean(),
  useProvidedToken: z.boolean(),
})
type RefreshSecretArgs = Pick<z.infer<typeof argsSchema>, 'refreshToken' | 'json'> & {
  client: Client
  useProvidedToken: boolean
  log: (message: string) => void
}

const INTEGRATION_SECRET_SANDBOX_ACCESS_TOKEN = 'SANDBOX_ACCESS_TOKEN'
const DEFAULT_API_URL = 'https://api.botpress.cloud'
const INSTAGRAM_GRAPH_API_URL = 'https://graph.instagram.com'

async function refreshSandboxAccessToken(args: RefreshSecretArgs) {
  const { refreshToken, client, useProvidedToken, log } = args

  const { integrations: integrationsList } = await client.listIntegrations({
    name: 'instagram',
  })

  if (integrationsList.length === 0) {
    console.error('Integration not found')
    process.exit(1)
  }

  const integrations = (
    await Promise.all(
      integrationsList.map(async (i) => {
        const { integration } = await client.getIntegration({ id: i.id })
        if (!integration.secrets.some((secret) => secret === INTEGRATION_SECRET_SANDBOX_ACCESS_TOKEN)) {
          return null
        }
        return integration
      })
    )
  ).filter((i) => i !== null)

  if (integrations.length === 0) {
    console.error('No integration with secret', INTEGRATION_SECRET_SANDBOX_ACCESS_TOKEN, 'found')
    process.exit(1)
  }

  let data: { access_token: string }
  if (useProvidedToken) {
    log('Using provided token, skipping refresh on Instagram API')
    data = {
      access_token: refreshToken,
    }
  } else {
    log('Refreshing access token on Instagram API')
    const response = await axios.get(`${INSTAGRAM_GRAPH_API_URL}/refresh_access_token`, {
      params: {
        grant_type: 'ig_refresh_token',
        access_token: refreshToken,
      },
    })
    data = response.data
  }

  for (const integration of integrations) {
    await client.updateIntegration({
      id: integration.id,
      visibility: integration.visibility,
      secrets: {
        [INTEGRATION_SECRET_SANDBOX_ACCESS_TOKEN]: data.access_token,
      },
    })
    log(
      `Updated integration secret ${INTEGRATION_SECRET_SANDBOX_ACCESS_TOKEN} for ${integration.name} v${integration.version} (${integration.id}) with new access token`
    )
  }

  return {
    refreshedToken: data.access_token,
  }
}

async function main() {
  const { values } = parseArgs({
    options: {
      apiUrl: { type: 'string', default: process.env.BP_API_URL || DEFAULT_API_URL },
      workspaceId: { type: 'string', default: process.env.BP_WORKSPACE_ID },
      token: { type: 'string', default: process.env.BP_TOKEN },
      refreshToken: { type: 'string' },
      json: { type: 'boolean', default: false },
      useProvidedToken: { type: 'boolean', default: false },
    },
  })

  try {
    const args = argsSchema.parse(values)
    const { apiUrl, token, workspaceId, refreshToken, json, useProvidedToken } = args
    const messages: string[] = []
    const log = (message: string) => {
      if (!json) {
        console.info(message)
      } else {
        messages.push(message)
      }
    }
    if (apiUrl !== DEFAULT_API_URL) {
      log(`🔗 Using custom API URL: ${apiUrl}`)
    }

    const client = new Client({ apiUrl, token, workspaceId })
    const refreshResult = await refreshSandboxAccessToken({
      refreshToken,
      client,
      json,
      useProvidedToken,
      log,
    })
    const output = json
      ? JSON.stringify({ message: messages.join('\n'), messages, ...refreshResult }, null, 2)
      : `New token: ${refreshResult.refreshedToken}`
    console.info(output)
    process.exit(0)
  } catch (error) {
    console.error('Error:', error)
    process.exit(1)
  }
}

if (require.main === module) {
  main().catch((error) => {
    console.error('Unhandled error:', error)
    process.exit(1)
  })
}
