import { HANDLE_RESOLVER_SERVICE_URL, PLC_DIRECTORY_URL } from "/js/config.js";

const PDS_SERVICE_ID = "#atproto_pds";

export function getServiceEndpointFromDidDoc(didDoc) {
  const service = didDoc.service.find((s) => s.id === PDS_SERVICE_ID);
  if (!service) {
    throw new Error(
      `No PDS service found in DID doc ${JSON.stringify(didDoc)}`,
    );
  }
  return service.serviceEndpoint;
}

export function didDocReferencesHandle(didDoc, handle) {
  const atHandle = "at://" + handle;
  const aliases = didDoc.alsoKnownAs ?? [];
  return aliases.includes(atHandle);
}

export async function resolveHandle(handle) {
  const params = new URLSearchParams({
    handle,
  });
  const res = await fetch(
    `${HANDLE_RESOLVER_SERVICE_URL}/xrpc/com.atproto.identity.resolveHandle?` +
      params.toString(),
  );
  const data = await res.json();
  return data.did ?? null;
}

export async function resolveDid(did) {
  if (did.startsWith("did:plc:")) {
    const res = await fetch(`${PLC_DIRECTORY_URL}/${encodeURIComponent(did)}`);
    const didDoc = await res.json();
    return didDoc;
  } else if (did.startsWith("did:web:")) {
    const website = did.split(":")[2];
    const res = await fetch(`https://${website}/.well-known/did.json`);
    const didDoc = await res.json();
    return didDoc;
  } else {
    throw new Error(`Unsupported DID: ${did}`);
  }
}

export async function resolveIdentity(handle) {
  const did = await resolveHandle(handle);
  if (!did) return null;
  const didDoc = await resolveDid(did);
  if (!didDocReferencesHandle(didDoc, handle)) {
    throw new Error(`DID doc for ${did} does not reference handle: ${handle}`);
  }
  return { did, didDoc };
}

export async function getServiceEndpointForHandle(handle) {
  const result = await resolveIdentity(handle);
  if (!result) {
    throw new HandleNotFoundError("DID not found for handle: " + handle);
  }
  return getServiceEndpointFromDidDoc(result.didDoc);
}

export class IdentityResolver {
  constructor() {
    this.handleToDidMap = new Map();
  }

  async resolveHandle(handle) {
    if (this.handleToDidMap.has(handle)) {
      return this.handleToDidMap.get(handle);
    }
    console.debug("[IdentityResolver] Resolving handle", handle);
    const did = await resolveHandle(handle);
    this.handleToDidMap.set(handle, did);
    return did;
  }

  setDidForHandle(handle, did) {
    this.handleToDidMap.set(handle, did);
  }
}

const TID_ALPHABET = "234567abcdefghijklmnopqrstuvwxyz";

let lastTimestamp = 0n;

// Will return null if the rkey is not a TID.
export function getTimestampFromRkey(rkey) {
  const noDashes = rkey.replaceAll("-", "");
  if (noDashes.length !== 13) {
    return null;
  }
  let value = 0;
  for (const c of noDashes.slice(0, 11)) {
    value = value * 32 + TID_ALPHABET.indexOf(c);
  }
  return value;
}

export function generateTid() {
  const nowMicroseconds = BigInt(Date.now()) * 1000n;
  const timestamp =
    nowMicroseconds <= lastTimestamp ? lastTimestamp + 1n : nowMicroseconds;
  lastTimestamp = timestamp;
  const clockSeq = BigInt(Math.floor(Math.random() * 1024));
  let tid = (timestamp << 10n) | clockSeq;
  let result = "";
  for (let i = 0; i < 13; i++) {
    const remainder = tid % 32n;
    result = TID_ALPHABET[Number(remainder)] + result;
    tid = tid / 32n;
  }
  return result;
}
