import { createR2Storage, type R2StorageBucket } from '@platform-modules/uploads/storage-r2';
import type { StorageAdapter } from '@platform-modules/uploads/storage';

export interface MediaEnv {
  MEDIA_BUCKET: R2StorageBucket;
  MEDIA_PUBLIC_BASE_URL: string;
  MEDIA_MAX_BYTES?: string;
  MEDIA_QUOTA_BYTES?: string;
}

export class MissingMediaBucketError extends Error {
  readonly name = 'MissingMediaBucketError';
  constructor() {
    super('MEDIA_BUCKET R2 binding is required but was not present in the Cloudflare runtime environment');
  }
}

export class InvalidMediaBaseUrlError extends Error {
  readonly name = 'InvalidMediaBaseUrlError';
  constructor(value: string) {
    super(
      `MEDIA_PUBLIC_BASE_URL must be an https:// URL (the content sanitizer allows only https image src); got: ${value}`,
    );
  }
}

/** Validated public display origin. Trailing slash stripped. https-only — see seam spec §4.2/§5. */
export function publicBaseUrl(env: MediaEnv): string {
  const raw = env.MEDIA_PUBLIC_BASE_URL;
  let parsed: URL;
  try {
    parsed = new URL(raw);
  } catch {
    throw new InvalidMediaBaseUrlError(String(raw));
  }
  if (parsed.protocol !== 'https:' || !parsed.hostname) throw new InvalidMediaBaseUrlError(String(raw));
  return raw.replace(/\/$/, '');
}

/** One wiring file (adapter-minimalism). Public-bucket default → no presign creds. */
export function getStorage(env: MediaEnv): StorageAdapter {
  if (!env.MEDIA_BUCKET) throw new MissingMediaBucketError();
  return createR2Storage({ bucket: env.MEDIA_BUCKET });
}

/** Image size ceiling — MUST stay well under the CF Workers ~128 MB memory limit (route buffers the file). Default 10 MB. */
export function maxBytes(env: MediaEnv): number {
  const n = Number(env.MEDIA_MAX_BYTES);
  return Number.isFinite(n) && n > 0 ? n : 10 * 1024 * 1024;
}

/** Per-site media quota. Default 1 GB. */
export function quotaBytes(env: MediaEnv): number {
  const n = Number(env.MEDIA_QUOTA_BYTES);
  return Number.isFinite(n) && n > 0 ? n : 1024 * 1024 * 1024;
}
