import { loadEncryptedUserSecrets, updateEncryptedUserSecrets } from './key-vault.js';
import { extractDeviceKey32FromStoredValue } from './device-key-utils.js';
import { deriveArweaveWalletFromDeviceKey32 } from './sawd-v1.js';

const DEFAULT_ARWEAVE_GATEWAY = 'https://arweave.net';
const AR_TOPUP_URL = 'https://changenow.io/exchange?from=usd&to=ar&amount=10&fiatMode=true';
const WINSTON_PER_AR = 1_000_000_000_000n;

let arweaveLibPromise = null;

function normalizeGateway(rawGateway) {
  const raw = String(rawGateway || '').trim();
  if (!raw) return DEFAULT_ARWEAVE_GATEWAY;
  let parsed;
  try {
    parsed = new URL(raw);
  } catch {
    throw new Error('Некорректный Arweave gateway URL');
  }
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
    throw new Error('Arweave gateway должен использовать http или https');
  }
  const normalized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`.replace(/\/+$/g, '');
  return normalized || DEFAULT_ARWEAVE_GATEWAY;
}

function parseGatewayForArweaveInit(gateway) {
  let parsed;
  try {
    parsed = new URL(gateway);
  } catch {
    throw new Error('Некорректный Arweave gateway URL');
  }
  const protocol = parsed.protocol.replace(':', '');
  if (protocol !== 'http' && protocol !== 'https') {
    throw new Error('Arweave gateway должен использовать http или https');
  }
  const port = parsed.port
    ? Number(parsed.port)
    : (protocol === 'https' ? 443 : 80);
  return {
    protocol,
    host: parsed.hostname,
    port,
  };
}

function parseArToWinston(amountAr) {
  const raw = String(amountAr ?? '').trim().replace(',', '.');
  const match = raw.match(/^(\d+)(?:\.(\d+))?$/);
  if (!match) {
    throw new Error('Сумма перевода должна быть числом');
  }

  const intPart = BigInt(match[1] || '0');
  const frac = String(match[2] || '');
  if (frac.length > 12) {
    throw new Error('Слишком много знаков после запятой (максимум 12)');
  }
  const fracPadded = `${frac}${'0'.repeat(12 - frac.length)}`;
  const winston = (intPart * WINSTON_PER_AR) + BigInt(fracPadded || '0');
  if (winston <= 0n) {
    throw new Error('Сумма перевода должна быть больше 0');
  }
  return winston.toString();
}

async function loadArweaveLib() {
  if (!arweaveLibPromise) {
    arweaveLibPromise = import('https://esm.sh/arweave@1.15.7?bundle');
  }
  return arweaveLibPromise;
}

function pickCachedWallet(secrets) {
  const cached = secrets?.arweaveWallet;
  if (!cached || typeof cached !== 'object') return null;
  const derivation = String(cached.derivation || '').trim();
  const address = String(cached.address || '').trim();
  const owner = String(cached.owner || '').trim();
  const jwk = cached.jwk;
  if (derivation !== 'SAWD-v1' || !address || !owner || !jwk || typeof jwk !== 'object') {
    return null;
  }
  if (!String(jwk.kty || '').trim() || !String(jwk.e || '').trim() || !String(jwk.n || '').trim() || !String(jwk.d || '').trim()) {
    return null;
  }
  return {
    derivation,
    address,
    owner,
    jwk,
  };
}

function safeStatus(onStatus, text) {
  if (typeof onStatus !== 'function') return;
  try {
    onStatus(String(text || ''));
  } catch {
    // ignore callback errors
  }
}

export async function getArweaveWalletFromStoredDeviceKey({ login, storagePwd, onStatus } = {}) {
  const cleanLogin = String(login || '').trim();
  const cleanPwd = String(storagePwd || '').trim();
  if (!cleanLogin || !cleanPwd) {
    throw new Error('Нет активной сессии для доступа к wallet.key');
  }

  const secrets = await loadEncryptedUserSecrets(cleanLogin, cleanPwd);
  const cached = pickCachedWallet(secrets);
  if (cached) {
    safeStatus(onStatus, 'Arweave-кошелёк загружен.');
    return cached;
  }

  safeStatus(onStatus, 'Сейчас мы впервые получаем Arweave-кошелёк из вашего device key. Это может занять немного времени.');

  const storedDeviceKey = String(secrets?.deviceKey || '').trim();
  if (!storedDeviceKey) {
    throw new Error('На устройстве не найден device.key (wallet.key)');
  }

  const deviceKey32 = extractDeviceKey32FromStoredValue(storedDeviceKey);
  let wallet;
  try {
    wallet = await deriveArweaveWalletFromDeviceKey32(deviceKey32);
  } finally {
    deviceKey32.fill(0);
  }

  const cachedWallet = {
    derivation: wallet.derivation,
    address: wallet.address,
    owner: wallet.owner,
    jwk: wallet.jwk,
    createdAtMs: Date.now(),
  };

  await updateEncryptedUserSecrets(cleanLogin, cleanPwd, (nextSecrets) => ({
    ...nextSecrets,
    arweaveWallet: cachedWallet,
  }));

  return {
    derivation: cachedWallet.derivation,
    address: cachedWallet.address,
    owner: cachedWallet.owner,
    jwk: cachedWallet.jwk,
  };
}

export async function getArweaveBalance({ gateway, address }) {
  const normalizedGateway = normalizeGateway(gateway);
  const cleanAddress = String(address || '').trim();
  if (!cleanAddress) {
    throw new Error('Не указан адрес Arweave');
  }

  const url = `${normalizedGateway}/wallet/${encodeURIComponent(cleanAddress)}/balance`;
  const response = await fetch(url, { method: 'GET' });
  const rawText = (await response.text()).trim();

  if (!response.ok) {
    throw new Error(`Не удалось получить баланс Arweave (${response.status} ${response.statusText})`);
  }
  if (!/^\d+$/.test(rawText)) {
    throw new Error('Arweave gateway вернул некорректный баланс');
  }

  const winstonBig = BigInt(rawText);
  const ar = Number(winstonBig) / Number(WINSTON_PER_AR);
  return {
    gateway: normalizedGateway,
    winston: rawText,
    ar,
  };
}

export function formatAr(value, digits = 6) {
  const n = Number(value);
  if (!Number.isFinite(n)) return '0';
  return n.toLocaleString('ru-RU', {
    minimumFractionDigits: 0,
    maximumFractionDigits: digits,
  });
}

export function getArweaveTopupSiteUrl() {
  return AR_TOPUP_URL;
}

export async function transferAr({ gateway, jwk, toAddress, amountAr }) {
  const cleanTo = String(toAddress || '').trim();
  if (!cleanTo) throw new Error('Не указан адрес получателя Arweave');
  if (!jwk || typeof jwk !== 'object') throw new Error('Кошелёк Arweave не инициализирован');

  const normalizedGateway = normalizeGateway(gateway);
  const { host, port, protocol } = parseGatewayForArweaveInit(normalizedGateway);
  const winston = parseArToWinston(amountAr);

  const moduleRef = await loadArweaveLib();
  const Arweave = moduleRef?.default || moduleRef;
  const arweave = Arweave.init({ host, port, protocol });

  const tx = await arweave.createTransaction(
    {
      target: cleanTo,
      quantity: winston,
    },
    jwk,
  );
  await arweave.transactions.sign(tx, jwk);
  const postResult = await arweave.transactions.post(tx);

  return {
    gateway: normalizedGateway,
    id: tx.id,
    status: Number(postResult?.status || 0),
    statusText: String(postResult?.statusText || ''),
  };
}
