import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { microsoftChatsSelectorContract } from '@/lib/api/contracts/selectors/microsoft'
import { parseRequest } from '@/lib/api/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation'
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('TeamsChatsAPI')

/**
 * Largest page size the `me/chats` Microsoft Graph endpoint permits via `$top`.
 */
const CHATS_PAGE_SIZE = 50

/**
 * Upper bound on Microsoft Graph pages drained when listing the user's chats.
 * Paging follows `@odata.nextLink`. The cap prevents an unbounded loop; hitting
 * it is logged as a warning.
 */
const MAX_CHATS_PAGES = 20

interface GraphChat {
  id: string
  topic?: string
}

/**
 * Helper function to get chat members and create a meaningful name
 *
 * @param chatId - Microsoft Teams chat ID to get display name for
 * @param accessToken - Access token for Microsoft Graph API
 * @param chatTopic - Optional existing chat topic
 * @returns A meaningful display name for the chat
 */
const getChatDisplayName = async (
  chatId: string,
  accessToken: string,
  chatTopic?: string
): Promise<string> => {
  try {
    const chatIdValidation = validateMicrosoftGraphId(chatId, 'chatId')
    if (!chatIdValidation.isValid) {
      logger.warn('Invalid chat ID in getChatDisplayName', {
        error: chatIdValidation.error,
        chatId: chatId.substring(0, 50),
      })
      return `Chat ${chatId.substring(0, 8)}...`
    }

    if (chatTopic?.trim() && chatTopic !== 'null') {
      return chatTopic
    }

    const membersResponse = await fetch(
      `https://graph.microsoft.com/v1.0/chats/${encodeURIComponent(chatId)}/members`,
      {
        method: 'GET',
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
        },
      }
    )

    if (membersResponse.ok) {
      const membersData = await membersResponse.json()
      const members = membersData.value || []

      const memberNames = members
        .filter((member: any) => member.displayName && member.displayName !== 'Unknown')
        .map((member: any) => member.displayName)
        .slice(0, 3)

      if (memberNames.length > 0) {
        if (memberNames.length === 1) {
          return memberNames[0]
        }
        if (memberNames.length === 2) {
          return memberNames.join(' & ')
        }
        return `${memberNames.slice(0, 2).join(', ')} & ${memberNames.length - 2} more`
      }
    }

    try {
      const messagesResponse = await fetch(
        `https://graph.microsoft.com/v1.0/chats/${encodeURIComponent(chatId)}/messages?$top=10&$orderby=createdDateTime desc`,
        {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${accessToken}`,
            'Content-Type': 'application/json',
          },
        }
      )

      if (messagesResponse.ok) {
        const messagesData = await messagesResponse.json()
        const messages = messagesData.value || []

        for (const message of messages) {
          if (message.eventDetail?.chatDisplayName) {
            return message.eventDetail.chatDisplayName
          }
        }

        const senderNames = [
          ...new Set(
            messages
              .filter(
                (msg: any) => msg.from?.user?.displayName && msg.from.user.displayName !== 'Unknown'
              )
              .map((msg: any) => msg.from.user.displayName)
          ),
        ].slice(0, 3)

        if (senderNames.length > 0) {
          if (senderNames.length === 1) {
            return senderNames[0] as string
          }
          if (senderNames.length === 2) {
            return senderNames.join(' & ')
          }
          return `${senderNames.slice(0, 2).join(', ')} & ${senderNames.length - 2} more`
        }
      }
    } catch (error) {
      logger.warn(
        `Failed to get better name from messages for chat ${chatId}: ${toError(error).message}`
      )
    }

    return `Chat ${chatId.split(':')[0] || chatId.substring(0, 8)}...`
  } catch (error) {
    logger.warn(`Failed to get display name for chat ${chatId}: ${toError(error).message}`)
    return `Chat ${chatId.split(':')[0] || chatId.substring(0, 8)}...`
  }
}

export const POST = withRouteHandler(async (request: NextRequest) => {
  try {
    const parsed = await parseRequest(microsoftChatsSelectorContract, request, {})
    if (!parsed.success) return parsed.response
    const { credential, workflowId } = parsed.data.body

    try {
      const authz = await authorizeCredentialUse(request, {
        credentialId: credential,
        workflowId,
      })
      if (!authz.ok || !authz.credentialOwnerUserId) {
        return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
      }
      const accessToken = await refreshAccessTokenIfNeeded(
        credential,
        authz.credentialOwnerUserId,
        'TeamsChatsAPI'
      )

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

      const rawChats: GraphChat[] = []
      let nextPageUrl: string | undefined =
        `https://graph.microsoft.com/v1.0/me/chats?$top=${CHATS_PAGE_SIZE}`

      for (let page = 0; page < MAX_CHATS_PAGES; page++) {
        const response = await fetch(nextPageUrl, {
          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 chats', {
            status: response.status,
            error: errorData,
            endpoint: nextPageUrl,
          })

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

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

        const data = await response.json()
        if (Array.isArray(data.value)) {
          rawChats.push(...(data.value as GraphChat[]))
        }

        const rawNextLink = getGraphNextPageUrl(data)
        if (!rawNextLink) {
          nextPageUrl = undefined
          break
        }
        nextPageUrl = assertGraphNextPageUrl(rawNextLink)

        if (page === MAX_CHATS_PAGES - 1) {
          logger.warn('Hit Microsoft Graph chats pagination cap; chat list may be incomplete', {
            maxPages: MAX_CHATS_PAGES,
            collected: rawChats.length,
          })
        }
      }

      const chats = await Promise.all(
        rawChats.map(async (chat) => ({
          id: chat.id,
          displayName: await getChatDisplayName(chat.id, accessToken, chat.topic),
        }))
      )

      return NextResponse.json({
        chats: chats,
      })
    } 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 Microsoft Teams account.',
            authRequired: true,
            details: errorMessage,
          },
          { status: 401 }
        )
      }

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