import { extractDeviceKey32FromStoredValue } from './device-key-utils.js';
import { loadEncryptedUserSecrets } from './key-vault.js';
import { SOLANA_ENDPOINT_DEFAULT } from '../solana-programs.js';

const DEFAULT_SOLANA_ENDPOINT = SOLANA_ENDPOINT_DEFAULT;
const TOPUP_SITE_URL = '/devnet-topup-view';

let solanaLibPromise = null;
const BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]+$/;
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const BASE58_MAP = (() => {
  const out = Object.create(null);
  for (let i = 0; i < BASE58_ALPHABET.length; i += 1) {
    out[BASE58_ALPHABET[i]] = i;
  }
  return out;
})();

function normalizeEndpoint(url) {
  const raw = String(url || '').trim();
  if (!raw) return DEFAULT_SOLANA_ENDPOINT;
  return raw;
}

async function loadSolanaLib() {
  if (!solanaLibPromise) {
    solanaLibPromise = import('https://esm.sh/@solana/web3.js@1.98.4?bundle');
  }
  return solanaLibPromise;
}

function decodeBase58(input) {
  const text = String(input || '').trim();
  if (!text) return new Uint8Array(0);

  const bytes = [0];
  for (let i = 0; i < text.length; i += 1) {
    const ch = text[i];
    const value = BASE58_MAP[ch];
    if (value == null) {
      throw new Error('Недопустимый символ Base58');
    }

    let carry = value;
    for (let j = 0; j < bytes.length; j += 1) {
      const x = (bytes[j] * 58) + carry;
      bytes[j] = x & 0xff;
      carry = x >> 8;
    }
    while (carry > 0) {
      bytes.push(carry & 0xff);
      carry >>= 8;
    }
  }

  for (let i = 0; i < text.length && text[i] === '1'; i += 1) {
    bytes.push(0);
  }

  bytes.reverse();
  return Uint8Array.from(bytes);
}

async function keypairFromPkcs8(pkcs8B64) {
  const solana = await loadSolanaLib();
  const seed32 = extractDeviceKey32FromStoredValue(pkcs8B64);
  return solana.Keypair.fromSeed(seed32);
}

export async function createRandomSolanaWallet() {
  const solana = await loadSolanaLib();
  const keypair = solana.Keypair.generate();
  const privateKey32Base58 = solana.bs58.encode(keypair.secretKey.slice(0, 32));
  return {
    address: keypair.publicKey.toBase58(),
    privateKey32Base58,
    keypair,
  };
}

export async function createSolanaWalletFromPrivateBase58(privateKey32Base58) {
  const solana = await loadSolanaLib();
  const clean = String(privateKey32Base58 || '').trim();
  if (!clean) throw new Error('Введите приватный ключ');
  if (!BASE58_RE.test(clean)) throw new Error('Разрешены только символы Base58');
  const privateBytes = decodeBase58(clean);
  if (privateBytes.length !== 32) {
    throw new Error('Приватный ключ должен быть ровно 32 байта в Base58');
  }
  const keypair = solana.Keypair.fromSeed(privateBytes);
  return {
    address: keypair.publicKey.toBase58(),
    privateKey32Base58: clean,
    keypair,
  };
}

export async function getWalletFromStoredDeviceKey({ login, storagePwd }) {
  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 devicePrivate = String(secrets?.deviceKey || '').trim();
  if (!devicePrivate) {
    throw new Error('На устройстве не найден device.key (wallet.key)');
  }
  const keypair = await keypairFromPkcs8(devicePrivate);
  return {
    address: keypair.publicKey.toBase58(),
    keypair,
    devicePrivatePkcs8B64: devicePrivate,
  };
}

export async function getBalanceSol({ endpoint, address }) {
  const solana = await loadSolanaLib();
  const rpc = normalizeEndpoint(endpoint);
  const conn = new solana.Connection(rpc, 'confirmed');
  const pubkey = new solana.PublicKey(String(address || '').trim());
  const lamports = await conn.getBalance(pubkey, 'confirmed');
  return {
    endpoint: rpc,
    lamports,
    sol: lamports / solana.LAMPORTS_PER_SOL,
  };
}

export async function requestAirdropSol({ endpoint, address, amountSol = 1 }) {
  const solana = await loadSolanaLib();
  const rpc = normalizeEndpoint(endpoint);
  const conn = new solana.Connection(rpc, 'confirmed');
  const pubkey = new solana.PublicKey(String(address || '').trim());
  const lamports = Math.max(1, Math.floor(Number(amountSol) * solana.LAMPORTS_PER_SOL));
  const signature = await conn.requestAirdrop(pubkey, lamports);
  await conn.confirmTransaction(signature, 'confirmed');
  return { endpoint: rpc, signature, lamports };
}

export async function transferSol({ endpoint, fromKeypair, toAddress, amountSol }) {
  const solana = await loadSolanaLib();
  const rpc = normalizeEndpoint(endpoint);
  const cleanTo = String(toAddress || '').trim();
  const amount = Number(amountSol);
  if (!cleanTo) throw new Error('Не указан адрес получателя');
  if (!Number.isFinite(amount) || amount <= 0) throw new Error('Сумма перевода должна быть больше 0');

  const conn = new solana.Connection(rpc, 'confirmed');
  const lamports = Math.floor(amount * solana.LAMPORTS_PER_SOL);
  if (lamports <= 0) throw new Error('Сумма слишком мала');

  const tx = new solana.Transaction().add(
    solana.SystemProgram.transfer({
      fromPubkey: fromKeypair.publicKey,
      toPubkey: new solana.PublicKey(cleanTo),
      lamports,
    }),
  );

  const signature = await solana.sendAndConfirmTransaction(conn, tx, [fromKeypair], {
    commitment: 'confirmed',
  });
  return { endpoint: rpc, signature, lamports };
}

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

export function getTopupSiteUrl(walletAddress = '') {
  const cleanWallet = String(walletAddress || '').trim();
  if (!cleanWallet) return TOPUP_SITE_URL;
  try {
    const base = typeof window !== 'undefined' && window.location?.origin
      ? window.location.origin
      : 'http://localhost:8088';
    const url = new URL(TOPUP_SITE_URL, base);
    url.searchParams.set('wallet', cleanWallet);
    return url.toString();
  } catch {
    return `${TOPUP_SITE_URL}?wallet=${encodeURIComponent(cleanWallet)}`;
  }
}
