// Copyright (c) 2017-2026 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// SPDX-License-Identifier: EUPL-1.2

//go:build !integration

package consensus

import (
	"testing"

	"dappco.re/go/core/blockchain/config"
	"dappco.re/go/core/blockchain/types"
)

// validV2Tx returns a minimal valid v2 (Zarcanum) transaction for testing.
func validV2Tx() *types.Transaction {
	return &types.Transaction{
		Version: types.VersionPostHF4,
		Vin: []types.TxInput{
			types.TxInputZC{
				KeyImage: types.KeyImage{1},
			},
		},
		Vout: []types.TxOutput{
			types.TxOutputZarcanum{StealthAddress: types.PublicKey{1}},
			types.TxOutputZarcanum{StealthAddress: types.PublicKey{2}},
		},
	}
}

// validV3Tx returns a minimal valid v3 (HF5) transaction for testing.
func validV3Tx() *types.Transaction {
	return &types.Transaction{
		Version:    types.VersionPostHF5,
		HardforkID: 5,
		Vin: []types.TxInput{
			types.TxInputZC{
				KeyImage: types.KeyImage{1},
			},
		},
		Vout: []types.TxOutput{
			types.TxOutputZarcanum{StealthAddress: types.PublicKey{1}},
			types.TxOutputZarcanum{StealthAddress: types.PublicKey{2}},
		},
	}
}

func TestCheckTxVersion_Good(t *testing.T) {
	tests := []struct {
		name   string
		tx     *types.Transaction
		forks  []config.HardFork
		height uint64
	}{
		// v1 transaction before HF4 — valid.
		{"v1_before_hf4", validV1Tx(), config.MainnetForks, 5000},
		// v2 transaction after HF4, before HF5 — valid.
		{"v2_after_hf4_before_hf5", validV2Tx(), config.TestnetForks, 150},
		// v3 transaction after HF5 — valid.
		{"v3_after_hf5", validV3Tx(), config.TestnetForks, 250},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := checkTxVersion(tt.tx, newTxForkState(tt.forks, tt.height), tt.height)
			if err != nil {
				t.Errorf("checkTxVersion returned unexpected error: %v", err)
			}
		})
	}
}

func TestCheckTxVersion_Bad(t *testing.T) {
	tests := []struct {
		name   string
		tx     *types.Transaction
		forks  []config.HardFork
		height uint64
	}{
		// v0 regular transaction before HF4 — must still be v1.
		{"v0_before_hf4", func() *types.Transaction {
			tx := validV1Tx()
			tx.Version = types.VersionInitial
			return tx
		}(), config.MainnetForks, 5000},
		// v1 transaction after HF4 — must be v2.
		{"v1_after_hf4", validV1Tx(), config.TestnetForks, 150},
		// v2 transaction after HF5 — must be v3.
		{"v2_after_hf5", validV2Tx(), config.TestnetForks, 250},
		// v3 transaction after HF4 but before HF5 — too early.
		{"v3_after_hf4_before_hf5", validV3Tx(), config.TestnetForks, 150},
		// v3 transaction after HF5 with wrong hardfork id.
		{"v3_after_hf5_wrong_hardfork", func() *types.Transaction {
			tx := validV3Tx()
			tx.HardforkID = 4
			return tx
		}(), config.TestnetForks, 250},
		// v3 transaction before HF5 — too early.
		{"v3_before_hf5", validV3Tx(), config.TestnetForks, 150},
		// future version must be rejected.
		{"v4_after_hf5", func() *types.Transaction {
			tx := validV3Tx()
			tx.Version = types.VersionPostHF5 + 1
			return tx
		}(), config.TestnetForks, 250},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := checkTxVersion(tt.tx, newTxForkState(tt.forks, tt.height), tt.height)
			if err == nil {
				t.Error("expected ErrTxVersionInvalid, got nil")
			}
		})
	}
}

func TestCheckTxVersion_Ugly(t *testing.T) {
	// v2 at exact HF4 activation boundary (height 101 on testnet, HF4.Height=100).
	txHF4 := validV2Tx()
	err := checkTxVersion(txHF4, newTxForkState(config.TestnetForks, 101), 101)
	if err != nil {
		t.Errorf("v2 at HF4 activation boundary should be valid: %v", err)
	}

	// v1 at exact HF4 activation boundary should be rejected.
	txPreHF4 := validV1Tx()
	err = checkTxVersion(txPreHF4, newTxForkState(config.TestnetForks, 101), 101)
	if err == nil {
		t.Error("v1 at HF4 activation boundary should be rejected")
	}

	// v3 at exact HF5 activation boundary (height 201 on testnet, HF5.Height=200).
	tx := validV3Tx()
	err = checkTxVersion(tx, newTxForkState(config.TestnetForks, 201), 201)
	if err != nil {
		t.Errorf("v3 at HF5 activation boundary should be valid: %v", err)
	}

	// v2 at exact HF5 activation boundary — should be rejected.
	tx2 := validV2Tx()
	err = checkTxVersion(tx2, newTxForkState(config.TestnetForks, 201), 201)
	if err == nil {
		t.Error("v2 at HF5 activation boundary should be rejected")
	}
}
