import * as SecureStore from 'expo-secure-store';
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';

export const BASE = 'https://health.rehbock.xyz';

export const EP = {
  meta: `${BASE}/health-meta.json`,
  biomarkers: `${BASE}/api/health/v1/biomarkers`,
  draws: `${BASE}/api/health/v1/draws`,
  workouts: `${BASE}/api/health/v1/workouts`,
  sleepApi: `${BASE}/api/health/v1/sleep`,
  sleepCycle: `${BASE}/sleep-data.json`,
  dexa: `${BASE}/dexa-scans.json`,
  medStats: `${BASE}/api/meditation/v1/stats`,
  medSessions: `${BASE}/api/meditation/v1/sessions?limit=1000`,
  traits: `${BASE}/ancestry-traits.json`,
} as const;

export type EpKey = keyof typeof EP;

const AUTH_KEY = 'hd.auth';

let token: string | null = null;
let onUnauthorized: (() => void) | null = null;

/* base64 without Buffer/btoa dependency quirks across Hermes versions */
const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
export function b64(str: string): string {
  const bytes: number[] = [];
  for (let i = 0; i < str.length; i++) {
    const code = str.codePointAt(i)!;
    if (code > 0xffff) i++;
    if (code < 0x80) bytes.push(code);
    else if (code < 0x800) bytes.push(0xc0 | (code >> 6), 0x80 | (code & 63));
    else if (code < 0x10000)
      bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 63), 0x80 | (code & 63));
    else
      bytes.push(
        0xf0 | (code >> 18), 0x80 | ((code >> 12) & 63),
        0x80 | ((code >> 6) & 63), 0x80 | (code & 63),
      );
  }
  let out = '';
  for (let i = 0; i < bytes.length; i += 3) {
    const [a, b, c] = [bytes[i], bytes[i + 1], bytes[i + 2]];
    out += B64[a >> 2] + B64[((a & 3) << 4) | (b ?? 0) >> 4];
    out += b === undefined ? '=' : B64[((b & 15) << 2) | (c ?? 0) >> 6];
    out += c === undefined ? '=' : B64[c & 63];
  }
  return out;
}

async function authFetch<T>(url: string): Promise<T> {
  const r = await fetch(url, {
    headers: { Authorization: 'Basic ' + token },
    cache: 'no-store',
  });
  if (r.status === 401 || r.status === 403) {
    onUnauthorized?.();
    throw new Error('unauthorized');
  }
  if (!r.ok) throw new Error(`${url} → ${r.status}`);
  return r.json();
}

/* module-level promise cache, cleared on refresh/logout */
let cache: Partial<Record<EpKey, Promise<unknown>>> = {};

export function load<T>(key: EpKey): Promise<T> {
  if (!cache[key])
    cache[key] = authFetch<T>(EP[key]).catch((e) => {
      delete cache[key];
      throw e;
    });
  return cache[key] as Promise<T>;
}

export function clearCache() {
  cache = {};
}

/* ---- auth context ---- */

interface AuthCtx {
  ready: boolean;
  authed: boolean;
  login: (user: string, pass: string) => Promise<boolean>;
  logout: () => void;
}

const Ctx = createContext<AuthCtx>({
  ready: false,
  authed: false,
  login: async () => false,
  logout: () => {},
});

export const useAuth = () => useContext(Ctx);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [ready, setReady] = useState(false);
  const [authed, setAuthed] = useState(false);

  useEffect(() => {
    SecureStore.getItemAsync(AUTH_KEY)
      .then((t) => {
        if (t) {
          token = t;
          setAuthed(true);
        }
      })
      .finally(() => setReady(true));
  }, []);

  const logout = useCallback(() => {
    token = null;
    clearCache();
    SecureStore.deleteItemAsync(AUTH_KEY).catch(() => {});
    setAuthed(false);
  }, []);

  useEffect(() => {
    onUnauthorized = logout;
    return () => {
      onUnauthorized = null;
    };
  }, [logout]);

  const login = useCallback(async (user: string, pass: string) => {
    const tok = b64(`${user}:${pass}`);
    const r = await fetch(EP.meta, {
      headers: { Authorization: 'Basic ' + tok },
      cache: 'no-store',
    });
    if (!r.ok) return false;
    token = tok;
    clearCache();
    await SecureStore.setItemAsync(AUTH_KEY, tok);
    setAuthed(true);
    return true;
  }, []);

  return <Ctx.Provider value={{ ready, authed, login, logout }}>{children}</Ctx.Provider>;
}
