require('../config/loadEnv')();

const fs = require('fs');
const path = require('path');
const { Wallet, getAddress } = require('ethers');
const operatorCustodyService = require('../services/operatorCustodyService');
const { unprotect } = require('../lib/operatorDpapi');
const { writeUtf8AtomicSync } = require('../services/atomicFileReplace');

const CONFIRMATION = 'MIGRATE_ONE_LEGACY_POINT_CONVERSION_TO_CANONICAL_EVM';
const EVIDENCE_PATH = path.join(__dirname, '..', 'data', 'canonical-point-conversion-migrations.json');

function argument(name) {
  const prefix = `--${name}=`;
  const value = process.argv.find(item => item.startsWith(prefix));
  return value ? value.slice(prefix.length).trim() : '';
}

function fail(message, code) {
  const error = new Error(message);
  error.code = code;
  throw error;
}

function readEvidenceStore() {
  if (!fs.existsSync(EVIDENCE_PATH)) return { version: 1, migrations: [] };
  const parsed = JSON.parse(fs.readFileSync(EVIDENCE_PATH, 'utf8'));
  return { version: 1, migrations: Array.isArray(parsed.migrations) ? parsed.migrations : [] };
}

function writeEvidenceStore(store) {
  writeUtf8AtomicSync(EVIDENCE_PATH, `${JSON.stringify(store, null, 2)}\n`);
}

function updateEvidence(key, patch) {
  const store = readEvidenceStore();
  const row = store.migrations.find(item => item.legacyPointConversionKey === key);
  if (!row) fail('Migration evidence record is missing.', 'MIGRATION_EVIDENCE_MISSING');
  Object.assign(row, patch, { updatedAt: new Date().toISOString() });
  writeEvidenceStore(store);
  return { ...row };
}

function loadOperatorPrivateKey() {
  const status = operatorCustodyService.getStatus();
  if (!status.ok) fail('Operator custody is not ready.', 'OPERATOR_CUSTODY_NOT_READY');
  const vault = JSON.parse(fs.readFileSync(operatorCustodyService.resolveVaultPath(), 'utf8'));
  const privateKey = unprotect(vault.ciphertext);
  const signer = new Wallet(privateKey);
  const expected = operatorCustodyService.getConfiguredOperatorAddress();
  if (!expected || getAddress(signer.address) !== getAddress(expected)) {
    fail('Operator signer identity mismatch.', 'OPERATOR_IDENTITY_MISMATCH');
  }
  return privateKey;
}

async function main() {
  const ownerId = argument('owner');
  const legacyKey = argument('legacy-key');
  const confirmation = argument('confirm');
  if (!/^[a-f0-9]{24}$/i.test(ownerId)) fail('A valid AISFOR owner id is required.', 'OWNER_ID_INVALID');
  if (!legacyKey || confirmation !== CONFIRMATION) fail('Explicit migration confirmation is required.', 'MIGRATION_CONFIRMATION_REQUIRED');
  if (process.env.PLAIS_EVM_EXTERNAL_TRANSFERS_ENABLED === '1') {
    fail('Broad EVM transfers must remain locked during one-time migration.', 'BROAD_EVM_TRANSFERS_MUST_BE_LOCKED');
  }
  process.env.PLAIS_EVM_EXTERNAL_TRANSFERS_ENABLED = '1';
  const walletService = require('../services/walletService');
  const evmWalletService = require('../services/evmWalletService');
  const evmTransferService = require('../services/evmTransferService');
  const { EVM_CANDIDATE_CONFIG } = require('../config/evmCandidateConfig');

  const legacyWallet = walletService.getWalletByOwner('aisfor_user', ownerId);
  if (!legacyWallet) fail('Legacy user wallet was not found.', 'LEGACY_WALLET_NOT_FOUND');
  const matches = walletService.getWalletTransactions(legacyWallet.address).filter(transaction => (
    transaction.metadata?.source === 'plai-point-conversion'
    && transaction.metadata?.pointConversionKey === legacyKey
  ));
  if (matches.length !== 1) fail('Exactly one legacy conversion must match.', 'LEGACY_CONVERSION_MATCH_INVALID');
  const legacy = matches[0];
  if (
    legacy.status !== 'CONFIRMED_MOCK'
    || Number(legacy.metadata?.pointAmount) !== 1000
    || Number(legacy.metadata?.coinAmount) !== 1
    || Number(legacy.metadata?.conversionRate) !== 1000
    || Number(legacy.amount) !== 1
  ) {
    fail('Legacy conversion facts do not match 1,000P = 1 PLAIS.', 'LEGACY_CONVERSION_FACT_MISMATCH');
  }

  const recipient = evmWalletService.getWalletByOwner('aisfor_user', ownerId);
  if (!recipient?.address || Number(recipient.chainId) !== Number(EVM_CANDIDATE_CONFIG.chainId)) {
    fail('Canonical recipient wallet is missing or on the wrong chain.', 'CANONICAL_RECIPIENT_INVALID');
  }

  const store = readEvidenceStore();
  let evidence = store.migrations.find(row => row.legacyPointConversionKey === legacyKey);
  if (evidence) {
    if (evidence.ownerId !== ownerId || getAddress(evidence.recipientAddress) !== getAddress(recipient.address)) {
      fail('Existing migration evidence conflicts with this request.', 'MIGRATION_EVIDENCE_CONFLICT');
    }
    if (evidence.txHash) {
      const transaction = await evmTransferService.getTransactionDetails(evidence.txHash);
      if (transaction?.status === 'confirmed') {
        evidence = updateEvidence(legacyKey, {
          status: 'confirmed',
          blockNumber: transaction.blockNumber,
          confirmedAt: evidence.confirmedAt || new Date().toISOString(),
          failureCode: '',
          failureReason: ''
        });
        process.stdout.write(`${JSON.stringify({ ok: true, duplicate: true, migration: evidence })}\n`);
        return;
      }
      fail('Existing migration transaction is not confirmed; refusing a second transfer.', 'MIGRATION_TRANSACTION_UNRESOLVED');
    }
  } else {
    const createdAt = new Date().toISOString();
    evidence = {
      schemaVersion: 'plais-legacy-point-conversion-evm-migration/v1',
      legacyPointConversionKey: legacyKey,
      legacyTxHash: legacy.hash || legacy.txHash || '',
      ownerId,
      pointAmount: 1000,
      coinAmount: 1,
      conversionRate: 1000,
      recipientAddress: recipient.address,
      operatorAddress: operatorCustodyService.getConfiguredOperatorAddress(),
      chainId: EVM_CANDIDATE_CONFIG.chainId,
      status: 'prepared',
      txHash: '',
      blockNumber: null,
      createdAt,
      updatedAt: createdAt
    };
    store.migrations.push(evidence);
    writeEvidenceStore(store);
  }

  const privateKey = loadOperatorPrivateKey();
  try {
    const transaction = await evmTransferService.sendSignedTransfer({
      privateKey,
      to: recipient.address,
      amount: '1',
      actorType: 'legacy_point_conversion_migration',
      actorId: ownerId,
      memo: `Canonical migration ${legacyKey}`,
      onBroadcast: broadcast => updateEvidence(legacyKey, {
        status: 'submitted',
        txHash: broadcast.hash,
        submittedAt: new Date().toISOString()
      })
    });
    evidence = updateEvidence(legacyKey, {
      status: 'confirmed',
      txHash: transaction.hash,
      blockNumber: Number(transaction.receipt?.blockNumber || 0) || null,
      confirmedAt: new Date().toISOString(),
      failureCode: '',
      failureReason: ''
    });
    process.stdout.write(`${JSON.stringify({ ok: true, duplicate: false, migration: evidence })}\n`);
  } catch (error) {
    const latest = readEvidenceStore().migrations.find(row => row.legacyPointConversionKey === legacyKey);
    if (!latest?.txHash) {
      updateEvidence(legacyKey, {
        status: 'failed_before_broadcast',
        failureCode: error.code || 'MIGRATION_FAILED',
        failureReason: String(error.message || error).slice(0, 500)
      });
    }
    throw error;
  }
}

main().catch(error => {
  process.stderr.write(`${error.code || 'ERROR'} ${error.message}\n`);
  process.exitCode = 1;
});
