package pprof

import (
	"bytes"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	"github.com/projectdiscovery/utils/env"
)

const (
	PPROFSwitchENV = "PPROF"
	MemProfileENV  = "MEM_PROFILE_DIR"
	CPUProfileENV  = "CPU_PROFILE_DIR"
	PPROFTimeENV   = "PPROF_TIME"
	MemProfileRate = "MEM_PROFILE_RATE"
)

func init() {
	if env.GetEnvOrDefault(PPROFSwitchENV, 0) == 1 {
		log.Printf("[+] GOOS: %v\n", runtime.GOOS)
		log.Printf("[+] GOARCH: %v\n", runtime.GOARCH)
		log.Printf("[+] Command: %v\n", strings.Join(os.Args, " "))
		log.Println("Available PPROF Config Options:")
		log.Printf("%-16v - directory to write memory profiles to\n", MemProfileENV)
		log.Printf("%-16v - directory to write cpu profiles to\n", CPUProfileENV)
		log.Printf("%-16v - polling time for cpu and memory profiles (with unit ex: 10s)\n", PPROFTimeENV)
		log.Printf("%-16v - memory profiling rate (default 4096)\n", MemProfileRate)

		memProfilesDir := env.GetEnvOrDefault(MemProfileENV, "memdump")
		cpuProfilesDir := env.GetEnvOrDefault(CPUProfileENV, "cpuprofile")
		pprofTimeDuration := env.GetEnvOrDefault(PPROFTimeENV, time.Duration(3)*time.Second)
		pprofRate := env.GetEnvOrDefault(MemProfileRate, 4096)

		_ = os.MkdirAll(memProfilesDir, 0755)
		_ = os.MkdirAll(cpuProfilesDir, 0755)

		runtime.MemProfileRate = pprofRate
		log.Printf("profile: memory profiling enabled (rate %d), %s\n", runtime.MemProfileRate, memProfilesDir)
		log.Printf("profile: ticker enabled (rate %s)\n", pprofTimeDuration)

		// cpu ticker and profiler
		go func() {
			ticker := time.NewTicker(pprofTimeDuration)
			count := 0
			buff := bytes.Buffer{}
			log.Printf("profile: cpu profiling enabled (ticker %s)\n", pprofTimeDuration)
			for {
				err := pprof.StartCPUProfile(&buff)
				if err != nil {
					log.Fatalf("profile: could not start cpu profile: %s\n", err)
				}
				<-ticker.C
				pprof.StopCPUProfile()
				if err := os.WriteFile(filepath.Join(cpuProfilesDir, "cpuprofile-t"+strconv.Itoa(count)+".out"), buff.Bytes(), 0755); err != nil {
					log.Fatalf("profile: could not write cpu profile: %s\n", err)
				}
				buff.Reset()
				count++
			}
		}()

		// memory ticker and profiler
		go func() {
			ticker := time.NewTicker(pprofTimeDuration)
			count := 0
			log.Printf("profile: memory profiling enabled (ticker %s)\n", pprofTimeDuration)
			for {
				<-ticker.C
				var buff bytes.Buffer
				if err := pprof.WriteHeapProfile(&buff); err != nil {
					log.Printf("profile: could not write memory profile: %s\n", err)
				}
				err := os.WriteFile(filepath.ToSlash(filepath.Join(memProfilesDir, "memprofile-t"+strconv.Itoa(count)+".out")), buff.Bytes(), 0755)
				if err != nil {
					log.Printf("profile: could not write memory profile: %s\n", err)
				}
				count++
			}
		}()
	}
}
