package containers

import (
	"context"
	"os"
	"time"

	"github.com/jpillora/backoff"

	"github.com/cilium/ebpf/link"
	"github.com/coroot/coroot-node-agent/proc"
	"github.com/mdlayher/taskstats"
)

type Process struct {
	Pid       uint32
	StartedAt time.Time
	NetNsId   string

	ctx        context.Context
	cancelFunc context.CancelFunc

	dotNetMonitor *DotNetMonitor

	uprobes               []link.Link
	goTlsUprobesChecked   bool
	openSslUprobesChecked bool
	jvmUprobesChecked     bool
}

func NewProcess(pid uint32, stats *taskstats.Stats) *Process {
	ns, err := proc.GetNetNs(pid)
	if err != nil {
		return nil
	}
	defer ns.Close()
	p := &Process{Pid: pid, StartedAt: stats.BeginTime, NetNsId: ns.UniqueId()}
	p.ctx, p.cancelFunc = context.WithCancel(context.Background())
	go p.instrument()
	return p
}

func (p *Process) isHostNs() bool {
	return p.NetNsId == hostNetNsId
}

func (p *Process) instrument() {
	b := backoff.Backoff{Factor: 2, Min: time.Second, Max: time.Minute}
	for {
		select {
		case <-p.ctx.Done():
			return
		default:
			dest, err := os.Readlink(proc.Path(p.Pid, "exe"))
			if err != nil {
				return
			}
			if dest != "/" {
				if dotNetAppName, err := dotNetApp(p.Pid); err == nil {
					if dotNetAppName != "" {
						p.dotNetMonitor = NewDotNetMonitor(p.ctx, p.Pid, dotNetAppName)
					}
					return
				}
			}
			time.Sleep(b.Duration())
		}
	}
}

func (p *Process) Close() {
	p.cancelFunc()
	for _, u := range p.uprobes {
		_ = u.Close()
	}
}
