import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { outlookFoldersSelectorContract } from '@/lib/api/contracts/selectors/microsoft'
import { parseRequest } from '@/lib/api/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'

export const dynamic = 'force-dynamic'

const logger = createLogger('OutlookFoldersAPI')

/**
 * Microsoft Graph paginates `mailFolders` via the `@odata.nextLink` absolute
 * URL in the response body (default page size is ~10). Bound the drain so a
 * pathological account can't loop unbounded; `$top` is capped at 999 by Graph.
 * See https://learn.microsoft.com/en-us/graph/paging
 */
const OUTLOOK_FOLDERS_PAGE_SIZE = 999
const MAX_OUTLOOK_FOLDERS_PAGES = 20

interface OutlookFolder {
  id: string
  displayName: string
  totalItemCount?: number
  unreadItemCount?: number
}

export const GET = withRouteHandler(async (request: NextRequest) => {
  try {
    const parsed = await parseRequest(outlookFoldersSelectorContract, request, {})
    if (!parsed.success) return parsed.response
    const { credentialId } = parsed.data.query

    const credentialIdValidation = validateAlphanumericId(credentialId, 'credentialId')
    if (!credentialIdValidation.isValid) {
      logger.warn('Invalid credentialId format', { error: credentialIdValidation.error })
      return NextResponse.json({ error: credentialIdValidation.error }, { status: 400 })
    }

    try {
      const credAccess = await authorizeCredentialUse(request, {
        credentialId,
        requireWorkflowIdForInternal: false,
      })
      if (!credAccess.ok || !credAccess.credentialOwnerUserId) {
        logger.warn('Credential access denied', { error: credAccess.error })
        return NextResponse.json(
          { error: credAccess.error || 'Authentication required' },
          { status: 401 }
        )
      }

      const accessToken = await refreshAccessTokenIfNeeded(
        credentialId,
        credAccess.credentialOwnerUserId,
        generateRequestId()
      )

      if (!accessToken) {
        logger.error('Failed to get access token', {
          credentialId,
          userId: credAccess.credentialOwnerUserId,
        })
        return NextResponse.json(
          {
            error: 'Could not retrieve access token',
            authRequired: true,
          },
          { status: 401 }
        )
      }

      const folders: OutlookFolder[] = []
      let nextUrl: string | undefined =
        `https://graph.microsoft.com/v1.0/me/mailFolders?$top=${OUTLOOK_FOLDERS_PAGE_SIZE}`

      for (let page = 0; page < MAX_OUTLOOK_FOLDERS_PAGES && nextUrl; page++) {
        const response = await fetch(nextUrl, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${accessToken}`,
            'Content-Type': 'application/json',
          },
        })

        if (!response.ok) {
          const errorData = await response.json()
          logger.error('Microsoft Graph API error getting folders', {
            status: response.status,
            error: errorData,
            endpoint: nextUrl,
          })

          if (response.status === 401) {
            return NextResponse.json(
              {
                error: 'Authentication failed. Please reconnect your Outlook account.',
                authRequired: true,
              },
              { status: 401 }
            )
          }

          throw new Error(`Microsoft Graph API error: ${JSON.stringify(errorData)}`)
        }

        const data = await response.json()
        folders.push(...((data.value as OutlookFolder[]) || []))

        const nextLink = getGraphNextPageUrl(data)
        nextUrl = nextLink ? assertGraphNextPageUrl(nextLink) : undefined

        if (nextUrl && page === MAX_OUTLOOK_FOLDERS_PAGES - 1) {
          logger.warn('Outlook mailFolders hit pagination cap; folder list may be incomplete', {
            pages: MAX_OUTLOOK_FOLDERS_PAGES,
            collected: folders.length,
          })
        }
      }

      const transformedFolders = folders.map((folder: OutlookFolder) => ({
        id: folder.id,
        name: folder.displayName,
        type: 'folder',
        messagesTotal: folder.totalItemCount || 0,
        messagesUnread: folder.unreadItemCount || 0,
      }))

      return NextResponse.json({
        folders: transformedFolders,
      })
    } catch (innerError) {
      logger.error('Error during API requests:', innerError)

      const errorMessage = toError(innerError).message
      if (
        errorMessage.includes('auth') ||
        errorMessage.includes('token') ||
        errorMessage.includes('unauthorized') ||
        errorMessage.includes('unauthenticated')
      ) {
        return NextResponse.json(
          {
            error: 'Authentication failed. Please reconnect your Outlook account.',
            authRequired: true,
            details: errorMessage,
          },
          { status: 401 }
        )
      }

      throw innerError
    }
  } catch (error) {
    logger.error('Error processing Outlook folders request:', error)
    return NextResponse.json(
      {
        error: 'Failed to retrieve Outlook folders',
        details: (error as Error).message,
      },
      { status: 500 }
    )
  }
})
