/**
 * MinIO / S3-compatible object storage client.
 *
 * Env vars (set in .env.local):
 *   MINIO_ENDPOINT        — host:port, e.g. s3.crispygoat.com (no https://)
 *   MINIO_REGION          — e.g. us-east-1 (default)
 *   MINIO_ACCESS_KEY      — MinIO access key
 *   MINIO_SECRET_KEY      — MinIO secret key
 *   MINIO_USE_SSL         — "true" to use HTTPS (default: true if endpoint ends in .com/.io/etc.)
 *   MINIO_BUCKET_PRODUCTS     — bucket for product images
 *   MINIO_BUCKET_BRAND_LOGOS  — bucket for brand logos
 *   MINIO_BUCKET_WATER_LOGS   — bucket for water log photos
 *
 * Public URL base — used to construct public-facing URLs after upload:
 *   MINIO_PUBLIC_URL       — e.g. https://s3.crispygoat.com (default derived from endpoint)
 */

import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

// ── Client singleton ─────────────────────────────────────────────────────────

let _client: S3Client | null = null;

export function getStorageClient(): S3Client {
  if (_client) return _client;

  const endpoint = process.env.MINIO_ENDPOINT ?? "s3.crispygoat.com";
  const ssl = process.env.MINIO_USE_SSL !== "false";
  const region = process.env.MINIO_REGION ?? "us-east-1";

  _client = new S3Client({
    endpoint: `${ssl ? "https" : "http"}://${endpoint}`,
    region,
    credentials: {
      accessKeyId: process.env.MINIO_ACCESS_KEY ?? "",
      secretAccessKey: process.env.MINIO_SECRET_KEY ?? "",
    },
    forcePathStyle: true, // Required for MinIO — bucket is not part of the host
  });

  return _client;
}

// ── Public URL helper ─────────────────────────────────────────────────────────

/**
 * Returns the public base URL for the given bucket.
 * Override with MINIO_PUBLIC_URL if your public endpoint differs from the endpoint.
 */
export function getPublicUrl(bucket: string, key: string): string {
  const base = process.env.MINIO_PUBLIC_URL ?? `https://${process.env.MINIO_ENDPOINT ?? "s3.crispygoat.com"}`;
  return `${base}/${bucket}/${key}`;
}

// ── Upload helpers ───────────────────────────────────────────────────────────

export type UploadOptions = {
  bucket: string;
  key: string;
  body: Buffer | Uint8Array;
  contentType: string;
  /**
   * Cache-Control value. Defaults to "public, max-age=31536000, immutable"
   * for immutable uploaded assets.
   */
  cacheControl?: string;
};

export async function uploadObject(opts: UploadOptions): Promise<string> {
  const client = getStorageClient();
  await client.send(
    new PutObjectCommand({
      Bucket: opts.bucket,
      Key: opts.key,
      Body: opts.body,
      ContentType: opts.contentType,
      CacheControl: opts.cacheControl ?? "public, max-age=31536000, immutable",
    })
  );
  return getPublicUrl(opts.bucket, opts.key);
}

export async function deleteObject(bucket: string, key: string): Promise<void> {
  const client = getStorageClient();
  await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
}

// ── Presigned URL helpers ─────────────────────────────────────────────────────

/**
 * Generate a short-lived presigned PUT URL for direct browser → MinIO upload.
 * Useful when you want the browser to upload bytes directly without routing
 * them through the Next.js server.
 *
 * @param bucket  Target bucket name
 * @param key     Desired object key
 * @param contentType  Expected MIME type
 * @param expiresSeconds  URL expiry (default 5 minutes)
 */
export async function getPresignedPutUrl(
  bucket: string,
  key: string,
  contentType: string,
  expiresSeconds = 300
): Promise<string> {
  const client = getStorageClient();
  return getSignedUrl(
    client,
    new PutObjectCommand({ Bucket: bucket, Key: key, ContentType: contentType }),
    { expiresIn: expiresSeconds }
  );
}

/**
 * Generate a short-lived presigned GET URL for private objects.
 * @param bucket  Bucket name
 * @param key     Object key
 * @param expiresSeconds  URL expiry (default 1 hour)
 */
export async function getPresignedGetUrl(
  bucket: string,
  key: string,
  expiresSeconds = 3600
): Promise<string> {
  const client = getStorageClient();
  return getSignedUrl(
    client,
    new GetObjectCommand({ Bucket: bucket, Key: key }),
    { expiresIn: expiresSeconds }
  );
}

// ── Bucket constants ─────────────────────────────────────────────────────────

export const BUCKETS = {
  PRODUCTS: process.env.MINIO_BUCKET_PRODUCTS ?? "route-products",
  BRAND_LOGOS: process.env.MINIO_BUCKET_BRAND_LOGOS ?? "route-brand-logos",
  WATER_LOGS: process.env.MINIO_BUCKET_WATER_LOGS ?? "route-water-logs",
} as const;
