import type { ExportPayload } from "./types";

const SHARE_SCHEMA = "arbeidspuls-secure-share-v1";

export type SecureShareCreateResponse = {
  export_id: string;
  expires_at: string;
};

export type SecureShareFetchResponse =
  | {
      status: "available";
      export_id: string;
      ciphertext: string;
      iv: string;
      schema: string;
      expires_at: string;
    }
  | {
      status: "deleted" | "expired" | "not_found";
      export_id?: string;
    };

export type SecureShareLink = {
  exportId: string;
  expiresAt: string;
  shareUrl: string;
};

export type ShareRequest = {
  exportId: string | null;
  key: string | null;
  confirmToken: string | null;
};

export function parseShareRequestFromLocation(location: Pick<Location, "search" | "hash">): ShareRequest {
  const search = location.search.startsWith("?") ? location.search.slice(1) : location.search;
  const params = new URLSearchParams(search);
  const positionalId = search && !search.includes("=") ? decodeURIComponent(search) : null;
  const exportId = params.get("id") ?? params.get("export_id") ?? positionalId;
  const hash = location.hash.startsWith("#") ? location.hash.slice(1) : location.hash;
  const hashParams = new URLSearchParams(hash);
  const key = hashParams.get("key");
  const confirmToken = hashParams.get("confirm");
  return { exportId, key, confirmToken };
}

export async function createSecureShare(jsonText: string, origin = window.location.origin): Promise<SecureShareLink> {
  const encrypted = await encryptJsonForShare(jsonText);
  const confirmToken = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));
  const confirmTokenHash = await sha256Base64Url(confirmToken);
  const response = await fetch("/api/share", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      ciphertext: encrypted.ciphertext,
      iv: encrypted.iv,
      schema: SHARE_SCHEMA,
      confirm_token_hash: confirmTokenHash
    })
  });
  if (!response.ok) throw new Error("Kunne ikke opprette sikker deling.");
  const result = await response.json() as SecureShareCreateResponse;
  return {
    exportId: result.export_id,
    expiresAt: result.expires_at,
    shareUrl: `${origin}/s?${encodeURIComponent(result.export_id)}#key=${encrypted.key}&confirm=${confirmToken}`
  };
}

export async function fetchSecureShare(exportId: string): Promise<SecureShareFetchResponse> {
  const response = await fetch(`/api/share/${encodeURIComponent(exportId)}`, { cache: "no-store" });
  if (response.status === 404) return { status: "not_found", export_id: exportId };
  if (!response.ok) throw new Error("Kunne ikke hente delingen.");
  return await response.json() as SecureShareFetchResponse;
}

export async function confirmSecureShareImport(exportId: string, confirmToken: string): Promise<void> {
  const response = await fetch(`/api/share/${encodeURIComponent(exportId)}/confirm-import`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ confirm_token: confirmToken })
  });
  if (!response.ok) throw new Error("Importen ble lagret lokalt, men delingen kunne ikke bekreftes slettet.");
}

export async function decryptSecureSharePayload(payload: { ciphertext: string; iv: string }, keyValue: string): Promise<ExportPayload> {
  const key = await crypto.subtle.importKey("raw", base64UrlDecode(keyValue), { name: "AES-GCM", length: 256 }, false, ["decrypt"]);
  const decrypted = await crypto.subtle.decrypt(
    { name: "AES-GCM", iv: base64UrlDecode(payload.iv) },
    key,
    base64UrlDecode(payload.ciphertext)
  );
  return JSON.parse(new TextDecoder().decode(decrypted)) as ExportPayload;
}

async function encryptJsonForShare(jsonText: string) {
  const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"]);
  const rawKey = await crypto.subtle.exportKey("raw", key);
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const data = new TextEncoder().encode(jsonText);
  const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
  return {
    ciphertext: base64UrlEncode(new Uint8Array(ciphertext)),
    iv: base64UrlEncode(iv),
    key: base64UrlEncode(new Uint8Array(rawKey))
  };
}

export function base64UrlEncode(bytes: Uint8Array) {
  let binary = "";
  for (let index = 0; index < bytes.length; index += 0x8000) {
    binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000));
  }
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}

export function base64UrlDecode(value: string) {
  const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
  const binary = atob(padded);
  const bytes = new Uint8Array(binary.length);
  for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
  return bytes;
}

async function sha256Base64Url(value: string) {
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
  return base64UrlEncode(new Uint8Array(digest));
}
