package plugin_debug

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/pprof"
	"os"
	"runtime"
	runtimePPROF "runtime/pprof"
	"sort"
	"strings"
	"sync"
	"time"

	myproc "github.com/cloudwego/goref/pkg/proc"
	"github.com/go-delve/delve/pkg/config"
	"github.com/go-delve/delve/service/debugger"
	"google.golang.org/protobuf/types/known/emptypb"
	"m7s.live/v5"
	"m7s.live/v5/plugin/debug/pb"
	debug "m7s.live/v5/plugin/debug/pkg"
	"m7s.live/v5/plugin/debug/pkg/profile"
)

var _ = m7s.InstallPlugin[DebugPlugin](&pb.Api_ServiceDesc, pb.RegisterApiHandler)
var conf, _ = config.LoadConfig()

type DebugPlugin struct {
	pb.UnimplementedApiServer
	m7s.Plugin
	ProfileDuration time.Duration `default:"10s" desc:"profile持续时间"`
	Profile         string        `desc:"采集profile存储文件"`
	ChartPeriod     time.Duration `default:"1s" desc:"图表更新周期"`
	Grfout          string        `default:"grf.out" desc:"grf输出文件"`

	// 添加缓存字段
	cpuProfileData *profile.Profile // 缓存 CPU Profile 数据
	cpuProfileOnce sync.Once        // 确保只采集一次
	cpuProfileLock sync.Mutex       // 保护缓存数据
}

type WriteToFile struct {
	header http.Header
	io.Writer
}

func (w *WriteToFile) Header() http.Header {
	return w.header
}

func (w *WriteToFile) WriteHeader(statusCode int) {}

func (p *DebugPlugin) OnInit() error {
	// 启用阻塞分析
	runtime.SetBlockProfileRate(1) // 设置采样率为1纳秒

	if p.Profile != "" {
		go func() {
			file, err := os.Create(p.Profile)
			if err != nil {
				return
			}
			defer file.Close()
			p.Info("cpu profile start")
			err = runtimePPROF.StartCPUProfile(file)
			time.Sleep(p.ProfileDuration)
			runtimePPROF.StopCPUProfile()
			p.Info("cpu profile done")
		}()
	}
	return nil
}

func (p *DebugPlugin) Pprof_Trace(w http.ResponseWriter, r *http.Request) {
	r.URL.Path = "/debug" + r.URL.Path
	pprof.Trace(w, r)
}

func (p *DebugPlugin) Pprof_profile(w http.ResponseWriter, r *http.Request) {
	r.URL.Path = "/debug" + r.URL.Path
	pprof.Profile(w, r)
}

func (p *DebugPlugin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/pprof" {
		http.Redirect(w, r, "/debug/pprof/", http.StatusFound)
		return
	}
	r.URL.Path = "/debug" + r.URL.Path
	pprof.Index(w, r)
}

func (p *DebugPlugin) Charts_(w http.ResponseWriter, r *http.Request) {
	r.URL.Path = "/static" + strings.TrimPrefix(r.URL.Path, "/charts")
	staticFSHandler.ServeHTTP(w, r)
}

func (p *DebugPlugin) Charts_data(w http.ResponseWriter, r *http.Request) {
	dataHandler(w, r)
}

func (p *DebugPlugin) Charts_datafeed(w http.ResponseWriter, r *http.Request) {
	s.dataFeedHandler(w, r)
}

func (p *DebugPlugin) Grf(w http.ResponseWriter, r *http.Request) {
	dConf := debugger.Config{
		AttachPid:             os.Getpid(),
		Backend:               "default",
		CoreFile:              "",
		DebugInfoDirectories:  conf.DebugInfoDirectories,
		AttachWaitFor:         "",
		AttachWaitForInterval: 1,
		AttachWaitForDuration: 0,
	}
	dbg, err := debugger.New(&dConf, nil)
	defer dbg.Detach(false)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = myproc.ObjectReference(dbg.Target(), p.Grfout); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	w.Write([]byte("ok"))
}

func (p *DebugPlugin) GetHeap(ctx context.Context, empty *emptypb.Empty) (*pb.HeapResponse, error) {
	// 创建临时文件用于存储堆信息
	f, err := os.CreateTemp("", "heap")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f.Name())
	defer f.Close()

	// 获取堆信息
	runtime.GC()
	if err := runtimePPROF.WriteHeapProfile(f); err != nil {
		return nil, err
	}

	// 读取堆信息
	f.Seek(0, 0)
	prof, err := profile.Parse(f)
	if err != nil {
		return nil, err
	}

	// 准备响应数据
	resp := &pb.HeapResponse{
		Data: &pb.HeapData{
			Stats:   &pb.HeapStats{},
			Objects: make([]*pb.HeapObject, 0),
			Edges:   make([]*pb.HeapEdge, 0),
		},
	}

	// 创建类型映射用于聚合统计
	typeMap := make(map[string]*pb.HeapObject)
	var totalSize int64

	// 处理每个样本
	for _, sample := range prof.Sample {
		size := sample.Value[1] // 内存大小
		if size == 0 {
			continue
		}

		// 获取分配类型信息
		var typeName string
		if len(sample.Location) > 0 && len(sample.Location[0].Line) > 0 {
			if fn := sample.Location[0].Line[0].Function; fn != nil {
				typeName = fn.Name
			}
		}

		// 创建或更新堆对象
		obj, exists := typeMap[typeName]
		if !exists {
			obj = &pb.HeapObject{
				Type:    typeName,
				Address: fmt.Sprintf("%p", sample),
				Refs:    make([]string, 0),
			}
			typeMap[typeName] = obj
			resp.Data.Objects = append(resp.Data.Objects, obj)
		}

		obj.Count++
		obj.Size += size
		totalSize += size

		// 构建引���关系
		for i := 1; i < len(sample.Location); i++ {
			loc := sample.Location[i]
			if len(loc.Line) == 0 || loc.Line[0].Function == nil {
				continue
			}

			callerName := loc.Line[0].Function.Name
			// 跳过系统函数
			if callerName == "" || strings.HasPrefix(callerName, "runtime.") {
				continue
			}

			// 添加边
			edge := &pb.HeapEdge{
				From:      callerName,
				To:        typeName,
				FieldName: callerName,
			}
			resp.Data.Edges = append(resp.Data.Edges, edge)

			// 将调用者添加到引用列表
			if !contains(obj.Refs, callerName) {
				obj.Refs = append(obj.Refs, callerName)
			}
		}
	}

	// 计算百分比
	for _, obj := range resp.Data.Objects {
		if totalSize > 0 {
			obj.SizePerc = float64(obj.Size) / float64(totalSize) * 100
		}
	}

	// 按大小排序
	sort.Slice(resp.Data.Objects, func(i, j int) bool {
		return resp.Data.Objects[i].Size > resp.Data.Objects[j].Size
	})

	// 获取运行时内存统计
	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)

	// 填充内存统计信息
	resp.Data.Stats.Alloc = ms.Alloc
	resp.Data.Stats.TotalAlloc = ms.TotalAlloc
	resp.Data.Stats.Sys = ms.Sys
	resp.Data.Stats.NumGC = ms.NumGC
	resp.Data.Stats.HeapAlloc = ms.HeapAlloc
	resp.Data.Stats.HeapSys = ms.HeapSys
	resp.Data.Stats.HeapIdle = ms.HeapIdle
	resp.Data.Stats.HeapInuse = ms.HeapInuse
	resp.Data.Stats.HeapReleased = ms.HeapReleased
	resp.Data.Stats.HeapObjects = ms.HeapObjects
	resp.Data.Stats.GcCPUFraction = ms.GCCPUFraction

	return resp, nil
}

// 采集 CPU Profile 并缓存
func (p *DebugPlugin) collectCPUProfile() error {
	p.cpuProfileLock.Lock()
	defer p.cpuProfileLock.Unlock()

	// 如果已经采集过，直接返回
	if p.cpuProfileData != nil {
		return nil
	}

	// 创建临时文件用于存储 CPU Profile 数据
	f, err := os.CreateTemp("", "cpu_profile")
	if err != nil {
		return fmt.Errorf("could not create CPU profile: %v", err)
	}
	defer os.Remove(f.Name())
	defer f.Close()

	// 开始 CPU profiling
	if err := runtimePPROF.StartCPUProfile(f); err != nil {
		return fmt.Errorf("could not start CPU profile: %v", err)
	}

	// 采样指定时间
	time.Sleep(p.ProfileDuration)
	runtimePPROF.StopCPUProfile()

	// 读取并解析 CPU Profile 数据
	f.Seek(0, 0)
	profileData, err := profile.Parse(f)
	if err != nil {
		return fmt.Errorf("could not parse CPU profile: %v", err)
	}

	// 缓存 CPU Profile 数据
	p.cpuProfileData = profileData
	return nil
}

// GetCpu 接口
func (p *DebugPlugin) GetCpu(ctx context.Context, req *pb.CpuRequest) (*pb.CpuResponse, error) {
	// 如果需要刷新或者缓存中没有数据
	if req.Refresh || p.cpuProfileData == nil {
		p.cpuProfileLock.Lock()
		p.cpuProfileData = nil         // 清除现有缓存
		p.cpuProfileOnce = sync.Once{} // 重置 Once
		p.cpuProfileLock.Unlock()
	}

	// 如果请求指定了duration，临时更新ProfileDuration
	originalDuration := p.ProfileDuration
	if req.Duration > 0 {
		p.ProfileDuration = time.Duration(req.Duration) * time.Second
	}

	// 确保采集 CPU Profile
	p.cpuProfileOnce.Do(func() {
		if err := p.collectCPUProfile(); err != nil {
			fmt.Printf("Failed to collect CPU profile: %v\n", err)
		}
	})

	// 恢复原始的ProfileDuration
	if req.Duration > 0 {
		p.ProfileDuration = originalDuration
	}

	// 如果缓存中没有数据，返回错误
	if p.cpuProfileData == nil {
		return nil, fmt.Errorf("CPU profile data is not available")
	}

	// 使用缓存的 CPU Profile 数据构建响应
	resp := &pb.CpuResponse{
		Data: &pb.CpuData{
			TotalCpuTimeNs:     uint64(p.cpuProfileData.DurationNanos),
			SamplingIntervalNs: uint64(p.cpuProfileData.Period),
			Functions:          make([]*pb.FunctionProfile, 0),
			Goroutines:         make([]*pb.GoroutineProfile, 0),
			SystemCalls:        make([]*pb.SystemCall, 0),
			RuntimeStats:       &pb.RuntimeStats{},
		},
	}

	// 填充函数调用信息
	for _, sample := range p.cpuProfileData.Sample {
		functionProfile := &pb.FunctionProfile{
			FunctionName:    sample.Location[0].Line[0].Function.Name,
			CpuTimeNs:       uint64(sample.Value[0]),
			InvocationCount: uint64(sample.Value[1]),
			CallStack:       make([]string, 0),
		}

		// 填充调用栈信息
		for _, loc := range sample.Location {
			for _, line := range loc.Line {
				functionProfile.CallStack = append(functionProfile.CallStack, line.Function.Name)
			}
		}

		resp.Data.Functions = append(resp.Data.Functions, functionProfile)
	}

	return resp, nil
}

// GetCpuGraph 接口
func (p *DebugPlugin) GetCpuGraph(ctx context.Context, req *pb.CpuRequest) (*pb.CpuGraphResponse, error) {
	// 如果需要刷新或者缓存中没有数据
	if req.Refresh || p.cpuProfileData == nil {
		p.cpuProfileLock.Lock()
		p.cpuProfileData = nil         // 清除现有缓存
		p.cpuProfileOnce = sync.Once{} // 重置 Once
		p.cpuProfileLock.Unlock()
	}

	// 如果请求指定了duration，临时更新ProfileDuration
	originalDuration := p.ProfileDuration
	if req.Duration > 0 {
		p.ProfileDuration = time.Duration(req.Duration) * time.Second
	}

	// 确保采集 CPU Profile
	p.cpuProfileOnce.Do(func() {
		if err := p.collectCPUProfile(); err != nil {
			fmt.Printf("Failed to collect CPU profile: %v\n", err)
		}
	})

	// 恢复原始的ProfileDuration
	if req.Duration > 0 {
		p.ProfileDuration = originalDuration
	}

	// 如果缓存中没有数据，返回错误
	if p.cpuProfileData == nil {
		return nil, fmt.Errorf("CPU profile data is not available")
	}

	// 使用缓存的 CPU Profile 数据生成 dot 图
	dot, err := debug.GetDotGraph(p.cpuProfileData)
	if err != nil {
		return nil, fmt.Errorf("could not generate dot graph: %v", err)
	}

	return &pb.CpuGraphResponse{
		Data: dot,
	}, nil
}

// 辅助函数：检查字符串切片是否包含特定字符串
func contains(slice []string, str string) bool {
	for _, s := range slice {
		if s == str {
			return true
		}
	}
	return false
}

func (p *DebugPlugin) GetHeapGraph(ctx context.Context, empty *emptypb.Empty) (*pb.HeapGraphResponse, error) {
	// 创建临时文件用于存储堆信息
	f, err := os.CreateTemp("", "heap")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f.Name())
	defer f.Close()

	// 获取堆信息
	runtime.GC()
	if err := runtimePPROF.WriteHeapProfile(f); err != nil {
		return nil, err
	}

	// 读取堆信息
	f.Seek(0, 0)
	profile, err := profile.Parse(f)
	if err != nil {
		return nil, err
	}
	// Generate dot graph.
	dot, err := debug.GetDotGraph(profile)
	if err != nil {
		return nil, err
	}
	return &pb.HeapGraphResponse{
		Data: dot,
	}, nil
}
