import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { v1ListFilesContract, v1UploadFileFormFieldsSchema } from '@/lib/api/contracts/v1/files'
import { getValidationErrorMessage, parseRequest } from '@/lib/api/server'
import { generateRequestId } from '@/lib/core/utils/request'
import {
  isPayloadSizeLimitError,
  MAX_MULTIPART_OVERHEAD_BYTES,
  readFileToBufferWithLimit,
  readFormDataWithLimit,
} from '@/lib/core/utils/stream-limits'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import {
  FileConflictError,
  getWorkspaceFile,
  listWorkspaceFiles,
  uploadWorkspaceFile,
} from '@/lib/uploads/contexts/workspace'
import {
  checkRateLimit,
  checkWorkspaceScope,
  createRateLimitResponse,
  validateWorkspaceAccess,
} from '@/app/api/v1/middleware'

const logger = createLogger('V1FilesAPI')

export const dynamic = 'force-dynamic'
export const revalidate = 0

const MAX_FILE_SIZE = 100 * 1024 * 1024

/** GET /api/v1/files — List all files in a workspace. */
export const GET = withRouteHandler(async (request: NextRequest) => {
  const requestId = generateRequestId()

  try {
    const rateLimit = await checkRateLimit(request, 'files')
    if (!rateLimit.allowed) {
      return createRateLimitResponse(rateLimit)
    }

    const userId = rateLimit.userId!
    const parsed = await parseRequest(v1ListFilesContract, request, {})
    if (!parsed.success) return parsed.response

    const { workspaceId } = parsed.data.query

    const accessError = await validateWorkspaceAccess(rateLimit, userId, workspaceId)
    if (accessError) return accessError

    const files = await listWorkspaceFiles(workspaceId)

    return NextResponse.json({
      success: true,
      data: {
        files: files.map((f) => ({
          id: f.id,
          name: f.name,
          size: f.size,
          type: f.type,
          key: f.key,
          uploadedBy: f.uploadedBy,
          uploadedAt:
            f.uploadedAt instanceof Date ? f.uploadedAt.toISOString() : String(f.uploadedAt),
        })),
        totalCount: files.length,
      },
    })
  } catch (error) {
    logger.error(`[${requestId}] Error listing files:`, error)
    return NextResponse.json({ error: 'Failed to list files' }, { status: 500 })
  }
})

/** POST /api/v1/files — Upload a file to a workspace. */
export const POST = withRouteHandler(async (request: NextRequest) => {
  const requestId = generateRequestId()

  try {
    const rateLimit = await checkRateLimit(request, 'files')
    if (!rateLimit.allowed) {
      return createRateLimitResponse(rateLimit)
    }

    const userId = rateLimit.userId!

    let formData: FormData
    try {
      formData = await readFormDataWithLimit(request, {
        maxBytes: MAX_FILE_SIZE + MAX_MULTIPART_OVERHEAD_BYTES,
        label: 'workspace file upload body',
      })
    } catch (error) {
      if (isPayloadSizeLimitError(error)) {
        return NextResponse.json({ error: error.message }, { status: 413 })
      }
      return NextResponse.json(
        { error: 'Request body must be valid multipart form data' },
        { status: 400 }
      )
    }
    const rawFile = formData.get('file')
    const file = rawFile instanceof File ? rawFile : null
    const rawWorkspaceId = formData.get('workspaceId')
    const formFieldsResult = v1UploadFileFormFieldsSchema.safeParse({
      workspaceId: typeof rawWorkspaceId === 'string' ? rawWorkspaceId : '',
    })

    if (!formFieldsResult.success) {
      return NextResponse.json(
        { error: getValidationErrorMessage(formFieldsResult.error, 'Invalid form data') },
        { status: 400 }
      )
    }
    const { workspaceId } = formFieldsResult.data

    const scopeError = await checkWorkspaceScope(rateLimit, workspaceId)
    if (scopeError) return scopeError

    if (!file) {
      return NextResponse.json({ error: 'file form field is required' }, { status: 400 })
    }

    if (file.size > MAX_FILE_SIZE) {
      return NextResponse.json(
        {
          error: `File size exceeds 100MB limit (${(file.size / (1024 * 1024)).toFixed(2)}MB)`,
        },
        { status: 413 }
      )
    }

    const accessError = await validateWorkspaceAccess(rateLimit, userId, workspaceId, 'write')
    if (accessError) return accessError

    const buffer = await readFileToBufferWithLimit(file, {
      maxBytes: MAX_FILE_SIZE,
      label: 'workspace upload file',
    })

    const userFile = await uploadWorkspaceFile(
      workspaceId,
      userId,
      buffer,
      file.name,
      file.type || 'application/octet-stream'
    )

    logger.info(`[${requestId}] Uploaded file: ${file.name} to workspace ${workspaceId}`)

    recordAudit({
      workspaceId,
      actorId: userId,
      action: AuditAction.FILE_UPLOADED,
      resourceType: AuditResourceType.FILE,
      resourceId: userFile.id,
      resourceName: file.name,
      description: `Uploaded file "${file.name}" via API`,
      metadata: { fileSize: file.size, fileType: file.type || 'application/octet-stream' },
      request,
    })

    const fileRecord = await getWorkspaceFile(workspaceId, userFile.id)
    const uploadedAt =
      fileRecord?.uploadedAt instanceof Date
        ? fileRecord.uploadedAt.toISOString()
        : fileRecord?.uploadedAt
          ? String(fileRecord.uploadedAt)
          : new Date().toISOString()

    return NextResponse.json({
      success: true,
      data: {
        file: {
          id: userFile.id,
          name: userFile.name,
          size: userFile.size,
          type: userFile.type,
          key: userFile.key,
          uploadedBy: userId,
          uploadedAt,
        },
        message: 'File uploaded successfully',
      },
    })
  } catch (error) {
    if (isPayloadSizeLimitError(error)) {
      return NextResponse.json({ error: error.message }, { status: 413 })
    }

    const errorMessage = getErrorMessage(error, 'Failed to upload file')
    const isDuplicate =
      error instanceof FileConflictError || errorMessage.includes('already exists')

    if (isDuplicate) {
      return NextResponse.json({ error: errorMessage }, { status: 409 })
    }

    logger.error(`[${requestId}] Error uploading file:`, error)
    return NextResponse.json({ error: 'Failed to upload file' }, { status: 500 })
  }
})
