import { MAX_SATS_RECEIVE, MIN_SATS_RECEIVE } from "$lib/config";
import { getDb } from "$lib/database";
import { bech32 } from "@scure/base";
import type { Dexie, Table } from "dexie";
import { type Writable, writable } from "svelte/store";
import { nip19 } from "nostr-tools";
import { browser } from "$app/environment";

export function satsComma(sats: number) {
  const chars = sats.toString().split("").reverse();
  const formatted = chars
    .reduce(
      (acc, char, i) => (i % 3 === 0 ? `${acc} ${char}` : acc + char),
      "",
    )
    .trim()
    .split("")
    .reverse()
    .join("");
  return formatted;
}

export function clamp(min: number, max: number, v: number) {
  return Math.max(min, Math.min(max, v));
}

export function encodeLNURL(url: string): string {
  const words = bech32.toWords(new TextEncoder().encode(url));
  return bech32.encode("lnurl", words, 1023);
}

export function validateLightningInvoiceAmount(
  amount: string,
  { isMillisats }: { isMillisats: boolean },
) {
  const minMillisats = MIN_SATS_RECEIVE * 1000;
  const maxMillisats = MAX_SATS_RECEIVE * 1000;

  const amountNumber = Number(amount);
  if (Number.isNaN(amountNumber)) return false;
  if (isMillisats && amountNumber < minMillisats) return false;
  if (!isMillisats && amountNumber < MIN_SATS_RECEIVE) return false;
  if (isMillisats && amountNumber > maxMillisats) return false;
  if (!isMillisats && amountNumber > MAX_SATS_RECEIVE) return false;
  return true;
}

export async function userExists(username: string) {
  const db = await getDb();
  const result = await db!.execute(
    "SELECT * FROM users WHERE username = ? OR npub = ?",
    [username, username],
  );
  return result.rows.length > 0;
}

export function isNpub(data: string): data is `npub1${string}` {
  try {
    nip19.decode(data);
    return true;
  } catch {
    return false;
  }
}

export async function userExistsOrNpub(username: string) {
  const userExistsResult = await userExists(username);
  if (userExistsResult) return true;
  return isNpub(username);
}

export async function getUser(username: string) {
  const db = await getDb();
  const result = await db!.execute(
    "SELECT * FROM users WHERE username = ? OR npub = ?",
    [username, username],
  );
  return result.rows[0];
}

export function hexToBytes(hex: string): Uint8Array {
  const bytes = new Uint8Array(hex.length / 2);
  for (let i = 0; i < hex.length; i += 2) {
    bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
  }
  return bytes;
}

export function bytesToHex(bytes: Uint8Array): string {
  return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
    "",
  );
}

export interface MetaEntry {
  key: string;
  value: string;
}

export function persistentDbWritable<T, DB extends Dexie>(
  key: string,
  initial: T,
  db: DB & { meta: Table<MetaEntry, string> },
): Writable<T> {
  const store = writable<T>(initial);

  if (browser) {
    (async () => {
      try {
        const entry = await db.meta.get(key);
        if (entry) {
          store.set(JSON.parse(entry.value));
        } else {
          const raw = localStorage.getItem(key);
          if (raw !== null) {
            store.set(JSON.parse(raw));
            await db.meta.put({ key, value: raw });
            localStorage.removeItem(key);
          }
        }
      } catch (err) {
        console.error(`NWC store load error for ${key}`, err);
      }
    })();

    store.subscribe(async (value) => {
      try {
        await db.meta.put({ key, value: JSON.stringify(value) });
      } catch (err) {
        console.error(`NWC store persist error for ${key}`, err);
      }
    });
  }

  return store;
}
