import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { sftpUploadContract } from '@/lib/api/contracts/storage-transfer'
import { parseRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
import { downloadServableFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import { docNotReadyResponse } from '@/lib/uploads/utils/servable-file-response'
import { assertToolFileAccess } from '@/app/api/files/authorization'
import {
  createSftpConnection,
  getSftp,
  isPathSafe,
  sanitizeFileName,
  sanitizePath,
  sftpExists,
} from '@/app/api/tools/sftp/utils'

export const dynamic = 'force-dynamic'

const logger = createLogger('SftpUploadAPI')

export const POST = withRouteHandler(async (request: NextRequest) => {
  const requestId = generateRequestId()

  try {
    const authResult = await checkInternalAuth(request, { requireWorkflowId: false })

    if (!authResult.success || !authResult.userId) {
      logger.warn(`[${requestId}] Unauthorized SFTP upload attempt: ${authResult.error}`)
      return NextResponse.json(
        { success: false, error: authResult.error || 'Authentication required' },
        { status: 401 }
      )
    }

    logger.info(`[${requestId}] Authenticated SFTP upload request via ${authResult.authType}`, {
      userId: authResult.userId,
    })

    const parsed = await parseRequest(sftpUploadContract, request, {})
    if (!parsed.success) return parsed.response
    const params = parsed.data.body

    const hasFiles = params.files && params.files.length > 0
    const hasDirectContent = params.fileContent && params.fileName

    if (!hasFiles && !hasDirectContent) {
      return NextResponse.json(
        { error: 'Either files or fileContent with fileName must be provided' },
        { status: 400 }
      )
    }

    if (!isPathSafe(params.remotePath)) {
      logger.warn(`[${requestId}] Path traversal attempt detected in remotePath`)
      return NextResponse.json(
        { error: 'Invalid remote path: path traversal sequences are not allowed' },
        { status: 400 }
      )
    }

    logger.info(`[${requestId}] Connecting to SFTP server ${params.host}:${params.port}`)

    const client = await createSftpConnection({
      host: params.host,
      port: params.port,
      username: params.username,
      password: params.password,
      privateKey: params.privateKey,
      passphrase: params.passphrase,
    })

    try {
      const sftp = await getSftp(client)
      const remotePath = sanitizePath(params.remotePath)
      const uploadedFiles: Array<{ name: string; remotePath: string; size: number }> = []

      if (hasFiles) {
        const rawFiles = params.files!
        logger.info(`[${requestId}] Processing ${rawFiles.length} file(s) for upload`)

        const userFiles = processFilesToUserFiles(rawFiles, requestId, logger)

        const totalSize = userFiles.reduce((sum, file) => sum + file.size, 0)
        const maxSize = 100 * 1024 * 1024

        if (totalSize > maxSize) {
          const sizeMB = (totalSize / (1024 * 1024)).toFixed(2)
          return NextResponse.json(
            { success: false, error: `Total file size (${sizeMB}MB) exceeds limit of 100MB` },
            { status: 400 }
          )
        }

        let resolvedTotal = 0
        for (const file of userFiles) {
          try {
            const denied = await assertToolFileAccess(
              file.key,
              authResult.userId,
              requestId,
              logger
            )
            if (denied) return denied
            logger.info(
              `[${requestId}] Downloading file for upload: ${file.name} (${file.size} bytes)`
            )
            const { buffer } = await downloadServableFileFromStorage(file, requestId, logger)

            resolvedTotal += buffer.length
            if (resolvedTotal > maxSize) {
              const sizeMB = (resolvedTotal / (1024 * 1024)).toFixed(2)
              return NextResponse.json(
                { success: false, error: `Total file size (${sizeMB}MB) exceeds limit of 100MB` },
                { status: 400 }
              )
            }

            const safeFileName = sanitizeFileName(file.name)
            const fullRemotePath = remotePath.endsWith('/')
              ? `${remotePath}${safeFileName}`
              : `${remotePath}/${safeFileName}`

            const sanitizedRemotePath = sanitizePath(fullRemotePath)

            if (!params.overwrite) {
              const exists = await sftpExists(sftp, sanitizedRemotePath)
              if (exists) {
                logger.warn(`[${requestId}] File ${sanitizedRemotePath} already exists, skipping`)
                continue
              }
            }

            await new Promise<void>((resolve, reject) => {
              const writeStream = sftp.createWriteStream(sanitizedRemotePath, {
                mode: params.permissions ? Number.parseInt(params.permissions, 8) : 0o644,
              })

              writeStream.on('error', reject)
              writeStream.on('close', () => resolve())
              writeStream.end(buffer)
            })

            uploadedFiles.push({
              name: safeFileName,
              remotePath: sanitizedRemotePath,
              size: buffer.length,
            })

            logger.info(`[${requestId}] Uploaded ${safeFileName} to ${sanitizedRemotePath}`)
          } catch (error) {
            const notReady = docNotReadyResponse(error)
            if (notReady) return notReady
            logger.error(`[${requestId}] Failed to upload file ${file.name}:`, error)
            throw new Error(
              `Failed to upload file "${file.name}": ${getErrorMessage(error, 'Unknown error')}`
            )
          }
        }
      }

      if (hasDirectContent) {
        const safeFileName = sanitizeFileName(params.fileName!)
        const fullRemotePath = remotePath.endsWith('/')
          ? `${remotePath}${safeFileName}`
          : `${remotePath}/${safeFileName}`

        const sanitizedRemotePath = sanitizePath(fullRemotePath)

        if (!params.overwrite) {
          const exists = await sftpExists(sftp, sanitizedRemotePath)
          if (exists) {
            return NextResponse.json(
              { error: 'File already exists and overwrite is disabled' },
              { status: 409 }
            )
          }
        }

        let content: Buffer
        try {
          content = Buffer.from(params.fileContent!, 'base64')
          const reEncoded = content.toString('base64')
          if (reEncoded !== params.fileContent) {
            content = Buffer.from(params.fileContent!, 'utf-8')
          }
        } catch {
          content = Buffer.from(params.fileContent!, 'utf-8')
        }

        await new Promise<void>((resolve, reject) => {
          const writeStream = sftp.createWriteStream(sanitizedRemotePath, {
            mode: params.permissions ? Number.parseInt(params.permissions, 8) : 0o644,
          })

          writeStream.on('error', reject)
          writeStream.on('close', () => resolve())
          writeStream.end(content)
        })

        uploadedFiles.push({
          name: safeFileName,
          remotePath: sanitizedRemotePath,
          size: content.length,
        })

        logger.info(`[${requestId}] Uploaded direct content to ${sanitizedRemotePath}`)
      }

      logger.info(`[${requestId}] SFTP upload completed: ${uploadedFiles.length} file(s)`)

      return NextResponse.json({
        success: true,
        uploadedFiles,
        message: `Successfully uploaded ${uploadedFiles.length} file(s)`,
      })
    } finally {
      client.end()
    }
  } catch (error) {
    const errorMessage = getErrorMessage(error, 'Unknown error occurred')
    logger.error(`[${requestId}] SFTP upload failed:`, error)

    return NextResponse.json({ error: `SFTP upload failed: ${errorMessage}` }, { status: 500 })
  }
})
