// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package executor

import (
	"fmt"

	"github.com/luxfi/ids"
	"github.com/luxfi/math/set"
	"github.com/luxfi/vm/chains/atomic"
	"github.com/luxfi/node/vms/platformvm/state"
	"github.com/luxfi/node/vms/platformvm/txs"
	"github.com/luxfi/node/vms/platformvm/txs/fee"
)

var _ txs.Visitor = (*atomicTxExecutor)(nil)

// AtomicTx executes the atomic transaction [tx] and returns the resulting state
// modifications.
//
// This is only used to execute atomic transactions pre-AP5. After AP5 the
// execution was moved to [StandardTx].
func AtomicTx(
	backend *Backend,
	feeCalculator fee.Calculator,
	parentID ids.ID,
	stateVersions state.Versions,
	tx *txs.Tx,
) (state.Diff, set.Set[ids.ID], map[ids.ID]*atomic.Requests, error) {
	atomicExecutor := atomicTxExecutor{
		backend:       backend,
		feeCalculator: feeCalculator,
		parentID:      parentID,
		stateVersions: stateVersions,
		tx:            tx,
	}
	if err := tx.Unsigned.Visit(&atomicExecutor); err != nil {
		txID := tx.ID()
		return nil, nil, nil, fmt.Errorf("atomic tx %s failed execution: %w", txID, err)
	}
	return atomicExecutor.onAccept, atomicExecutor.inputs, atomicExecutor.atomicRequests, nil
}

type atomicTxExecutor struct {
	// inputs, to be filled before visitor methods are called
	backend       *Backend
	feeCalculator fee.Calculator
	parentID      ids.ID
	stateVersions state.Versions
	tx            *txs.Tx

	// outputs of visitor execution
	onAccept       state.Diff
	inputs         set.Set[ids.ID]
	atomicRequests map[ids.ID]*atomic.Requests
}

func (*atomicTxExecutor) AddValidatorTx(*txs.AddValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) AddChainValidatorTx(*txs.AddChainValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) AddDelegatorTx(*txs.AddDelegatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) CreateChainTx(*txs.CreateChainTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) CreateNetworkTx(*txs.CreateNetworkTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) AdvanceTimeTx(*txs.AdvanceTimeTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) RewardValidatorTx(*txs.RewardValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) RemoveChainValidatorTx(*txs.RemoveChainValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) TransformChainTx(*txs.TransformChainTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) TransferChainOwnershipTx(*txs.TransferChainOwnershipTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) BaseTx(*txs.BaseTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) ConvertNetworkToL1Tx(*txs.ConvertNetworkToL1Tx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) CreateSovereignL1Tx(*txs.CreateSovereignL1Tx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) RegisterL1ValidatorTx(*txs.RegisterL1ValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) SetL1ValidatorWeightTx(*txs.SetL1ValidatorWeightTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) IncreaseL1ValidatorBalanceTx(*txs.IncreaseL1ValidatorBalanceTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) DisableL1ValidatorTx(*txs.DisableL1ValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) SlashValidatorTx(*txs.SlashValidatorTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) CreateAssetTx(*txs.CreateAssetTx) error {
	return ErrWrongTxType
}

func (*atomicTxExecutor) OperationTx(*txs.OperationTx) error {
	return ErrWrongTxType
}

func (e *atomicTxExecutor) ImportTx(*txs.ImportTx) error {
	return e.atomicTx()
}

func (e *atomicTxExecutor) ExportTx(*txs.ExportTx) error {
	return e.atomicTx()
}

func (e *atomicTxExecutor) atomicTx() error {
	onAccept, err := state.NewDiff(
		e.parentID,
		e.stateVersions,
	)
	if err != nil {
		return err
	}

	e.onAccept = onAccept
	e.inputs, e.atomicRequests, _, err = StandardTx(
		e.backend,
		e.feeCalculator,
		e.tx,
		onAccept,
	)
	return err
}
