import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { googleDriveFilesSelectorContract } from '@/lib/api/contracts/selectors/google'
import { parseRequest } from '@/lib/api/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
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 { drainGooglePagedList, GooglePageError } from '@/lib/oauth/google-pagination'
import { getScopesForService } from '@/lib/oauth/utils'
import { refreshAccessTokenIfNeeded, ServiceAccountTokenError } from '@/app/api/auth/oauth/utils'
export const dynamic = 'force-dynamic'

const logger = createLogger('GoogleDriveFilesAPI')

const MAX_DRIVE_FILE_PAGES = 20
const DRIVE_FILE_PAGE_SIZE = 100

interface DriveFilesResponse {
  files?: DriveFile[]
  nextPageToken?: string
}

function escapeForDriveQuery(value: string): string {
  return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
}

interface SharedDrive {
  id: string
  name: string
  kind: string
}

interface DriveFile {
  id: string
  name: string
  mimeType: string
  iconLink?: string
  webViewLink?: string
  thumbnailLink?: string
  createdTime?: string
  modifiedTime?: string
  size?: string
  owners?: unknown[]
  parents?: string[]
}

/**
 * Fetches shared drives the user has access to
 */
async function fetchSharedDrives(accessToken: string, requestId: string): Promise<DriveFile[]> {
  try {
    const response = await fetch(
      'https://www.googleapis.com/drive/v3/drives?pageSize=100&fields=drives(id,name)',
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    )

    if (!response.ok) {
      logger.warn(`[${requestId}] Failed to fetch shared drives`, {
        status: response.status,
      })
      return []
    }

    const data = await response.json()
    const drives: SharedDrive[] = data.drives || []

    return drives.map((drive) => ({
      id: drive.id,
      name: drive.name,
      mimeType: 'application/vnd.google-apps.folder',
      iconLink: 'https://ssl.gstatic.com/docs/doclist/images/icon_11_shared_collection_list_1.png',
    }))
  } catch (error) {
    logger.error(`[${requestId}] Error fetching shared drives`, error)
    return []
  }
}

export const GET = withRouteHandler(async (request: NextRequest) => {
  const requestId = generateRequestId()
  logger.info(`[${requestId}] Google Drive files request received`)

  const auth = await checkSessionOrInternalAuth(request)
  if (!auth.success || !auth.userId) {
    return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
  }

  try {
    const parsed = await parseRequest(
      googleDriveFilesSelectorContract,
      request,
      {},
      {
        validationErrorResponse: () => {
          logger.warn(`[${requestId}] Missing credential ID`)
          return NextResponse.json({ error: 'Credential ID is required' }, { status: 400 })
        },
      }
    )
    if (!parsed.success) return parsed.response

    const { credentialId, mimeType } = parsed.data.query
    const query = parsed.data.query.query || ''
    const folderId = parsed.data.query.folderId || parsed.data.query.parentId || ''
    const workflowId = parsed.data.query.workflowId || undefined
    const impersonateEmail = parsed.data.query.impersonateEmail || undefined

    const authz = await authorizeCredentialUse(request, { credentialId, workflowId })
    if (!authz.ok || !authz.credentialOwnerUserId) {
      logger.warn(`[${requestId}] Unauthorized credential access attempt`, authz)
      return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
    }

    const accessToken = await refreshAccessTokenIfNeeded(
      credentialId,
      authz.credentialOwnerUserId,
      requestId,
      getScopesForService('google-drive'),
      impersonateEmail
    )

    if (!accessToken) {
      return NextResponse.json({ error: 'Failed to obtain valid access token' }, { status: 401 })
    }

    if (folderId) {
      const folderIdValidation = validateAlphanumericId(folderId, 'folderId', 50)
      if (!folderIdValidation.isValid) {
        logger.warn(`[${requestId}] Invalid folderId`, { error: folderIdValidation.error })
        return NextResponse.json({ error: folderIdValidation.error }, { status: 400 })
      }
    }

    const qParts: string[] = ['trashed = false']
    if (folderId) {
      qParts.push(`'${escapeForDriveQuery(folderId)}' in parents`)
    }
    if (mimeType) {
      qParts.push(`mimeType = '${escapeForDriveQuery(mimeType)}'`)
    }
    if (query) {
      qParts.push(`name contains '${escapeForDriveQuery(query)}'`)
    }
    const q = qParts.join(' and ')

    let files: DriveFile[]
    try {
      const drained = await drainGooglePagedList<DriveFile, DriveFilesResponse>({
        buildUrl: (pageToken) => {
          const url = new URL('https://www.googleapis.com/drive/v3/files')
          url.searchParams.set('q', q)
          url.searchParams.set('corpora', 'allDrives')
          url.searchParams.set('supportsAllDrives', 'true')
          url.searchParams.set('includeItemsFromAllDrives', 'true')
          url.searchParams.set('pageSize', String(DRIVE_FILE_PAGE_SIZE))
          url.searchParams.set(
            'fields',
            'nextPageToken,files(id,name,mimeType,iconLink,webViewLink,thumbnailLink,createdTime,modifiedTime,size,owners,parents)'
          )
          if (pageToken) url.searchParams.set('pageToken', pageToken)
          return url.toString()
        },
        fetch: (url) =>
          fetch(url, {
            headers: {
              Authorization: `Bearer ${accessToken}`,
            },
          }),
        parseError: (response) =>
          response.json().catch(() => ({ error: { message: 'Unknown error' } })),
        getItems: (body) => body.files,
        getNextPageToken: (body) => body.nextPageToken,
        maxPages: MAX_DRIVE_FILE_PAGES,
        label: 'Google Drive files',
      })
      files = drained.items
    } catch (error) {
      if (error instanceof GooglePageError) {
        const errorBody = error.body as { error?: { message?: string } }
        logger.error(`[${requestId}] Google Drive API error`, {
          status: error.status,
          error: errorBody?.error?.message || 'Failed to fetch files from Google Drive',
        })
        return NextResponse.json(
          {
            error: errorBody?.error?.message || 'Failed to fetch files from Google Drive',
          },
          { status: error.status }
        )
      }
      throw error
    }

    if (mimeType === 'application/vnd.google-apps.spreadsheet') {
      files = files.filter(
        (file: DriveFile) => file.mimeType === 'application/vnd.google-apps.spreadsheet'
      )
    } else if (mimeType === 'application/vnd.google-apps.document') {
      files = files.filter(
        (file: DriveFile) => file.mimeType === 'application/vnd.google-apps.document'
      )
    }

    const isRootFolderListing =
      !folderId && mimeType === 'application/vnd.google-apps.folder' && !query
    if (isRootFolderListing) {
      const sharedDrives = await fetchSharedDrives(accessToken, requestId)
      if (sharedDrives.length > 0) {
        logger.info(`[${requestId}] Found ${sharedDrives.length} shared drives`)
        files = [...sharedDrives, ...files]
      }
    }

    return NextResponse.json({ files }, { status: 200 })
  } catch (error) {
    if (error instanceof ServiceAccountTokenError) {
      logger.warn(`[${requestId}] Service account token error`, { message: error.message })
      return NextResponse.json({ error: error.message }, { status: 400 })
    }
    logger.error(`[${requestId}] Error fetching files from Google Drive`, error)
    return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
  }
})
