package leigh.chain.pki.service;

import leigh.chain.BlockScanner;
import leigh.chain.BlockTranHandler;
import leigh.chain.ReplayOption;
import leigh.chain.directory.DirectoryStateMachine;
import leigh.chain.directory.DirectoryStore;
import leigh.chain.directory.Identity;
import leigh.chain.directory.RamDirectoryStore;
import leigh.chain.pki.NameClaim;
import leigh.chain.pki.PKIAddresses;
import leigh.mecha.json.JSONObject;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.util.UniversalJob;
import org.bouncycastle.util.encoders.Hex;
import org.web3j.protocol.core.methods.response.EthBlock;

import java.io.*;
import java.time.Instant;

/**
 * @author Alex Leigh
 */
public class PKIService {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(PKIService.class);
    private final DirectoryStateMachine dsm;

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        UniversalJob.banner(logger, "PKIService");

        // PKIService svc = new PKIService("http://10.52.4.6:8545");
        // svc.saveToFile(new File("dsm.dat"));

        PKIService svc = new PKIService(new File("dsm.dat"));
        svc.dump();
        System.exit(UniversalJob.RET_OK);
    }

    public DirectoryStore getStore() {
        return dsm.getStore();
    }

    public PKIService(String rpcUrl) {
        dsm = new DirectoryStateMachine(new RamDirectoryStore());
        loadFromRPC(rpcUrl);
    }

    public PKIService(File file) throws IOException, ClassNotFoundException {
        try (ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) {
            dsm = (DirectoryStateMachine) ois.readObject();
        }
    }

    public void dump() {
        DirectoryStore ds = dsm.getStore();
        for (String s : ds.getNames()) {
            Identity id = ds.getIdentityByName(s, Instant.now());
            logger.info("Name. {}", id);
        }
    }

    public void saveToFile(File file) throws IOException {
        try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))) {
            oos.writeObject(dsm);
        }
        logger.info("Saved DSM. [file: {}]", file);
    }

    public void loadFromRPC(String rpcUrl) {
        BlockScanner scanner = new BlockScanner(rpcUrl, ReplayOption.PAST, new BlockTranHandler() {
            @Override
            public void event(EthBlock.Block block, EthBlock.TransactionObject txn) {
                logger.info("TXN [to: {}] [value: {}]", txn.getTo(), txn.getValue());

                if (PKIAddresses.OP_NAME_CLAIM.equals(txn.getTo())) {
                    try {
                        // Remove the leading 0x
                        byte[] d = Hex.decode(txn.getInput().substring(2));
                        JSONObject obj = new JSONObject(new String(d));

                        String from = txn.getFrom();
                        String name = obj.getString(NameClaim.KEY_NAME);
                        String serviceUrl = obj.getString(NameClaim.KEY_SERVICE_URL);
                        int validityPeriod = obj.getInt(NameClaim.KEY_VALIDITY_DAYS);
                        Instant when = Instant.ofEpochSecond(block.getTimestamp().longValue());

                        if (from != null && name != null && serviceUrl != null && validityPeriod > 0
                                && validityPeriod <= NameClaim.MAX_VALIDITY) {
                            dsm.processNameClaim(from, name, validityPeriod, serviceUrl, when);
                        }
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        scanner.scan();
        logger.info("Scan finished.");
    }
}
