// 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 create

import (
	"fmt"
	"os"

	"github.com/apache/skywalking-cli/internal/commands/interceptor"
	"github.com/apache/skywalking-cli/internal/flags"
	"github.com/apache/skywalking-cli/pkg/display"
	"github.com/apache/skywalking-cli/pkg/display/displayable"
	"github.com/apache/skywalking-cli/pkg/graphql/profiling"

	"gopkg.in/yaml.v2"

	"github.com/urfave/cli/v2"

	api "skywalking.apache.org/repo/goapi/query"
)

type SamplingConfig struct {
	Samplings []*SamplingRule `yaml:"samplings"`
}

type SamplingRule struct {
	URIPattern  *string          `yaml:"uri_pattern"`
	MinDuration *int             `yaml:"min_duration"`
	When4xx     *bool            `yaml:"when_4xx"`
	When5xx     *bool            `yaml:"when_5xx"`
	Setting     *SamplingSetting `yaml:"setting"`
}

type SamplingSetting struct {
	RequireRequest  *bool `yaml:"require_request"`
	MaxRequestSize  *int  `yaml:"max_request_size"`
	RequireResponse *bool `yaml:"require_response"`
	MaxResponseSize *int  `yaml:"max_response_size"`
}

var NetworkCreateCommand = &cli.Command{
	Name:    "network",
	Aliases: []string{"net"},
	Usage:   "Create a new ebpf network profiling task",
	UsageText: `Create a new ebpf network profiling task

Examples:
1. Create ebpf network profiling task
$ swctl profiling ebpf create network --service-instance-id=abc`,
	Flags: flags.Flags(
		flags.ServiceFlags,
		flags.InstanceFlags,
		[]cli.Flag{
			&cli.StringFlag{
				Name:     "sampling-config",
				Usage:    "the `sampling-config` file define how to sampling the network data, if not then then ignore data sampling",
				Required: false,
			},
		},
	),
	Before: interceptor.BeforeChain(
		interceptor.ParseInstance(true),
	),
	Action: func(ctx *cli.Context) error {
		instanceID := ctx.String("instance-id")

		samplingConfigFile := ctx.String("sampling-config")

		// convert the sampling rule
		samplings := make([]*api.EBPFNetworkSamplingRule, 0)
		if samplingConfigFile != "" {
			config, err := os.ReadFile(samplingConfigFile)
			if err != nil {
				return err
			}
			r := &SamplingConfig{}
			if e := yaml.Unmarshal(config, r); e != nil {
				return e
			}

			samplings, err = parsingNetworkSampling(r)
			if err != nil {
				return err
			}
		}

		request := &api.EBPFProfilingNetworkTaskRequest{
			InstanceID: instanceID,
			Samplings:  samplings,
		}

		task, err := profiling.CreateEBPFNetworkProfilingTask(ctx.Context, request)
		if err != nil {
			return err
		}

		return display.Display(ctx.Context, &displayable.Displayable{Data: task, Condition: request})
	},
}

func parsingNetworkSampling(config *SamplingConfig) ([]*api.EBPFNetworkSamplingRule, error) {
	rules := make([]*api.EBPFNetworkSamplingRule, 0)
	if config == nil || len(config.Samplings) == 0 {
		return rules, nil
	}
	for _, conf := range config.Samplings {
		rule := &api.EBPFNetworkSamplingRule{}
		rule.URIRegex = conf.URIPattern
		rule.MinDuration = conf.MinDuration
		if conf.When4xx == nil {
			return nil, fmt.Errorf("the when_4xx is required")
		}
		rule.When4xx = *conf.When4xx
		if conf.When5xx == nil {
			return nil, fmt.Errorf("the when_5xx is required")
		}
		rule.When5xx = *conf.When5xx

		confSetting := conf.Setting
		if confSetting == nil {
			return nil, fmt.Errorf("the sampling settings is required")
		}
		setting := &api.EBPFNetworkDataCollectingSettings{}
		rule.Settings = setting
		if confSetting.RequireRequest == nil {
			return nil, fmt.Errorf("the sampling request is required")
		}
		setting.RequireCompleteRequest = *confSetting.RequireRequest
		setting.MaxRequestSize = confSetting.MaxRequestSize
		if confSetting.RequireResponse == nil {
			return nil, fmt.Errorf("the sampling response is required")
		}
		setting.RequireCompleteResponse = *confSetting.RequireResponse
		setting.MaxResponseSize = confSetting.MaxResponseSize

		rules = append(rules, rule)
	}

	return rules, nil
}
