// Copyright 2023 The AthanorLabs/atomic-swap Authors
// SPDX-License-Identifier: LGPL-3.0-only

package main

import (
	"context"
	"crypto/ecdsa"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"time"

	"github.com/athanorlabs/atomic-swap/common"
	"github.com/athanorlabs/atomic-swap/common/vjson"
	contracts "github.com/athanorlabs/atomic-swap/ethereum"
	"github.com/athanorlabs/atomic-swap/ethereum/extethclient"

	ethcommon "github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
)

const (
	contractAddressesFile = "contract-addresses.json"
)

var (
	errNoEthPrivateKey = fmt.Errorf("must provide --%s file for non-development environment", flagEthPrivKey)
)

type contractAddresses struct {
	SwapCreatorAddr ethcommon.Address `json:"swapCreatorAddr" validate:"required"`
}

func getOrDeploySwapCreator(
	ctx context.Context,
	swapCreatorAddr ethcommon.Address,
	env common.Environment,
	dataDir string,
	ec extethclient.EthClient,
) (ethcommon.Address, error) {
	var err error
	if (swapCreatorAddr == ethcommon.Address{}) {
		if env == common.Mainnet {
			log.Warnf("you are deploying SwapCreator.sol on mainnet! giving you a few seconds to cancel if this is unintended")
			time.Sleep(10 * time.Second)
		}

		swapCreatorAddr, err = deploySwapCreator(ctx, ec.Raw(), ec.PrivateKey(), dataDir)
		if err != nil {
			return ethcommon.Address{}, fmt.Errorf("failed to deploy swap creator: %w", err)
		}
	} else {
		// otherwise, load the contract from the given address
		// and check that its bytecode is valid (ie. matches the
		// bytecode of this repo's swap contract)
		err = contracts.CheckSwapCreatorContractCode(ctx, ec.Raw(), swapCreatorAddr)
		if err != nil {
			return ethcommon.Address{}, err
		}
	}

	return swapCreatorAddr, nil
}

func deploySwapCreator(
	ctx context.Context,
	ec *ethclient.Client,
	privkey *ecdsa.PrivateKey,
	dataDir string,
) (ethcommon.Address, error) {
	if privkey == nil {
		return ethcommon.Address{}, errNoEthPrivateKey
	}

	swapCreatorAddr, _, err := contracts.DeploySwapCreatorWithKey(ctx, ec, privkey)
	if err != nil {
		return ethcommon.Address{}, err
	}

	// store the contract addresses on disk
	err = writeContractAddressesToFile(
		path.Join(dataDir, contractAddressesFile),
		&contractAddresses{
			SwapCreatorAddr: swapCreatorAddr,
		},
	)
	if err != nil {
		return ethcommon.Address{}, fmt.Errorf("failed to write contract address to file: %w", err)
	}

	return swapCreatorAddr, nil
}

// writeContractAddressesToFile writes the contract addresses to the given file
func writeContractAddressesToFile(filePath string, addresses *contractAddresses) error {
	jsonData, err := vjson.MarshalIndentStruct(addresses, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(filepath.Clean(filePath), jsonData, 0600)
}
