import * as Minio from 'minio'

// MinIO client singleton (lazy-initialized to avoid build-time errors)
const globalForMinio = globalThis as unknown as {
  minio: Minio.Client | undefined
}

// Internal endpoint for server-to-server communication
const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || 'http://localhost:9000'

// Public endpoint for browser-accessible URLs (pre-signed URLs)
// If not set, falls back to internal endpoint
export const MINIO_PUBLIC_ENDPOINT = process.env.MINIO_PUBLIC_ENDPOINT || MINIO_ENDPOINT

function createMinioClient(): Minio.Client {
  const url = new URL(MINIO_ENDPOINT)

  const accessKey = process.env.MINIO_ACCESS_KEY
  const secretKey = process.env.MINIO_SECRET_KEY
  if (process.env.NODE_ENV === 'production' && (!accessKey || !secretKey)) {
    throw new Error('MINIO_ACCESS_KEY and MINIO_SECRET_KEY environment variables are required in production')
  }

  return new Minio.Client({
    endPoint: url.hostname,
    port: url.port ? parseInt(url.port) : (url.protocol === 'https:' ? 443 : 80),
    useSSL: url.protocol === 'https:',
    accessKey: accessKey || 'minioadmin',
    secretKey: secretKey || 'minioadmin',
  })
}

/**
 * Get the MinIO client instance (lazy-initialized).
 * The client is only created on first access, not at module import time.
 * This prevents build-time errors when env vars are not available.
 */
export function getMinioClient(): Minio.Client {
  if (!globalForMinio.minio) {
    globalForMinio.minio = createMinioClient()
  }
  return globalForMinio.minio
}

// Backward-compatible export — lazy getter via Proxy
export const minio: Minio.Client = new Proxy({} as Minio.Client, {
  get(_target, prop, receiver) {
    return Reflect.get(getMinioClient(), prop, receiver)
  },
})

// Default bucket name
export const BUCKET_NAME = process.env.MINIO_BUCKET || 'mopc-files'

/**
 * Replace internal endpoint with public endpoint in a URL
 */
function replaceEndpoint(url: string): string {
  if (MINIO_PUBLIC_ENDPOINT === MINIO_ENDPOINT) {
    return url
  }

  try {
    const internalUrl = new URL(MINIO_ENDPOINT)
    const publicUrl = new URL(MINIO_PUBLIC_ENDPOINT)
    return url.replace(
      `${internalUrl.protocol}//${internalUrl.host}`,
      `${publicUrl.protocol}//${publicUrl.host}`
    )
  } catch {
    return url
  }
}

// =============================================================================
// Helper Functions
// =============================================================================

/**
 * Generate a pre-signed URL for file download or upload
 * Uses MINIO_PUBLIC_ENDPOINT for browser-accessible URLs
 */
export async function getPresignedUrl(
  bucket: string,
  objectKey: string,
  method: 'GET' | 'PUT' = 'GET',
  expirySeconds: number = 900 // 15 minutes default
): Promise<string> {
  let url: string
  if (method === 'GET') {
    url = await minio.presignedGetObject(bucket, objectKey, expirySeconds)
  } else {
    url = await minio.presignedPutObject(bucket, objectKey, expirySeconds)
  }

  // Replace internal endpoint with public endpoint for browser access
  return replaceEndpoint(url)
}

/**
 * Check if a bucket exists, create if not
 */
export async function ensureBucket(bucket: string): Promise<void> {
  const exists = await minio.bucketExists(bucket)
  if (!exists) {
    await minio.makeBucket(bucket)
    console.log(`Created MinIO bucket: ${bucket}`)
  }
}

/**
 * Delete an object from MinIO
 */
export async function deleteObject(
  bucket: string,
  objectKey: string
): Promise<void> {
  await minio.removeObject(bucket, objectKey)
}

/**
 * Sanitize a name for use as a MinIO path segment.
 * Removes special characters, replaces spaces with underscores, limits length.
 */
function sanitizePath(name: string): string {
  return (
    name
      .trim()
      .replace(/[^a-zA-Z0-9\-_ ]/g, '')
      .replace(/\s+/g, '_')
      .substring(0, 100) || 'unnamed'
  )
}

/**
 * Generate a unique object key for a project file.
 *
 * Structure: {ProjectName}/{RoundName}/{timestamp}-{fileName}
 * - projectName: human-readable project title (sanitized)
 * - roundName: round name for submission context (sanitized), defaults to "general"
 * - fileName: original file name (sanitized)
 *
 * Existing files with old-style keys (projects/{id}/...) are unaffected
 * because retrieval uses the objectKey stored in the database.
 */
export function generateObjectKey(
  projectName: string,
  fileName: string,
  roundName?: string
): string {
  const timestamp = Date.now()
  const sanitizedProject = sanitizePath(projectName)
  const sanitizedRound = roundName ? sanitizePath(roundName) : 'general'
  const sanitizedFile = fileName.replace(/[^a-zA-Z0-9.-]/g, '_')
  return `${sanitizedProject}/${sanitizedRound}/${timestamp}-${sanitizedFile}`
}

