package com.wuban.tron.explore.controller.v1;

import com.github.pagehelper.PageInfo;
import com.wuban.tron.explore.constant.Constant;
import com.wuban.tron.explore.constant.HomeSearchTypeEnum;
import com.wuban.tron.explore.entity.*;
import com.wuban.tron.explore.entity.example.AddressExample;
import com.wuban.tron.explore.entity.example.BlockDayCensusExample;
import com.wuban.tron.explore.entity.example.BlockHeaderExample;
import com.wuban.tron.explore.entity.example.TransactionExample;
import com.wuban.tron.explore.param.response.*;
import com.wuban.tron.explore.service.*;
import com.wuban.tron.explore.util.ApiResponse;
import com.wuban.tron.explore.util.BigDecimalUtil;
import com.wuban.tron.explore.util.DateUtil;
import com.wuban.tron.explore.util.ResponseKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <core>波场浏览器API接口</core>
 *
 * @author sky
 * @date 2020/11/02
 */
@Slf4j
@RestController
@RequestMapping("/api/explorer/v1/")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class TransactionControllerV1 {

    private final TransactionService transactionService;

    private final BlockDayCensusService blockDayCensusService;

    private final BlockHeaderService blockHeaderService;

    private final AddressService addressService;

    private final LastBlockService lastBlockService;

    private final CensusService censusService;

    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 首页 - 搜索
     *
     * @param condition
     * @return
     */
    @RequestMapping(value="indexGetInfoByCondition/{condition}", method = RequestMethod.GET)
    public ApiResponse search(@PathVariable("condition") String condition) {
        if (StringUtils.isEmpty(condition)) {
            return ResponseKit.success();
        }
        condition = StringUtils.trimAllWhitespace(condition);

        /*
            块查询
         */
        if (isNumeric(condition)) {
            ResDataModel<BlockInfoModel> resDataModel = new ResDataModel<>();
            BlockHeaderExample headerExample = new BlockHeaderExample();
            headerExample.createCriteria().andNumberEqualTo(Long.valueOf(condition));
            BlockHeader blockHeader = this.blockHeaderService.getOneByExample(headerExample);
            BlockInfoModel model = BlockInfoModel.getInstance();
            if (blockHeader != null) {
                model.setNumber(blockHeader.getNumber());
                if (blockHeader.getTimestamp() != null) {
                    Long tt = blockHeader.getTimestamp()/1000;
                    model.setTimestamp(tt.toString());
                }
                model.setParentHash(blockHeader.getParentHash());
                model.setTrans_number(blockHeader.getTransactionVolume());
                model.setSize(blockHeader.getBlockBytes());
            }
            resDataModel.setData(model);
            resDataModel.setT(HomeSearchTypeEnum.BLOCK_INFO.getCode());
            return ResponseKit.success(resDataModel);
        }

         /*
            账户地址
         */
        if (condition.length() == Constant.USER_ADDRESS_LEN) {
            ResDataModel<AddessInfoModel> resDataModel = new ResDataModel<>();
            AddessInfoModel model = AddessInfoModel.getInstance();

            AddressExample addressExample = new AddressExample();
            addressExample.createCriteria().andAddressEqualTo(condition);
            Address address = this.addressService.selectOneByExample(addressExample);
            if (address != null) {
                model.setAddress(address.getAddress());
                // 账户余额 = 可用余额+冻结余额
                BigDecimal accountBalance = new BigDecimal(0);
                if (address.getBalance() != null) {
                    accountBalance = BigDecimalUtil.getAdd(accountBalance, new BigDecimal(address.getBalance()));
                }
                if (address.getFrozenBalance() != null) {
                    accountBalance = BigDecimalUtil.getAdd(accountBalance, new BigDecimal(address.getFrozenBalance()));
                }
                model.setBalance(accountBalance.toPlainString());
            }
            resDataModel.setData(model);
            resDataModel.setT(HomeSearchTypeEnum.ADDRESS_INFO.getCode());
            return ResponseKit.success(resDataModel);
        }

        /*
            交易hash
         */
        if (condition.length()== Constant.TX_ID_LEN) {
            ResDataModel<AddessInfoModel> resDataModel = new ResDataModel<>();
            TransactionExample example = new TransactionExample();
            example.createCriteria().andTxIdEqualTo(condition);
            List<Transaction> txList = this.transactionService.getByExample(example);
            List<TransInfoModel> modelList = transferTransInfoModel(txList);
            TransInfoModel model = modelList.get(0);
            /*if (!CollectionUtils.isEmpty(txList)) {
                Transaction trans = txList.get(0);
                model.setHash(trans.getTxId());
                model.setBlockHash(trans.getBlockId());
                model.setBlockNumber(trans.getNumber().intValue());
                model.setFrom(trans.getOwnerAddress());
                if (!StringUtils.isEmpty(trans.getContractAddress())) {
                    model.setTo(trans.getContractAddress());
                } else {
                    model.setTo(trans.getToAddress());
                }

                if (trans.getTimestamp() != null) {
                    Long tt = trans.getTimestamp()/1000;
                    model.setTimestamp(tt.toString());
                }
            }*/
            resDataModel.setData(model);
            resDataModel.setT(HomeSearchTypeEnum.TRANSACTION_INFO.getCode());
            return ResponseKit.success(resDataModel);
        }

        return ResponseKit.success();
    }

    /**
     * 首页 - Latest Blocks
     *
     * @return
     */
    @RequestMapping(value="indexGetBlockInfo", method = RequestMethod.GET)
    public ApiResponse lastBlockList() {
        BlockHeaderExample headerExample = new BlockHeaderExample();
        PageInfo<BlockHeader> pageInfo = this.blockHeaderService.getByPagerEx(1, 11, headerExample);
        List<BlockInfoModel> list = transferBlockInfoModelV2(pageInfo);

        return ResponseKit.success(list);
    }

    /**
     * 首页 - All Blocks
     *
     * @return
     */
    @RequestMapping(value="getBlockInfo/{pageNo}/{pageSize}", method = RequestMethod.GET)
    public ApiResponse allBlockList(@PathVariable("pageNo") Integer pageNo, @PathVariable("pageSize") Integer pageSize) {
        BlockHeaderExample headerExample = new BlockHeaderExample();
        PageInfo<BlockHeader> pageInfo = this.blockHeaderService.getByPagerEx(pageNo, pageSize, headerExample);
        List<BlockInfoModel> list = transferBlockInfoModel(pageInfo);

        ResDataModel<BlockInfoModel> model = new ResDataModel<>();
        model.setData(list);
        model.setTotal(Integer.valueOf(pageInfo.getTotal()+""));
        return ResponseKit.success(model);
    }

    private List<BlockInfoModel> transferBlockInfoModelV2(PageInfo<BlockHeader> pageInfo) {
        List<BlockInfoModel> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(pageInfo.getList())) {
            for (int i = 0; i < pageInfo.getList().size() - 1; i++) {
                BlockHeader o = pageInfo.getList().get(i);
                BlockInfoModel model = BlockInfoModel.getInstance();
                model.setNumber(o.getNumber());

                BlockHeader pre = pageInfo.getList().get(i+1);
                Long preTime = pre.getTimestamp()/1000;
                model.setMined_time(preTime.toString());
                if (o.getTimestamp() != null) {
                    Long tt = o.getTimestamp()/1000;
                    model.setTimestamp(tt.toString());
                }
                model.setMiner(o.getWitnessAddress());
                model.setParentHash(o.getParentHash());
                model.setHash(o.getBlockId());
                model.setTrans_number(o.getTransactionVolume());
                model.setSize(o.getBlockBytes());
                list.add(model);
            }
        }

        return list;
    }

    private List<BlockInfoModel> transferBlockInfoModel(PageInfo<BlockHeader> pageInfo) {
        List<BlockInfoModel> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(pageInfo.getList())) {
            pageInfo.getList().forEach(o -> {
                BlockInfoModel model = BlockInfoModel.getInstance();
                model.setNumber(o.getNumber());
                if (o.getTimestamp() != null) {
                    Long tt = o.getTimestamp()/1000;
                    model.setTimestamp(tt.toString());
                }
                model.setParentHash(o.getParentHash());
                model.setHash(o.getBlockId());
                model.setTrans_number(o.getTransactionVolume());
                model.setSize(o.getBlockBytes());
                list.add(model);
            });
        }

        return list;
    }

    /**
     * 首页 - Latest Transactions
     *
     * @return
     */
    @RequestMapping(value="indexGetTxInfo", method = RequestMethod.GET)
    public ApiResponse lastTransList() {
        PageInfo<Transaction> pageInfo = this.transactionService.getByPageWithCategory(null, null, new TransactionExample());

        List<TransInfoModel> modelList = transferTransInfoModel(pageInfo.getList());

        return ResponseKit.success(modelList);
    }

    /**
     * 首页 - All Transactions
     *
     * @return
     */
    @RequestMapping(value="getTxInfo/{pageNo}/{pageSize}", method = RequestMethod.GET)
    public ApiResponse allTransList(@PathVariable("pageNo") Integer pageNo, @PathVariable("pageSize") Integer pageSize) {
        PageInfo<Transaction> pageInfo = this.transactionService.getByPageWithCategory(pageNo, pageSize, new TransactionExample());

        List<TransInfoModel> modelList = transferTransInfoModel(pageInfo.getList());
        ResDataModel<TransInfoModel> resDataModel = new ResDataModel<>();
        resDataModel.setTotal(Integer.valueOf(pageInfo.getTotal()+""));
        resDataModel.setData(modelList);

        return ResponseKit.success(resDataModel);
    }

    private List<TransInfoModel> transferTransInfoModel(List<Transaction> txList) {
        List<TransInfoModel> modelList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(txList)) {
            txList.forEach(o -> {
                TransInfoModel model = TransInfoModel.getInstance();
                model.setHash(o.getTxId());
                model.setBlockHash(o.getTxId());
                model.setBlockNumber(o.getNumber().intValue());
                model.setFrom(o.getOwnerAddress());
                BlockInfoModel blockInfoModel = BlockInfoModel.getInstance();
                blockInfoModel.setNumber(o.getNumber().intValue());
                model.setBlockNumber(blockInfoModel);
                if (!StringUtils.isEmpty(o.getContractAddress())) {
                    model.setTo(o.getContractAddress());
                } else {
                    model.setTo(o.getToAddress());
                }

                if (o.getTimestamp() != null) {
                    Long tt = o.getTimestamp()/1000;
                    model.setTimestamp(tt.toString());
                }
                modelList.add(model);
            });
        }

        return modelList;
    }

    /**
     * 区块列表-通过区块高度查询区块详情
     *
     * @return
     */
    @RequestMapping(value="getBlockDetailInfoByBlock/{block}", method = RequestMethod.GET)
    public ApiResponse getBlockDetailInfoByBlock(@PathVariable("block") String block) {
        BlockHeaderExample headerExample = new BlockHeaderExample();
        headerExample.createCriteria().andNumberEqualTo(Long.valueOf(block));
        BlockHeader blockHeader = this.blockHeaderService.getOneByExample(headerExample);
        BlockInfoModel model = BlockInfoModel.getInstance();
        if (blockHeader != null) {
            model.setNumber(blockHeader.getNumber());
            if (blockHeader.getTimestamp() != null) {
                Long tt = blockHeader.getTimestamp()/1000;
                model.setTimestamp(tt.toString());
            }
            model.setParentHash(blockHeader.getParentHash());
            model.setTrans_number(blockHeader.getTransactionVolume());
            model.setSize(blockHeader.getBlockBytes());
        }

        return ResponseKit.success(model);
    }

    /**
     * 区块列表-通过区块高度查询交易列表
     *
     * @return
     */
    @RequestMapping(value="getTxInfoByBlock/{block}/{pageNo}/{pageSize}", method = RequestMethod.GET)
    public ApiResponse getTxInfoByBlock(@PathVariable("block") String block, @PathVariable("pageNo") Integer pageNo, @PathVariable("pageSize") Integer pageSize) {
        TransactionExample example = new TransactionExample();
        example.createCriteria().andNumberEqualTo(Long.valueOf(block));
        PageInfo<Transaction> pageInfo = this.transactionService.getByPageWithCategory(pageNo, pageSize, example);

        List<TransInfoModel> modelList = transferTransInfoModel(pageInfo.getList());
        ResDataModel<TransInfoModel> resDataModel = new ResDataModel<>();
        resDataModel.setTotal(Integer.valueOf(pageInfo.getTotal()+""));
        resDataModel.setData(modelList);

        return ResponseKit.success(resDataModel);
    }

    /**
     * 区块列表-通过地址查询账户信息
     *
     * @return
     */
    @RequestMapping(value="getAddressInfoByAddress/{address}", method = RequestMethod.GET)
    public ApiResponse getAddressInfoByAddress(@PathVariable("address") String address) {
        AddessInfoModel model = AddessInfoModel.getInstance();
        AddressExample addressExample = new AddressExample();
        addressExample.createCriteria().andAddressEqualTo(address);
        Address obj = this.addressService.selectOneByExample(addressExample);
        if (obj != null) {
            model.setAddress(obj.getAddress());
            model.setBalance(obj.getBalance()+"");
        }

        return ResponseKit.success(model);
    }

    /**
     * 交易列表-通过交易哈希查询交易详情
     *
     * @return
     */
    @RequestMapping(value="getTxDetailInfoByHash/{hash}", method = RequestMethod.GET)
    public ApiResponse getTxDetailInfoByHash(@PathVariable("hash") String hash) {
        TransInfoModel model = TransInfoModel.getInstance();
        TransactionExample example = new TransactionExample();
        example.createCriteria().andTxIdEqualTo(hash);
        List<Transaction> txList = this.transactionService.getByExample(example);
        if (!CollectionUtils.isEmpty(txList)) {
            Transaction trans = txList.get(0);
            model.setHash(trans.getTxId());
            model.setBlockHash(trans.getBlockId());
            model.setBlockNumber(trans.getNumber().intValue());
            model.setFrom(trans.getOwnerAddress());
            model.setInput(trans.getData());
            if (!StringUtils.isEmpty(trans.getContractAddress())) {
                model.setTo(trans.getContractAddress());
            } else {
                model.setTo(trans.getToAddress());
            }

            if (trans.getTimestamp() != null) {
                Long tt = trans.getTimestamp()/1000;
                model.setTimestamp(tt.toString());
            }
        }

        return ResponseKit.success(model);
    }

    /**
     * 地址详情页-交易列表
     *
     * @return
     */
    @RequestMapping(value="getTxInfoByAddress/{address}", method = RequestMethod.GET)
    public ApiResponse getTxInfoByAddress(@PathVariable("address") String address) {
        //ResDataModel<TransInfoModel> resDataModel = new ResDataModel<>();
        PageInfo<Transaction> pageInfo = this.transactionService.selectPageByAddress(address, 1, 25);
        List<TransInfoModel> modelList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(pageInfo.getList())) {
            modelList = transferTransInfoModel(pageInfo.getList());
        }

        return ResponseKit.success(modelList);
    }

    /**
     * 账户余额排行榜
     *
     * @return
     */
    @RequestMapping(value="getTopAccountsInfo/{pageNo}/{pageSize}", method = RequestMethod.GET)
    public ApiResponse getTopAccountsInfo(@PathVariable("pageNo") Integer pageNo, @PathVariable("pageSize") Integer pageSize) {
        ResDataModel<TransInfoModel> resDataModel = new ResDataModel<>();
        AddressExample example = new AddressExample();
        example.setOrderByClause("balance desc");
        PageInfo<Address> pageInfo = this.addressService.selectByPager(pageNo, pageSize, example);
        resDataModel.setTotal(Integer.parseInt(pageInfo.getTotal()+""));
        List<AccountInfoModel> modelList = new ArrayList<>();

        if (!CollectionUtils.isEmpty(pageInfo.getList())) {
            pageInfo.getList().forEach(o -> {
                AccountInfoModel infoModel = AccountInfoModel.getInstance();
                infoModel.setAddress(o.getAddress());

                /*
                    BigDecimal balance = BigDecimalUtil.getDevide(new BigDecimal(o.getBalance()), BigDecimalUtil.NUM);
                    账户余额 = 可用余额+冻结余额
                 */
                BigDecimal accountBalance = new BigDecimal(0);
                if (o.getBalance() != null) {
                    accountBalance = BigDecimalUtil.getAdd(accountBalance, new BigDecimal(o.getBalance()));
                }
                if (o.getFrozenBalance() != null) {
                    accountBalance = BigDecimalUtil.getAdd(accountBalance, new BigDecimal(o.getFrozenBalance()));
                }
                infoModel.setBalance(accountBalance.toPlainString());
                modelList.add(infoModel);
            });
        }
        BigDecimal bg = this.totalBalance();

        resDataModel.setSum(bg.toPlainString());
        resDataModel.setData(modelList);

        return ResponseKit.success(resDataModel);
    }

    private BigDecimal totalBalance() {
        AddressExample example = new AddressExample();
        example.createCriteria().andBalanceGreaterThan(0L);
        List<Address> list = this.addressService.selectByExample(example);
        BigDecimal totalBalance = new BigDecimal(0);
        if (!CollectionUtils.isEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).getBalance() != null) {
                    totalBalance = BigDecimalUtil.getAdd(totalBalance, new BigDecimal(list.get(i).getBalance()));
                }
            }
        }

        return totalBalance;
        //return BigDecimalUtil.getDevide(totalBalance, BigDecimalUtil.NUM);
    }

    /**
     * 首页图表
     *
     * @return
     */
    @RequestMapping(value="indexGetChartInfo", method = RequestMethod.GET)
    public ApiResponse indexGetChartInfo() {
        PageInfo<BlockDayCensus> pageInfo = this.blockDayCensusService.getByPageWithCategory(1, 14, new BlockDayCensusExample());

        List<HomeChartModel> modelList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(pageInfo.getList())) {
            List<BlockDayCensus> list = pageInfo.getList();
            HomeChartModel model;
            int id = 1;
            for (int i = list.size() - 1; i >=0; i--) {
                model = new HomeChartModel();
                model.setId(id);
                long time = DateUtil.getDateFromDateStr(list.get(i).getCensusDate(), DateUtil.PATTERN_YMD);
                model.setTime(time/1000);
                model.setCount(list.get(i).getTotalVolume());
                modelList.add(model);
                id++;
            }
        }

        return ResponseKit.success(modelList);
    }

    /**
     * 获取最新块高度
     *
     * @return
     */
    @RequestMapping(value="getBlockNumber", method = RequestMethod.GET)
    public ApiResponse getBlockNumber() {
        LastBlock lastBlock = this.lastBlockService.getOneByExample();
        return ResponseKit.success(lastBlock.getLastBlockNum());
    }

    /**
     * 图表
     *
     * @return
     */
    @RequestMapping(value="indexGetChartDetailInfo", method = RequestMethod.GET)
    public ApiResponse indexGetChartDetailInfo() {
        List<BlockDayCensus> list = this.blockDayCensusService.getByExample(new BlockDayCensusExample());

        List<ChartModel> modelList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            ChartModel model;
            int id = 1;
            for (int i = 0; i < list.size(); i++) {
                model = ChartModel.getInstance();
                model.setId(id);
                long time = DateUtil.getDateFromDateStr(list.get(i).getCensusDate(), DateUtil.PATTERN_YMD);
                model.setTime(time/1000);
                model.setValue(list.get(i).getTotalVolume());
                model.setAvg_block_time(list.get(i).getGenBlockAverTime());
                model.setAvg_block_size(list.get(i).getTotalBlockBytes());
                model.setTotal_block_count(list.get(i).getGenBlockTotalNum());
                modelList.add(model);
                id++;
            }
        }

        return ResponseKit.success(modelList);
    }

    /**
     * 交易TPS
     *
     * @return
     */
    @RequestMapping(value="/indexStatistical", method = RequestMethod.GET)
    public ApiResponse transTPS() {
        CensusModel censusModel = new CensusModel();

        // 当前毫秒
        long curMillis = System.currentTimeMillis();

        // 上一分钟毫秒
        long millis = curMillis - (60*1000);

        /*
            计算上一分钟交易tps
         */
        /*TransactionExample example = new TransactionExample();
        example.createCriteria().andTimestampBetween(millis, curMillis);
        List<Transaction> list = this.transactionService.getByExample(example);
        BigDecimal tps = new BigDecimal(0);
        if (!StringUtils.isEmpty(list)) {
            tps = BigDecimalUtil.getDevide(1, new BigDecimal(list.size()), new BigDecimal(60));
        }
        censusModel.setTps(tps.toPlainString());*/
        String tpsPeakStr = this.stringRedisTemplate.opsForValue().get(Constant.TRANS_TPS_PEAK);
        Integer tpsPeak = 0;
        if (tpsPeakStr != null) {
            tpsPeak = Integer.valueOf(tpsPeakStr);
        }
        censusModel.setTps(tpsPeak.toString());

        // 最新区块高度
        LastBlock lastBlock = this.lastBlockService.getOneByExample();
        if (lastBlock != null) {
            if (lastBlock.getLastBlockNum() != null) {
                censusModel.setBlockNumber(lastBlock.getLastBlockNum().toString());
            }
        }

        // 区块、交易、账户数统计
        Census census = this.censusService.selectOneByExample();
        if (census != null) {
            if (census.getTotalAccount() != null) {
                censusModel.setAddressNumber(census.getTotalAccount().toString());
            }

            if (census.getTotalBlockSize() != null) {
                censusModel.setBlockSize(census.getTotalBlockSize().toString());
            }

            if (census.getTotalTrans() != null){
                censusModel.setTransTotal(census.getTotalTrans().toString());
            }
        }

        // 前一天交易数
        BlockDayCensusExample example1 = new BlockDayCensusExample();
        Date date = DateUtil.addDays(new Date(), -1);
        String censusDate = DateUtil.getFormatDate(date, DateUtil.PATTERN_YMD);
        example1.createCriteria().andCensusDateEqualTo(censusDate);
        List<BlockDayCensus> tmpList = this.blockDayCensusService.getByExample(example1);
        if (!CollectionUtils.isEmpty(tmpList)) {
            Integer totalVolume = tmpList.get(0).getTotalVolume();
            censusModel.setTransNumber(totalVolume.toString());
        }

        /*
            censusModel.setDifficulty("");
         */
        return ResponseKit.success(censusModel);
    }

    public final static boolean isNumeric(String s) {
        if (s != null && !"".equals(s.trim())) {
            return s.matches("^[0-9]*$");
        } else {
            return false;
        }
    }

}
