package com.izouma.nineth.service;

import com.github.kevinsawicki.http.HttpRequest;
import com.izouma.nineth.domain.Asset;
import com.izouma.nineth.domain.User;
import com.izouma.nineth.dto.NFT;
import com.izouma.nineth.dto.NFTAccount;
import com.izouma.nineth.event.CreateAssetEvent;
import com.izouma.nineth.event.MintedEvent;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.AssetRepo;
import com.izouma.nineth.repo.UserRepo;
import io.ipfs.api.IPFS;
import io.ipfs.api.MerkleNode;
import io.ipfs.api.NamedStreamable;
import io.ipfs.multihash.Multihash;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Arrays;

@Service
@Slf4j
@AllArgsConstructor
public class AssetMintService {
    private AssetRepo          assetRepo;
    private UserRepo           userRepo;
    private NFTService         nftService;
    private ApplicationContext applicationContext;
    private Environment        env;
    private UserService        userService;
    private HCChainService     hcChainService;

    @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 1000))
    public void mint(Long assetId) {
        Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
        if ((asset.getChainFlag() & 0b01) != 0b01) return;
        User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
        if (StringUtils.isEmpty(user.getNftAccount())) {
            NFTAccount account = nftService.createAccount(user.getUsername() + "_");
            user.setNftAccount(account.getAccountId());
            user.setKmsId(account.getAccountKmsId());
            user.setPublicKey(account.getPublicKey());
            userService.save(user);
        }
        try {
            NFT nft = nftService.createToken(user.getNftAccount(), asset.getTokenId());
            if (nft != null) {
                asset.setTokenId(nft.getTokenId());
                asset.setBlockNumber(nft.getBlockNumber());
                asset.setTxHash(nft.getTxHash());
                asset.setGasUsed(nft.getGasUsed());
                if (asset.getIpfsUrl() == null) {
                    asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
                }
                assetRepo.save(asset);
                applicationContext.publishEvent(new CreateAssetEvent(this, true, asset));
            } else {
                log.error("铸造失败");
            }
        } catch (Exception e) {
            log.error("铸造失败", e);
        }
    }

    @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 1000))
    public void hcMint(Long assetId) {
        Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
        if ((asset.getChainFlag() & 0b10) != 0b10) return;
        User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
        if (StringUtils.isEmpty(user.getHcChainAddress())) {
            user.setHcChainAddress(hcChainService.createAccount(user.getId()));
            userService.save(user);
        }
        try {
            NFT nft = hcChainService.mint(user.getHcChainAddress(), asset.getTokenId());
            asset.setHcTokenId(nft.getTokenId());
            asset.setHcBlockNumber(nft.getBlockNumber());
            asset.setHcTxHash(nft.getTxHash());
            asset.setHcGasUsed(nft.getGasUsed());
            if (asset.getIpfsUrl() == null) {
                asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
            }
            assetRepo.save(asset);
            applicationContext.publishEvent(new MintedEvent(this, assetId, nft));
        } catch (Exception e) {
            log.error("铸造失败", e);
        }
    }
//    @Async
//    public void mint(Asset asset) {
//        User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
//        if (StringUtils.isEmpty(user.getPublicKey())) {
//            NFTAccount account = nftService.createAccount(user.getUsername() + "_");
//            user.setNftAccount(account.getAccountId());
//            user.setKmsId(account.getAccountKmsId());
//            user.setPublicKey(account.getPublicKey());
//            userService.save(user);
//        }
//        try {
//            NFT nft = nftService.createToken(user.getNftAccount(), asset.getTokenId());
//            if (nft != null) {
//                asset.setTokenId(nft.getTokenId());
//                asset.setBlockNumber(nft.getBlockNumber());
//                asset.setTxHash(nft.getTxHash());
//                asset.setGasUsed(nft.getGasUsed());
//                if (asset.getIpfsUrl() == null) {
//                    asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
//                }
//                assetRepo.save(asset);
//            }
//        } catch (Exception e) {
//            log.error("铸造失败", e);
//        }
//        applicationContext.publishEvent(new CreateAssetEvent(this, true, asset));
//    }

    public String ipfsUpload(String url) {
        try {
            url = url.replace("raex-meta.oss-cn-shenzhen.aliyuncs.com",
                            "raex-meta.oss-cn-shenzhen-internal.aliyuncs.com")
                    .replace("cdn.raex.vip",
                            "raex-meta.oss-cn-shenzhen-internal.aliyuncs.com");
            String host = "120.24.204.226";
            if (Arrays.asList(env.getActiveProfiles()).contains("prod")) {
                host = "172.29.50.102";
            }
            IPFS ipfs = new IPFS(host, 5001);
            HttpRequest request = HttpRequest.get(url);
            File file = File.createTempFile("ipfs", ".tmp");
            request.receive(file);
            NamedStreamable.FileWrapper file1 = new NamedStreamable.FileWrapper(file);
            MerkleNode put = ipfs.add(file1).get(0);
            Multihash multihash = ipfs.pin.add(put.hash).get(0);
            log.info("上传ipfs成功 {}", multihash.toBase58());
            file.delete();
            return multihash.toBase58();
        } catch (Exception e) {
        }
        return null;
    }
}
