// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package trigger

import (
	"fmt"

	"github.com/apache/skywalking-rover/pkg/module"
	"github.com/apache/skywalking-rover/pkg/process/api"
	"github.com/apache/skywalking-rover/pkg/profiling/continuous/base"
	"github.com/apache/skywalking-rover/pkg/profiling/continuous/checker/common"
	profiling "github.com/apache/skywalking-rover/pkg/profiling/task/base"

	v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/v3"
)

type NetworkTrigger struct {
	*BaseTrigger
}

func NewNetworkTrigger() base.Trigger {
	return &NetworkTrigger{}
}

func (n *NetworkTrigger) Init(_ *module.Manager, conf *base.ContinuousConfig) error {
	n.BaseTrigger = NewMultipleProcessBasedTrigger(conf, func(p api.ProcessInterface) string {
		// same instance
		entity := p.Entity()
		return fmt.Sprintf("%s_%s", entity.ServiceName, entity.InstanceName)
	}, func(ps []api.ProcessInterface) api.ProcessInterface {
		var mainApplication api.ProcessInterface
		for _, p := range ps {
			// for service mesh, find the application
			if processHasLabel(p, "mesh-application") {
				mainApplication = p
			}
			// otherwise, find the process belong a kubernetes service
			if mainApplication == nil && processHasLabel(p, "k8s-service") {
				mainApplication = p
			}
		}
		if mainApplication != nil {
			return mainApplication
		}
		return ps[0]
	}, func(task *profiling.ProfilingTask, _ []api.ProcessInterface, thresholds []base.ThresholdCause) {
		task.TargetType = profiling.TargetTypeNetworkTopology
		task.ExtensionConfig = &profiling.ExtensionConfig{
			NetworkSamplings: transformCausesToNetworkSamplingRules(thresholds),
		}
	}, func(report *v3.ContinuousProfilingReport, _ []api.ProcessInterface, thresholds []base.ThresholdCause) {
		rules := transformCausesToNetworkSamplingRules(thresholds)
		uriRegexes := make([]string, 0)
		if len(rules) > 0 {
			for _, r := range rules {
				uriRegexes = append(uriRegexes, *r.URIRegex)
			}
		}
		report.TargetTask = &v3.ContinuousProfilingReport_Network{
			Network: &v3.ContinuousNetworkProfilingTask{
				SamplingURIRegexes: uriRegexes,
			},
		}
	})
	return n.BaseTrigger.Init(conf)
}

func processHasLabel(p api.ProcessInterface, label string) bool {
	for _, l := range p.Entity().Labels {
		if l == label {
			return true
		}
	}
	return false
}

func transformCausesToNetworkSamplingRules(thresholds []base.ThresholdCause) []*profiling.NetworkSamplingRule {
	result := make([]*profiling.NetworkSamplingRule, 0)
	var minDuration int32
	for _, threshold := range thresholds {
		uriCause, ok := threshold.(*common.URICause)
		if !ok {
			continue
		}
		// collecting all request and response
		rule := &profiling.NetworkSamplingRule{
			URIRegex:    &uriCause.URI,
			MinDuration: &minDuration,
			When4XX:     true,
			When5XX:     true,
			Settings: &profiling.NetworkDataCollectingSettings{
				RequireCompleteRequest:  true,
				MaxRequestSize:          -1,
				RequireCompleteResponse: true,
				MaxResponseSize:         -1,
			},
		}

		if uriCause.URI == "" {
			return []*profiling.NetworkSamplingRule{rule}
		}
		result = append(result, rule)
	}
	return result
}
