const jsonHeaders = { 'Content-Type': 'application/json' };

async function handle<T>(res: Response): Promise<T> {
  if (!res.ok) {
    let msg = res.statusText;
    try {
      const j = (await res.json()) as { error?: string };
      if (j.error) msg = j.error;
    } catch {
      /* ignore */
    }
    throw new Error(msg);
  }
  return (await res.json()) as T;
}

export async function login(password: string): Promise<void> {
  const res = await fetch('/api/auth/login', {
    method: 'POST',
    credentials: 'include',
    headers: jsonHeaders,
    body: JSON.stringify({ password }),
  });
  await handle<{ ok: boolean }>(res);
}

export async function logout(): Promise<void> {
  const res = await fetch('/api/auth/logout', {
    method: 'POST',
    credentials: 'include',
  });
  await handle<{ ok: boolean }>(res);
}

export type AccountDto = {
  id: string;
  label: string;
  issuer: string | null;
  algorithm: string;
  digits: number;
  period: number;
  code: string;
  expiresInSeconds: number;
  shareToken: string;
};

export type SharedAccountDto = {
  label: string;
  issuer: string | null;
  algorithm: string;
  digits: number;
  period: number;
  code: string;
  expiresInSeconds: number;
};

export async function fetchSharedAccount(token: string): Promise<SharedAccountDto> {
  const res = await fetch(`/api/share/${encodeURIComponent(token)}`);
  const data = await handle<{ account: SharedAccountDto }>(res);
  return data.account;
}

export async function fetchAccounts(): Promise<AccountDto[]> {
  const res = await fetch('/api/accounts', { credentials: 'include' });
  const data = await handle<{ accounts: AccountDto[] }>(res);
  return data.accounts;
}

export async function createAccount(body: {
  label: string;
  issuer?: string | null;
  secret: string;
  algorithm?: string;
  digits?: number;
  period?: number;
}): Promise<{ id: string }> {
  const res = await fetch('/api/accounts', {
    method: 'POST',
    credentials: 'include',
    headers: jsonHeaders,
    body: JSON.stringify(body),
  });
  return handle<{ id: string; ok: boolean }>(res);
}

export async function updateAccount(
  id: string,
  body: {
    label: string;
    issuer?: string | null;
    secret?: string;
    algorithm?: string;
    digits?: number;
    period?: number;
  },
): Promise<void> {
  const payload: Record<string, unknown> = {
    label: body.label,
    issuer: body.issuer ?? null,
  };
  if (body.secret?.trim()) payload.secret = body.secret.trim();
  if (body.algorithm) payload.algorithm = body.algorithm;
  if (body.digits !== undefined) payload.digits = body.digits;
  if (body.period !== undefined) payload.period = body.period;

  const res = await fetch(`/api/accounts/${encodeURIComponent(id)}`, {
    method: 'PATCH',
    credentials: 'include',
    headers: jsonHeaders,
    body: JSON.stringify(payload),
  });
  await handle<{ ok: boolean }>(res);
}

export async function deleteAccount(id: string): Promise<void> {
  const res = await fetch(`/api/accounts/${encodeURIComponent(id)}`, {
    method: 'DELETE',
    credentials: 'include',
  });
  await handle<{ ok: boolean }>(res);
}

export type ExportPayload = {
  version: number;
  exportedAt: string;
  items: {
    label: string;
    issuer: string | null;
    secret: string;
    algorithm: string;
    digits: number;
    period: number;
  }[];
};

export async function exportAll(): Promise<ExportPayload> {
  const res = await fetch('/api/export', { credentials: 'include' });
  return handle<ExportPayload>(res);
}

export async function importItems(
  items: ExportPayload['items'],
): Promise<{ imported: number }> {
  const res = await fetch('/api/import', {
    method: 'POST',
    credentials: 'include',
    headers: jsonHeaders,
    body: JSON.stringify({ items }),
  });
  return handle<{ ok: boolean; imported: number }>(res);
}
