package com.wuban.tron.explore.controller;

import com.alibaba.fastjson.JSON;
import com.wuban.tron.explore.domain.TronTransEvent;
import com.wuban.tron.explore.domain.TronTransEventResult;
import com.wuban.tron.explore.domain.TronTransEventResultType;
import com.wuban.tron.explore.entity.Contract;
import com.wuban.tron.explore.entity.ContractEvent;
import com.wuban.tron.explore.entity.SolidityVersion;
import com.wuban.tron.explore.entity.example.ContractEventExample;
import com.wuban.tron.explore.entity.example.ContractExample;
import com.wuban.tron.explore.param.request.ContractCompilerRequest;
import com.wuban.tron.explore.param.request.ContractRequest;
import com.wuban.tron.explore.param.response.ContractModel;
import com.wuban.tron.explore.service.*;
import com.wuban.tron.explore.util.ApiResponse;
import com.wuban.tron.explore.util.BizExceptionEnum;
import com.wuban.tron.explore.util.ResponseKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.tron.common.utils.ByteArray;
import org.tron.walletserver.WalletApi;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;

/**
 * <core>合约校验API接口</core>
 *
 * @author sky
 * @date 2020/11/30
 */
@Slf4j
@RestController
@RequestMapping("/api/explorer/v1/")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ContractController {

    private final ContractService contractService;

    private final TronService tronService;

    private final ContractCompilerService contractCompilerService;

    private final SolidityVersionService solidityVersionService;

    private final ContractEventService contractEventService;

    @RequestMapping(value="getVersion", method = RequestMethod.GET)
    public ApiResponse getVersion() {
        List<SolidityVersion> list = this.solidityVersionService.selectByExample(null);
        return ResponseKit.success(list);
    }

    @RequestMapping(value="checkContract/{address}", method = RequestMethod.GET)
    public ApiResponse checkContract(@PathVariable("address") String address) {

        if (StringUtils.isEmpty(address)) {
            return ResponseKit.fail(BizExceptionEnum.REQUEST_NULL.getCode(), BizExceptionEnum.REQUEST_NULL.getMessage());
        }

        /*
            按照合约地址查询DB中是否存在此合约
         */
        ContractExample example = new ContractExample();
        example.createCriteria().andContractAddressEqualTo(address);
        Contract con = this.contractService.selectOneByExample(example);

        // 未编译
        int isCompiler = 0;
        if (con != null) {
            isCompiler = 1;
        }

        return ResponseKit.success(isCompiler);
    }

    @RequestMapping(value="getContractInfoById/{address}", method = RequestMethod.GET)
    public ApiResponse getContractInfoById(@PathVariable("address") String address) {
        if (StringUtils.isEmpty(address)) {
            return ResponseKit.fail(BizExceptionEnum.REQUEST_NULL.getCode(), BizExceptionEnum.REQUEST_NULL.getMessage());
        }

        ContractExample example = new ContractExample();
        example.createCriteria().andContractAddressEqualTo(address);
        Contract con = this.contractService.selectOneByExample(example);

        ContractModel model = new ContractModel();
        if (con != null) {
            BeanUtils.copyProperties(con, model, "id");
            model.setId(con.getContractAddress());
            model.setAddress(con.getContractAddress());
            model.setCode(con.getBytecode());
        }

        return ResponseKit.success(model);
    }

    @RequestMapping(value="compileSolidityOne", method = RequestMethod.POST)
    public ApiResponse compileSolidityOne(@RequestBody @Valid ContractModel reqParam) {
        boolean flag = false;

        // 编译合约、获取code
        String code = this.contractCompilerService.compiler(reqParam.getName(), reqParam.getValue(), reqParam.getCompiler());

        // 根据合约地址获取合约内容
        String hexString = ByteArray.toHexString(WalletApi.decodeFromBase58Check(reqParam.getAddress()));
        Contract con = this.tronService.getContract(hexString);

        // 验证合约code，如果一致，持久化到数据库中
        if (con != null && !StringUtils.isEmpty(con.getBytecode())) {
           /* if (con.getBytecode().equals(code)) {
                Contract contract = new Contract();
                BeanUtils.copyProperties(reqParam, contract);
                if (contract != null) {
                    this.contractService.insert(contract);
                }
                flag = true;
            }*/
            con.setName(reqParam.getName());
            con.setContractAddress(reqParam.getAddress());
            con.setVersion(reqParam.getCompiler());
            con.setOptimization(reqParam.getOptimization());
            con.setSource(reqParam.getValue());
            con.setRuns(reqParam.getRuns());
            con.setLicenseType(reqParam.getLicense_type());
            con.setEvmVersion(reqParam.getEvm_version());
            if (con != null) {
                ContractExample example = new ContractExample();
                example.createCriteria().andContractAddressEqualTo(reqParam.getAddress());
                Contract obj = this.contractService.selectOneByExample(example);
                if (obj == null) {
                    this.contractService.insert(con);
                }
            }
            flag = true;
        }

        return ResponseKit.success(flag);
    }

    @RequestMapping(value="compileSolidity", method = RequestMethod.POST)
    public ApiResponse compileSolidity(@RequestBody @Valid ContractCompilerRequest reqParam) {
        String code = this.contractCompilerService.compiler(reqParam.getName(), reqParam.getValue(), reqParam.getCompiler());
        return ResponseKit.success(code);
    }

    @RequestMapping(value="getEvent/{address}", method = RequestMethod.GET)
    public ApiResponse getEvent(@PathVariable("address") String address) {
        ContractEventExample example = new ContractEventExample();
        example.createCriteria().andContractAddressEqualTo(address);
        List<ContractEvent> list = this.contractEventService.selectByExample(example);

        List<TronTransEvent> retList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(o -> {
                TronTransEvent event = new TronTransEvent();
                event.setBlock_number(o.getBlockNumber());
                event.setBlock_timestamp(o.getBlockTimestamp());
                event.setCaller_contract_address(o.getCallerContractAddress());
                event.setEvent_index(o.getEventIndex());
                event.setEvent_name(o.getEventName());
                event.setContract_address(o.getContractAddress());
                event.setTransaction_id(o.getTransactionId());

                if (!StringUtils.isEmpty(o.getResult())) {
                    TronTransEventResult result = JSON.parseObject(o.getResult(), TronTransEventResult.class);
                    event.setResult(result);
                }

                if(!StringUtils.isEmpty(o.getResultType())) {
                    TronTransEventResultType resultType = JSON.parseObject(o.getResultType(), TronTransEventResultType.class);
                    event.setResult_type(resultType);
                }
                retList.add(event);
            });
        }

        return ResponseKit.success(retList);
    }













    @Deprecated
    @RequestMapping(value="compileSolidityAll", method = RequestMethod.POST)
    public ApiResponse compileSolidityAll(@RequestBody @Valid ContractRequest reqParam) {

        String address = reqParam.getAddress();
        if (StringUtils.isEmpty(address)) {
            return ResponseKit.fail(BizExceptionEnum.REQUEST_NULL.getCode(),BizExceptionEnum.REQUEST_NULL.getMessage());
        }

        /*
            按照合约地址查询DB中是否存在此合约，如果不存在，调用链上接口API
         */
        ContractExample example = new ContractExample();
        example.createCriteria().andContractAddressEqualTo(address);
        Contract con = this.contractService.selectOneByExample(example);
        if (con != null) {
            return ResponseKit.success(con);
        }

        // 调用链上接口API
        Contract contract = this.tronService.getContract(address);
        if (contract == null) {
            return ResponseKit.fail(BizExceptionEnum.SERVER_ERROR.getCode(),BizExceptionEnum.SERVER_ERROR.getMessage());
        }

        // 编译合约
        String code = this.contractCompilerService.compiler(reqParam.getName(), reqParam.getValue(), reqParam.getCompiler());

        // 校验btyecode是否一致，不一致返回，一致持久化
        if (!contract.getBytecode().equals(code)) {
            return ResponseKit.fail(BizExceptionEnum.SERVER_ERROR.getCode(),BizExceptionEnum.SERVER_ERROR.getMessage());
        }

        this.contractService.insert(contract);

        return ResponseKit.success();
    }

}
