package miner

import (
	"strings"
	"sync/atomic"
	"testing"
	"time"

	"crypto-miner-agent/config"
	"crypto-miner-agent/job"
	"crypto-miner-agent/stats"
)

func testPoolCfg() config.RuntimeConfig {
	return config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{
		MiningMode:   "always",
		MaxCPUUsage:  0,
		MaxMemoryPct: 0,
		MinFreeRAM:   0,
	}}
}

func TestNewPoolDefaultsThreads(t *testing.T) {
	p := NewPool(0, testPoolCfg(), stats.NewReporter(), nil)
	if p == nil || len(p.engines) != 1 {
		t.Fatalf("threads<=0 should default to 1 engine, got %d", len(p.engines))
	}
}

func TestNewPoolMultipleEngines(t *testing.T) {
	p := NewPool(3, testPoolCfg(), stats.NewReporter(), nil)
	if len(p.engines) != 3 {
		t.Fatalf("expected 3 engines, got %d", len(p.engines))
	}
}

func TestPoolSetJobNil(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	p.SetJob(nil)
	p.mu.RLock()
	defer p.mu.RUnlock()
	if p.currentJob != nil {
		t.Fatal("SetJob(nil) should leave current job nil")
	}
}

func TestPoolSetJobDerivesSeedFromBlob(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	blobPrefix := strings.Repeat("ee", 32)
	blob := blobPrefix + strings.Repeat("11", 22)
	j := &job.Job{ID: "j1", Blob: blob}
	p.SetJob(j)
	if p.engines[0].seedHex != blobPrefix {
		t.Fatalf("seed from blob prefix = %q, want %q", p.engines[0].seedHex, blobPrefix)
	}
}

func TestPoolHashesPerSecondAndReset(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	p.hashesTotal.Store(1000)
	p.hashesLastReset = time.Now().Add(-2 * time.Second)
	rate := p.HashesPerSecond()
	if rate <= 0 {
		t.Fatalf("expected positive hashrate, got %f", rate)
	}
	p.ResetHashCounter()
	if p.hashesTotal.Load() != 0 {
		t.Fatal("ResetHashCounter should zero hashesTotal")
	}
	if p.HashesPerSecond() != 0 {
		t.Fatalf("hashrate after reset with no hashes should be 0, got %f", p.HashesPerSecond())
	}
}

func TestPoolRemotePause(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	if p.IsRemotePaused() {
		t.Fatal("new pool should not be remote-paused")
	}
	p.PauseRemote()
	if !p.IsRemotePaused() {
		t.Fatal("PauseRemote should set flag")
	}
	p.ResumeRemote()
	if p.IsRemotePaused() {
		t.Fatal("ResumeRemote should clear flag")
	}
}

func TestPoolSetShareHandler(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	var called atomic.Bool
	p.SetShareHandler(func(jobID, nonce, hash string) {
		called.Store(true)
	})
	p.handlerMu.RLock()
	h := p.handler
	p.handlerMu.RUnlock()
	if h == nil {
		t.Fatal("handler should be set")
	}
	h("j", "n", "h")
	if !called.Load() {
		t.Fatal("swapped handler should run")
	}
}

func TestPoolMiningAllowedAlways(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	if !p.miningAllowed() {
		t.Fatal("always mode with resource limits disabled should allow mining")
	}
}

func TestPoolResourcesOKDisabledLimits(t *testing.T) {
	p := NewPool(1, testPoolCfg(), stats.NewReporter(), nil)
	if !p.resourcesOK() {
		t.Fatal("zero resource limits should allow mining")
	}
}
