const express = require('express');
const walletService = require('../services/walletService');
const canonicalPointConversionService = require('../services/canonicalPointConversionService');
const evmWalletService = require('../services/evmWalletService');
const evmTransferService = require('../services/evmTransferService');
const { EVM_CANDIDATE_CONFIG } = require('../config/evmCandidateConfig');
const { requireServiceAuth } = require('../middleware/serviceAuth');

const router = express.Router();

function publicRecovery(recovery) {
  if (!recovery) return null;
  return {
    enabled: !!recovery.enabled,
    version: recovery.version || null,
    algorithm: recovery.algorithm || null,
    kdf: recovery.kdf || null,
    encryptedPrivateKeyStored: !!recovery.encryptedPrivateKeyStored,
    recoveryConfirmedAt: recovery.recoveryConfirmedAt || null,
    restoredAt: recovery.restoredAt || null,
    createdAt: recovery.createdAt || null,
    updatedAt: recovery.updatedAt || null
  };
}
function publicWallet(wallet) {
  if (!wallet) return null;
  const metadata = { ...(wallet.metadata || {}) };
  delete metadata.pinHash;
  delete metadata.recoveryVault;
  return {
    id: wallet.id,
    address: wallet.address,
    label: wallet.label,
    ownerType: wallet.ownerType,
    ownerId: wallet.ownerId,
    publicKey: wallet.publicKey,
    balance: wallet.balance,
    lockedBalance: wallet.lockedBalance,
    rewardBalance: wallet.rewardBalance,
    nonce: wallet.nonce,
    status: wallet.status,
    metadata,
    pinEnabled: walletService.walletHasPin(wallet.address),
    recovery: publicRecovery(walletService.getWalletRecoveryStatus(wallet)),
    createdAt: wallet.createdAt,
    updatedAt: wallet.updatedAt
  };
}

function publicWallets(wallets) {
  return (wallets || []).map(publicWallet).filter(Boolean);
}

function sendWalletError(res, error, fallback = 'WALLET_ERROR') {
  return res.status(error.status || 400).json({
    ok: false,
    error: error.code || fallback,
    message: error.message || fallback
  });
}

router.get('/', (req, res) => {
  res.json({
    ok: true,
    stats: walletService.getStats(),
    wallets: publicWallets(walletService.listWallets())
  });
});

router.post('/', (req, res, next) => {
  try {
    const wallet = walletService.createWallet(req.body || {});
    res.status(201).json({ ok: true, wallet: publicWallet(wallet) });
  } catch (err) {
    next(err);
  }
});

router.get('/stats', (req, res) => {
  res.json({ ok: true, stats: walletService.getStats() });
});

router.get('/tx', (req, res) => {
  res.json({ ok: true, transactions: walletService.listWalletTransactions() });
});

router.post('/transfer', (req, res, next) => {
  try {
    walletService.verifyWalletPin(req.body.fromAddress, req.body.pin);
    const transaction = walletService.transfer({
      ...(req.body || {}),
      fee: 1,
      metadata: {
        ...((req.body && req.body.metadata) || {}),
        feePolicy: 'fixed-1-plai'
      }
    });
    res.status(201).json({ ok: true, transaction });
  } catch (err) {
    next(err);
  }
});

router.post('/service-transfer', requireServiceAuth, (req, res, next) => {
  try {
    const ownerType = String(req.body.ownerType || 'aisfor_user').trim();
    const ownerId = String(req.body.ownerId || '').trim();

    if (ownerType !== 'aisfor_user') {
      const err = new Error('Only AISFOR user wallets can be used for service transfers.');
      err.status = 400;
      err.code = 'UNSUPPORTED_OWNER_TYPE';
      throw err;
    }
    if (!ownerId) {
      const err = new Error('ownerId is required');
      err.status = 400;
      err.code = 'OWNER_ID_REQUIRED';
      throw err;
    }

    const wallet = walletService.getWalletByOwner(ownerType, ownerId);
    if (!wallet) {
      const err = new Error('User wallet not found');
      err.status = 404;
      err.code = 'USER_WALLET_NOT_FOUND';
      throw err;
    }

    const transaction = walletService.transfer({
      fromAddress: wallet.address,
      toAddress: req.body.toAddress,
      amount: req.body.amount,
      memo: req.body.memo || 'Service approved wallet transfer',
      fee: 1,
      metadata: {
        ...((req.body && req.body.metadata) || {}),
        feePolicy: 'fixed-1-plai',
        transferMode: 'service-approved',
        approvedByService: req.serviceIdentity?.serviceName || ''
      }
    });

    res.status(201).json({
      ok: true,
      mode: 'service-approved-transfer',
      serviceIdentity: req.serviceIdentity,
      wallet: publicWallet(walletService.getWallet(wallet.address)),
      transaction
    });
  } catch (err) {
    next(err);
  }
});

router.get('/evm/user/:userId', requireServiceAuth, async (req, res, next) => {
  try {
    const wallet = evmWalletService.getWalletByOwner('aisfor_user', req.params.userId);
    if (!wallet) {
      const err = new Error('PLAIS EVM wallet not found');
      err.status = 404;
      err.code = 'EVM_WALLET_NOT_FOUND';
      throw err;
    }
    const account = await evmTransferService.getAccountSummary(wallet.address);
    return res.json({
      ok: true,
      wallet,
      account,
      paymentsEnabled: EVM_CANDIDATE_CONFIG.externalTransfersEnabled === true
    });
  } catch (err) {
    return next(err);
  }
});

router.post('/evm/service-transfer', requireServiceAuth, async (req, res, next) => {
  try {
    const ownerId = String(req.body.ownerId || '').trim();
    const recipientOwnerId = String(req.body.recipientOwnerId || '').trim();
    if (!ownerId || !recipientOwnerId || ownerId === recipientOwnerId) {
      const err = new Error('Valid buyer and recipient owner IDs are required');
      err.status = 400;
      err.code = 'EVM_TRANSFER_OWNERS_REQUIRED';
      throw err;
    }
    const recipient = evmWalletService.getWalletByOwner('aisfor_user', recipientOwnerId);
    if (!recipient) {
      const err = new Error('Creator PLAIS wallet not found');
      err.status = 409;
      err.code = 'RECIPIENT_EVM_WALLET_NOT_FOUND';
      throw err;
    }
    const result = await evmWalletService.sendTransferForOwner({
      ownerType: 'aisfor_user',
      ownerId,
      pin: req.body.pin,
      to: recipient.address,
      amount: req.body.amount,
      memo: req.body.memo || 'PLAISHARING paid download',
      idempotencyKey: req.body.idempotencyKey
    });
    if (result.status && result.status !== 'confirmed') {
      const err = new Error('PLAIS transaction is still pending');
      err.status = 409;
      err.code = 'EVM_TRANSACTION_PENDING';
      throw err;
    }
    return res.status(result.duplicate ? 200 : 201).json({
      ok: true,
      duplicate: result.duplicate === true,
      transaction: {
        hash: result.hash,
        from: result.from,
        to: result.to,
        amount: result.amount,
        status: 'confirmed'
      }
    });
  } catch (err) {
    return next(err);
  }
});

router.post('/service-address-transfer', requireServiceAuth, (req, res, next) => {
  try {
    const fromAddress = String(req.body.fromAddress || '').trim();
    const toAddress = String(req.body.toAddress || '').trim();
    if (!fromAddress || !toAddress) {
      const err = new Error('fromAddress and toAddress are required');
      err.status = 400;
      err.code = 'ADDRESS_REQUIRED';
      throw err;
    }

    const fromWallet = walletService.getWallet(fromAddress);
    if (!fromWallet) {
      const err = new Error('Escrow wallet not found');
      err.status = 404;
      err.code = 'ESCROW_WALLET_NOT_FOUND';
      throw err;
    }

    const fee = Number.isFinite(Number(req.body.fee)) ? Number(req.body.fee) : 0;
    const transaction = walletService.transfer({
      fromAddress,
      toAddress,
      amount: req.body.amount,
      memo: req.body.memo || 'Service approved escrow release',
      fee,
      metadata: {
        ...((req.body && req.body.metadata) || {}),
        feePolicy: fee > 0 ? 'service-address-fee' : 'escrow-release-no-extra-fee',
        transferMode: 'service-address-approved',
        approvedByService: req.serviceIdentity?.serviceName || ''
      }
    });

    res.status(201).json({
      ok: true,
      mode: 'service-address-transfer',
      serviceIdentity: req.serviceIdentity,
      wallet: publicWallet(walletService.getWallet(fromAddress)),
      transaction
    });
  } catch (err) {
    next(err);
  }
});

router.post('/service-owned-wallet', requireServiceAuth, (req, res, next) => {
  try {
    const result = walletService.ensureOwnedWalletWithRecoveryExport({
      ownerType: req.body.ownerType,
      ownerId: req.body.ownerId,
      label: req.body.label,
      metadata: {
        ...((req.body && req.body.metadata) || {}),
        createdByService: req.serviceIdentity?.serviceName || ''
      },
      faucetAmount: req.body.faucetAmount || 0
    });

    res.status(result.created ? 201 : 200).json({
      ok: true,
      mode: 'service-owned-wallet',
      serviceIdentity: req.serviceIdentity,
      created: result.created,
      wallet: publicWallet(result.wallet),
      recoveryExport: result.recoveryExport,
      faucetTransaction: result.faucetTransaction || null
    });
  } catch (err) {
    next(err);
  }
});

router.post('/service-owned-wallet/recovery-reissue', requireServiceAuth, (req, res, next) => {
  try {
    const result = walletService.reissueOwnedWalletRecoveryExport({
      ownerType: req.body.ownerType,
      ownerId: req.body.ownerId,
      metadata: {
        ...((req.body && req.body.metadata) || {}),
        reissuedByService: req.serviceIdentity?.serviceName || ''
      }
    });

    res.status(200).json({
      ok: true,
      mode: 'service-owned-wallet-recovery-reissue',
      serviceIdentity: req.serviceIdentity,
      wallet: publicWallet(result.wallet),
      recoveryExport: result.recoveryExport
    });
  } catch (err) {
    next(err);
  }
});

router.post('/point-conversion/readiness', requireServiceAuth, (req, res, next) => {
  try {
    const readiness = canonicalPointConversionService.getReadiness(req.body.userId);
    res.json({ ok: true, readiness });
  } catch (err) {
    next(err);
  }
});

router.post('/point-conversion/status', requireServiceAuth, (req, res, next) => {
  try {
    const conversion = canonicalPointConversionService.getConversion(req.body.idempotencyKey);
    if (!conversion) {
      const err = new Error('Point conversion was not found.');
      err.status = 404;
      err.code = 'POINT_CONVERSION_NOT_FOUND';
      throw err;
    }
    res.json({ ok: true, mode: 'canonical-besu-qbft', conversion });
  } catch (err) {
    next(err);
  }
});

router.post('/point-conversion', requireServiceAuth, (req, res, next) => {
  try {
    const result = canonicalPointConversionService.createConversion({
      userId: req.body.userId,
      pointAmount: req.body.pointAmount,
      coinAmount: req.body.coinAmount,
      conversionRate: req.body.conversionRate || 1000,
      requestId: req.body.requestId,
      idempotencyKey: req.body.idempotencyKey,
      label: req.body.label
    });
    res.status(result.duplicate ? 200 : 201).json({
      ok: true,
      mode: 'canonical-besu-qbft',
      serviceIdentity: req.serviceIdentity,
      duplicate: result.duplicate,
      conversion: result.conversion
    });
  } catch (err) {
    next(err);
  }
});

router.get('/user/:userId', (req, res, next) => {
  try {
    const result = walletService.ensureUserWallet({
      userId: req.params.userId,
      label: req.query.label,
      faucetAmount: 0
    });
    res.json({ ok: true, wallet: publicWallet(result.wallet), created: result.created, faucetTransaction: result.faucetTransaction, transactions: walletService.getWalletTransactions(result.wallet.address) });
  } catch (err) {
    next(err);
  }
});

router.post('/user/:userId', (req, res, next) => {
  try {
    const result = walletService.ensureUserWallet({
      userId: req.params.userId,
      label: req.body.label,
      metadata: req.body.metadata || {},
      faucetAmount: req.body.faucetAmount || 0
    });
    res.status(result.created ? 201 : 200).json({ ok: true, wallet: publicWallet(result.wallet), created: result.created, faucetTransaction: result.faucetTransaction });
  } catch (err) {
    next(err);
  }
});

router.get('/users/list', (req, res) => {
  res.json({ ok: true, wallets: publicWallets(walletService.listUserWallets()) });
});


router.post('/:address/pin', (req, res) => {
  try {
    const wallet = walletService.setWalletPin(req.params.address, req.body.pin);
    return res.json({ ok: true, wallet: publicWallet(wallet) });
  } catch (error) {
    return sendWalletError(res, error, 'PIN_SET_FAILED');
  }
});
router.get('/:address/recovery', (req, res) => {
  const wallet = walletService.getWallet(req.params.address);
  if (!wallet) return res.status(404).json({ ok: false, error: 'wallet_not_found' });
  return res.json({ ok: true, recovery: publicRecovery(walletService.getWalletRecoveryStatus(wallet)) });
});

router.post('/:address/recovery/enable', (req, res) => {
  try {
    const result = walletService.enableWalletRecovery(req.params.address, req.body.pin);
    return res.json({ ok: true, recovery: publicRecovery(result.status), export: result.export });
  } catch (error) {
    return sendWalletError(res, error, 'recovery_enable_failed');
  }
});

router.post('/:address/recovery/export', (req, res) => {
  try {
    const exported = walletService.exportWalletRecovery(req.params.address, req.body.pin);
    return res.json({ ok: true, export: exported });
  } catch (error) {
    return sendWalletError(res, error, 'recovery_export_failed');
  }
});

router.post('/:address/recovery/confirm', (req, res) => {
  try {
    const recovery = walletService.confirmWalletRecovery(req.params.address, req.body.pin, req.body.recoveryPhrase);
    return res.json({ ok: true, recovery: publicRecovery(recovery) });
  } catch (error) {
    return sendWalletError(res, error, 'recovery_confirm_failed');
  }
});

router.post('/recovery/restore', (req, res) => {
  try {
    const result = walletService.restoreWalletWithRecoveryPhrase(req.body.recoveryPhrase, req.body.newPin);
    return res.json({ ok: true, wallet: publicWallet(result.wallet), recovery: publicRecovery(result.recovery) });
  } catch (error) {
    return sendWalletError(res, error, 'recovery_restore_failed');
  }
});
router.get('/:address', (req, res) => {
  const wallet = walletService.getWallet(req.params.address);
  if (!wallet) return res.status(404).json({ ok: false, error: 'WALLET_NOT_FOUND' });
  res.json({
    ok: true,
    wallet: publicWallet(wallet),
    transactions: walletService.getWalletTransactions(wallet.address)
  });
});

module.exports = router;
