export const MAX_CAD_BYTES = 50 * 1024 * 1024;
export const CAD_FETCH_TIMEOUT_MS = 15_000;

export function assertCadByteLength(byteLength: number): void {
  if (!Number.isFinite(byteLength) || byteLength < 0) {
    throw new Error('CAD 파일 크기를 확인할 수 없습니다.');
  }
  if (byteLength > MAX_CAD_BYTES) {
    throw new Error('CAD 파일은 50 MiB 이하여야 합니다.');
  }
}

export function resolveCadUrl(input: string, base: URL): URL {
  const url = new URL(input, base);
  if (!['http:', 'https:'].includes(url.protocol)) {
    throw new Error('CAD URL은 HTTP(S)만 허용합니다.');
  }
  if (url.origin !== base.origin) {
    throw new Error('CAD URL은 viewer와 같은 origin만 허용합니다.');
  }
  return url;
}

export async function fetchCadBuffer(url: URL): Promise<ArrayBuffer> {
  const controller = new AbortController();
  const timeout = window.setTimeout(() => controller.abort(), CAD_FETCH_TIMEOUT_MS);

  try {
    const response = await fetch(url, {
      credentials: 'same-origin',
      signal: controller.signal,
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const declaredLength = Number(response.headers.get('content-length'));
    if (Number.isFinite(declaredLength) && declaredLength > 0) {
      assertCadByteLength(declaredLength);
    }

    if (!response.body) {
      const buffer = await response.arrayBuffer();
      assertCadByteLength(buffer.byteLength);
      return buffer;
    }

    const reader = response.body.getReader();
    const chunks: Uint8Array[] = [];
    let total = 0;
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      total += value.byteLength;
      assertCadByteLength(total);
      chunks.push(value);
    }

    const data = new Uint8Array(total);
    let offset = 0;
    for (const chunk of chunks) {
      data.set(chunk, offset);
      offset += chunk.byteLength;
    }
    return data.buffer;
  } catch (error) {
    if (controller.signal.aborted) {
      throw new Error('CAD 파일 요청 시간이 초과되었습니다.');
    }
    throw error;
  } finally {
    window.clearTimeout(timeout);
  }
}
