import { ToolingError } from '../../../../../../types'
import type { ExecutionContext } from '../../../../../../../base/types'

/**
 * Dropbox credentials for API calls
 */
export interface DropboxCredentials {
  accessToken: string
}

/**
 * Parameters for uploading a file
 */
export interface UploadFileParams {
  path: string
  contents: Buffer
  mode?: 'add' | 'overwrite'
  autorename?: boolean
}

/**
 * Result of file upload
 */
export interface UploadFileResult {
  id: string
  name: string
  path_lower: string
  path_display: string
  rev: string
  size: number
  is_downloadable: boolean
  content_hash?: string
}

/**
 * Upload a file to Dropbox
 *
 * Uses the content upload endpoint with Dropbox-API-Arg header.
 *
 * @param credentials - Dropbox access token
 * @param params - Upload parameters
 * @param context - Execution context for logging
 * @returns File metadata
 *
 * @see https://www.dropbox.com/developers/documentation/http/documentation#files-upload
 */
export async function uploadFile(
  credentials: DropboxCredentials,
  params: UploadFileParams,
  context?: ExecutionContext
): Promise<UploadFileResult> {
  const dropboxApiArg = JSON.stringify({
    path: params.path,
    mode: params.mode || 'overwrite',
    autorename: params.autorename ?? false,
    mute: false,
    strict_conflict: false
  })

  context?.logger?.debug(`Uploading file to Dropbox: ${params.path}`)

  const response = await fetch('https://content.dropboxapi.com/2/files/upload', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${credentials.accessToken}`,
      'Content-Type': 'application/octet-stream',
      'Dropbox-API-Arg': dropboxApiArg
    },
    // Node.js fetch accepts Buffer as body, cast for TypeScript compatibility
    body: params.contents as unknown as BodyInit
  })

  if (!response.ok) {
    await handleDropboxError(response, context)
  }

  const result = (await response.json()) as UploadFileResult

  context?.logger?.info(`File uploaded to Dropbox: ${result.path_display} (${result.size} bytes)`)

  return result
}

/**
 * Handle Dropbox API errors
 */
async function handleDropboxError(response: Response, context?: ExecutionContext): Promise<never> {
  let errorMessage = `Dropbox API error: ${response.status}`
  let errorDetails: Record<string, unknown> = { status: response.status }

  try {
    const errorData = (await response.json()) as { error_summary?: string; error?: Record<string, unknown> }
    if (errorData.error_summary) {
      errorMessage = `Dropbox API error: ${errorData.error_summary}`
      errorDetails = { ...errorDetails, ...errorData }
    }
  } catch {
    errorMessage += ` - ${response.statusText}`
  }

  context?.logger?.error(errorMessage)

  // Map HTTP status to ToolingError codes
  if (response.status === 401) {
    throw new ToolingError('credentials_invalid', 'Dropbox access token expired or invalid', errorDetails)
  }
  if (response.status === 429) {
    throw new ToolingError('rate_limit_exceeded', errorMessage, errorDetails)
  }
  throw new ToolingError('api_error', errorMessage, errorDetails)
}
