package contractcourt

import (
	"testing"
	"time"

	"github.com/btcsuite/btcd/wire/v2"
)

// TestChainWatcherCoopCloseReorg tests that the chain watcher properly handles
// a reorganization during cooperative close confirmation waiting. When a
// cooperative close transaction is reorganized out, the chain watcher should
// re-register for spend notifications and detect an alternative transaction.
func TestChainWatcherCoopCloseReorg(t *testing.T) {
	t.Parallel()

	// Create test harness.
	harness := newChainWatcherTestHarness(t)

	// Create two cooperative close transactions with different fees.
	tx1 := harness.createCoopCloseTx(5000)
	tx2 := harness.createCoopCloseTx(4900)

	// Run cooperative close flow with reorg.
	closeInfo := harness.runCoopCloseFlow(tx1, true, 2, tx2)

	// Assert that the second transaction was confirmed.
	harness.assertCoopCloseTx(closeInfo, tx2)
}

// TestChainWatcherCoopCloseSameTransactionAfterReorg tests that if the same
// transaction re-confirms after a reorganization, it is properly handled.
func TestChainWatcherCoopCloseSameTransactionAfterReorg(t *testing.T) {
	t.Parallel()

	harness := newChainWatcherTestHarness(t)

	// Create a single cooperative close transaction.
	tx := harness.createCoopCloseTx(5000)

	// Run flow with the same tx confirming after the reorg.
	closeInfo := harness.runCoopCloseFlow(tx, true, 2, tx)

	harness.assertCoopCloseTx(closeInfo, tx)
}

// TestChainWatcherCoopCloseMultipleReorgs tests handling of multiple
// consecutive reorganizations during cooperative close confirmation.
func TestChainWatcherCoopCloseMultipleReorgs(t *testing.T) {
	t.Parallel()

	// Create test harness.
	harness := newChainWatcherTestHarness(t)

	// Create multiple cooperative close transactions with different fees.
	txs := []*wire.MsgTx{
		harness.createCoopCloseTx(5000),
		harness.createCoopCloseTx(4950),
		harness.createCoopCloseTx(4900),
		harness.createCoopCloseTx(4850),
	}

	// Define reorg depths for each transition.
	reorgDepths := []int32{1, 2, 3}

	// Run multiple reorg flow.
	closeInfo := harness.runMultipleReorgFlow(txs, reorgDepths)

	// Assert that the final transaction was confirmed.
	harness.assertCoopCloseTx(closeInfo, txs[3])
}

// TestChainWatcherCoopCloseReorgNoAlternative tests that if a cooperative
// close is reorganized out and no alternative transaction appears, the
// chain watcher continues waiting.
func TestChainWatcherCoopCloseReorgNoAlternative(t *testing.T) {
	t.Parallel()

	// Create test harness.
	harness := newChainWatcherTestHarness(t)

	// Create a cooperative close transaction.
	tx := harness.createCoopCloseTx(5000)

	// Send spend and wait for confirmation registration.
	harness.sendSpend(tx)
	harness.waitForConfRegistration()

	// Trigger reorg after some confirmations.
	harness.mineBlocks(2)
	harness.triggerReorg(tx, 2)

	// Assert no cooperative close event is received.
	harness.assertNoCoopClose(2 * time.Second)

	// Now send a new transaction after the timeout.
	harness.waitForSpendRegistration()
	newTx := harness.createCoopCloseTx(4900)
	harness.sendSpend(newTx)
	harness.waitForConfRegistration()
	harness.mineBlocks(1)
	harness.confirmTx(newTx, harness.currentHeight)

	// Should receive cooperative close for the new transaction.
	closeInfo := harness.waitForCoopClose(5 * time.Second)
	harness.assertCoopCloseTx(closeInfo, newTx)
}

// TestChainWatcherCoopCloseScaledConfirmationsWithReorg tests that scaled
// confirmations (based on channel capacity) work correctly with reorgs.
func TestChainWatcherCoopCloseScaledConfirmationsWithReorg(t *testing.T) {
	t.Parallel()

	// Test with different confirmation requirements and reorg depths.
	// Note: We start at 3 confirmations because 1-conf uses the fast path
	// which bypasses reorg protection (it dispatches immediately).
	testCases := []struct {
		name          string
		requiredConfs uint32
		reorgDepth    int32
	}{
		{
			name:          "triple_conf",
			requiredConfs: 3,
			reorgDepth:    2,
		},
		{
			name:          "six_conf",
			requiredConfs: 6,
			reorgDepth:    4,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			// Create harness with specific confirmation
			// requirements.
			harness := newChainWatcherTestHarness(
				t, withRequiredConfs(tc.requiredConfs),
			)

			// Create transactions.
			tx1 := harness.createCoopCloseTx(5000)
			tx2 := harness.createCoopCloseTx(4900)

			// Run with reorg at different depths based on capacity.
			closeInfo := harness.runCoopCloseFlow(
				tx1, true, tc.reorgDepth, tx2,
			)

			// Verify correct transaction confirmed.
			harness.assertCoopCloseTx(closeInfo, tx2)
		})
	}
}

// TestChainWatcherCoopCloseRapidReorgs tests that the chain watcher handles
// multiple rapid reorgs in succession without getting into a broken state.
func TestChainWatcherCoopCloseRapidReorgs(t *testing.T) {
	t.Parallel()

	// Create test harness.
	harness := newChainWatcherTestHarness(t)

	// Create a cooperative close transaction.
	tx := harness.createCoopCloseTx(5000)

	// Send spend notification.
	harness.sendSpend(tx)

	// Trigger multiple rapid reorgs to stress the state machine.
	for i := 0; i < 5; i++ {
		harness.waitForConfRegistration()
		harness.mineBlocks(1)
		harness.triggerReorg(tx, int32(i+1))
		if i < 4 {
			// Re-register for spend after each reorg except the
			// last.
			harness.waitForSpendRegistration()
			harness.sendSpend(tx)
		}
	}

	// After stress, send a clean transaction.
	harness.waitForSpendRegistration()
	cleanTx := harness.createCoopCloseTx(4800)
	harness.sendSpend(cleanTx)
	harness.waitForConfRegistration()
	harness.mineBlocks(1)
	harness.confirmTx(cleanTx, harness.currentHeight)

	// Should still receive the cooperative close.
	closeInfo := harness.waitForCoopClose(10 * time.Second)
	harness.assertCoopCloseTx(closeInfo, cleanTx)
}
