/**
 * Clipboard helpers with HTTP fallback.
 *
 * navigator.clipboard requires a secure context (HTTPS or localhost).
 * Production sites served over plain HTTP must use the execCommand fallback for copy.
 */

export class ClipboardReadUnavailableError extends Error {
  constructor(message = "Clipboard read is unavailable outside a secure context (HTTPS).") {
    super(message);
    this.name = "ClipboardReadUnavailableError";
  }
}

export class ClipboardWriteUnavailableError extends Error {
  constructor(message = "Could not copy to clipboard.") {
    super(message);
    this.name = "ClipboardWriteUnavailableError";
  }
}

function canUseAsyncClipboardWrite(): boolean {
  return typeof navigator !== "undefined" && !!navigator.clipboard?.writeText && window.isSecureContext;
}

function canUseAsyncClipboardRead(): boolean {
  return typeof navigator !== "undefined" && !!navigator.clipboard?.readText && window.isSecureContext;
}

/** execCommand copy — works on HTTP when triggered by a user click. */
function copyTextViaExecCommand(text: string): boolean {
  try {
    const el = document.createElement("textarea");
    el.value = text;
    el.setAttribute("readonly", "");
    el.style.position = "fixed";
    el.style.top = "0";
    el.style.left = "0";
    el.style.width = "2em";
    el.style.height = "2em";
    el.style.padding = "0";
    el.style.border = "none";
    el.style.outline = "none";
    el.style.boxShadow = "none";
    el.style.background = "transparent";
    el.style.opacity = "0";
    document.body.appendChild(el);
    el.focus();
    el.select();
    el.setSelectionRange(0, text.length);
    const ok = document.execCommand("copy");
    document.body.removeChild(el);
    return ok;
  } catch {
    return false;
  }
}

export async function copyTextToClipboard(text: string): Promise<void> {
  if (canUseAsyncClipboardWrite()) {
    await navigator.clipboard.writeText(text);
    return;
  }

  if (copyTextViaExecCommand(text)) return;

  throw new ClipboardWriteUnavailableError();
}

export async function readTextFromClipboard(): Promise<string> {
  if (canUseAsyncClipboardRead()) {
    try {
      return await navigator.clipboard.readText();
    } catch (e) {
      if (e instanceof DOMException && e.name === "NotAllowedError") {
        throw new ClipboardReadUnavailableError(
          "Clipboard read permission denied by the browser. Use Paste JSON and paste manually."
        );
      }
      throw e;
    }
  }

  throw new ClipboardReadUnavailableError();
}

export function isClipboardReadAvailable(): boolean {
  return canUseAsyncClipboardRead();
}
