import { signPayload } from './wallet_crypto.mjs';

const LEGACY_WALLET_KEY = 'mazexchain.native.wallet';
const VAULTS_KEY = 'mazexchain.wallet.vaults';
const ACTIVE_ADDRESS_KEY = 'mazexchain.wallet.active.address';
const SESSION_WALLET_KEY = 'mazexchain.session.wallet';
const CONNECTED_ORIGINS_KEY = 'mazexchain.connected.origins';
const SIGNATURE_APPROVAL_TIMEOUT_MS = 120000;
const pendingSignatureRequests = new Map();

function getStorage(keys) {
  return new Promise((resolve) => chrome.storage.local.get(keys, resolve));
}

function setStorage(items) {
  return new Promise((resolve) => chrome.storage.local.set(items, resolve));
}

function sessionStorageArea() {
  return chrome.storage.session || chrome.storage.local;
}

function getSession(keys) {
  return new Promise((resolve) => sessionStorageArea().get(keys, resolve));
}

function originFromSender(sender = {}) {
  try {
    return new URL(sender.url || sender.tab?.url || '').origin;
  } catch {
    return 'unknown';
  }
}

function publicAccount(wallet) {
  if (!wallet?.address) return null;
  return {
    address: wallet.address,
    public_key: wallet.public_key,
    algorithm: wallet.algorithm || 'ed25519',
    version: wallet.version || 'mazex-wallet-1.0',
    wallet_type: 'mazexchain-extension',
  };
}

function summarizePayload(payload = {}) {
  const summary = {
    type: payload.type || 'UNKNOWN_PAYLOAD',
    chain_id: payload.chain_id || '',
    layer_no: payload.layer_no ?? '',
    step_no: payload.step_no ?? '',
    chain_height: payload.chain_height ?? '',
    account_nonce: payload.account_nonce ?? '',
  };
  if (payload.submit_fee !== undefined) summary.submit_fee = payload.submit_fee;
  if (payload.fee !== undefined) summary.fee = payload.fee;
  if (payload.amount !== undefined) summary.amount = payload.amount;
  if (payload.to !== undefined) summary.to = payload.to;
  if (payload.candidate_hash !== undefined) summary.candidate_hash = payload.candidate_hash;
  if (payload.chain_head_hash !== undefined) summary.chain_head_hash = payload.chain_head_hash;
  return summary;
}

async function readWalletState() {
  const stored = await getStorage([LEGACY_WALLET_KEY, VAULTS_KEY, ACTIVE_ADDRESS_KEY, CONNECTED_ORIGINS_KEY]);
  const session = await getSession([SESSION_WALLET_KEY]);
  const vaults = Array.isArray(stored[VAULTS_KEY]) ? stored[VAULTS_KEY] : [];
  const activeAddress = stored[ACTIVE_ADDRESS_KEY] || vaults[0]?.address || '';
  const activeVault = vaults.find((vault) => vault.address === activeAddress) || vaults[0] || null;
  const sessionWallet = session[SESSION_WALLET_KEY] || stored[LEGACY_WALLET_KEY] || null;
  const wallet = sessionWallet?.address === activeVault?.address || !activeVault ? sessionWallet : null;
  return {
    wallet,
    activeVault,
    connectedOrigins: Array.isArray(stored[CONNECTED_ORIGINS_KEY]) ? stored[CONNECTED_ORIGINS_KEY] : [],
  };
}

async function connectOrigin(origin) {
  const { wallet, activeVault, connectedOrigins } = await readWalletState();
  if (!activeVault?.address && !wallet?.address) {
    throw Object.assign(new Error('Create or import a MazeXChain wallet in the extension first.'), {
      code: 'WALLET_NOT_READY',
    });
  }
  if (!wallet?.address) {
    throw Object.assign(new Error('Unlock the MazeXChain Wallet extension before connecting this site.'), {
      code: 'WALLET_LOCKED',
    });
  }
  const nextOrigins = Array.from(new Set([...connectedOrigins, origin]));
  await setStorage({ [CONNECTED_ORIGINS_KEY]: nextOrigins });
  return {
    connected: true,
    origin,
    account: publicAccount(wallet),
  };
}

async function ensureConnected(origin) {
  const { wallet, activeVault, connectedOrigins } = await readWalletState();
  if (!activeVault?.address && !wallet?.address) {
    throw Object.assign(new Error('Create or import a MazeXChain wallet in the extension first.'), {
      code: 'WALLET_NOT_READY',
    });
  }
  if (!wallet?.address) {
    throw Object.assign(new Error('Unlock the MazeXChain Wallet extension before signing.'), {
      code: 'WALLET_LOCKED',
    });
  }
  if (!connectedOrigins.includes(origin)) {
    throw Object.assign(new Error('Connect this site to the MazeXChain Wallet extension first.'), {
      code: 'CONNECT_REQUIRED',
    });
  }
  return wallet;
}

function rejectApproval(id, reason) {
  const pending = pendingSignatureRequests.get(id);
  if (!pending) return;
  clearTimeout(pending.timeout);
  pendingSignatureRequests.delete(id);
  pending.reject(Object.assign(new Error(reason || 'Signature request rejected.'), { code: 'SIGNATURE_REJECTED' }));
}

function signatureRequestPublicView(id, pending) {
  return {
    id,
    origin: pending.origin,
    account: publicAccount(pending.wallet),
    payload: summarizePayload(pending.payload),
    created_at: pending.createdAt,
    expires_at: pending.expiresAt,
  };
}

async function requestSignatureApproval({ origin, wallet, payload }) {
  const id = `sig-${Date.now()}-${Math.random().toString(16).slice(2)}`;
  const createdAt = new Date().toISOString();
  const expiresAt = new Date(Date.now() + SIGNATURE_APPROVAL_TIMEOUT_MS).toISOString();
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => rejectApproval(id, 'Signature request expired.'), SIGNATURE_APPROVAL_TIMEOUT_MS);
    pendingSignatureRequests.set(id, {
      origin,
      wallet,
      payload,
      createdAt,
      expiresAt,
      timeout,
      resolve,
      reject,
    });
    chrome.windows.create(
      {
        url: chrome.runtime.getURL(`confirm.html?id=${encodeURIComponent(id)}`),
        type: 'popup',
        width: 470,
        height: 620,
        focused: true,
      },
      () => {
        if (chrome.runtime.lastError) {
          rejectApproval(id, chrome.runtime.lastError.message);
        }
      },
    );
  });
}

async function handleRpc(message, sender) {
  const origin = originFromSender(sender);
  const method = String(message.method || '');
  const params = message.params || {};

  if (method === 'ping') {
    return { installed: true, version: chrome.runtime.getManifest().version };
  }

  if (method === 'connect') {
    return connectOrigin(origin);
  }

  if (method === 'getAccount') {
    const { wallet, activeVault, connectedOrigins } = await readWalletState();
    return {
      connected: connectedOrigins.includes(origin),
      origin,
      locked: Boolean(activeVault?.address && !wallet?.address),
      account: publicAccount(wallet) || publicAccount(activeVault),
    };
  }

  if (method === 'signPayload') {
    const wallet = await ensureConnected(origin);
    if (!params.payload || typeof params.payload !== 'object') {
      throw Object.assign(new Error('signPayload requires a protocol payload object.'), {
        code: 'PAYLOAD_REQUIRED',
      });
    }
    return {
      signedPayload: await requestSignatureApproval({
        origin,
        wallet,
        payload: params.payload,
      }),
    };
  }

  throw Object.assign(new Error(`Unsupported MazeXChain Wallet method: ${method}`), {
    code: 'METHOD_NOT_SUPPORTED',
  });
}

async function handleSignatureApproval(message) {
  const method = String(message.method || '');
  const id = String(message.id || '');
  if (method === 'getPendingSignature') {
    const pending = pendingSignatureRequests.get(id);
    if (!pending) {
      throw Object.assign(new Error('Signature request is no longer available.'), {
        code: 'SIGNATURE_REQUEST_MISSING',
      });
    }
    return signatureRequestPublicView(id, pending);
  }

  if (method === 'approveSignature') {
    const pending = pendingSignatureRequests.get(id);
    if (!pending) {
      throw Object.assign(new Error('Signature request is no longer available.'), {
        code: 'SIGNATURE_REQUEST_MISSING',
      });
    }
    const signedPayload = await signPayload(pending.wallet, pending.payload);
    clearTimeout(pending.timeout);
    pendingSignatureRequests.delete(id);
    pending.resolve(signedPayload);
    return { approved: true };
  }

  if (method === 'rejectSignature') {
    rejectApproval(id, 'Signature rejected by user.');
    return { rejected: true };
  }

  throw Object.assign(new Error(`Unsupported signature approval method: ${method}`), {
    code: 'METHOD_NOT_SUPPORTED',
  });
}

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message?.type === 'MAZEXCHAIN_SIGNATURE_APPROVAL') {
    handleSignatureApproval(message)
      .then((result) => sendResponse({ ok: true, result }))
      .catch((error) =>
        sendResponse({
          ok: false,
          error: {
            code: error.code || 'MAZEXCHAIN_SIGNATURE_APPROVAL_ERROR',
            message: error.message || 'Signature approval request failed.',
          },
        }),
      );
    return true;
  }

  if (!message || message.type !== 'MAZEXCHAIN_WALLET_RPC') return false;
  handleRpc(message, sender)
    .then((result) => sendResponse({ ok: true, id: message.id, result }))
    .catch((error) =>
      sendResponse({
        ok: false,
        id: message.id,
        error: {
          code: error.code || 'MAZEXCHAIN_WALLET_ERROR',
          message: error.message || 'MazeXChain Wallet request failed.',
        },
      }),
    );
  return true;
});
