//
// Copyright 2024 The Chainloop Authors.
//
// Licensed 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 cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"

	"github.com/chainloop-dev/chainloop/app/cli/cmd/output"
	"github.com/chainloop-dev/chainloop/app/cli/pkg/action"
	"github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1"
	"github.com/jedib0t/go-pretty/v6/table"
	"github.com/jedib0t/go-pretty/v6/text"
	"github.com/spf13/cobra"
)

func newAvailableIntegrationDescribeCmd() *cobra.Command {
	var integrationName string

	cmd := &cobra.Command{
		Use:   "describe",
		Short: "Describe integration",
		RunE: func(cmd *cobra.Command, args []string) error {
			item, err := action.NewAvailableIntegrationDescribe(ActionOpts).Run(integrationName)
			if err != nil {
				return err
			}

			if item == nil {
				return fmt.Errorf("integration %q not found", integrationName)
			}

			return output.EncodeOutput(flagOutputFormat, []*action.AvailableIntegrationItem{item}, availableIntegrationDescribeTableOutput)
		},
	}

	cmd.Flags().StringVar(&integrationName, "name", "", "integration name")
	cobra.CheckErr(cmd.MarkFlagRequired("name"))
	cmd.Flags().BoolVar(&full, "full", false, "show the full output including JSON schemas")

	return cmd
}

func availableIntegrationDescribeTableOutput(items []*action.AvailableIntegrationItem) error {
	if len(items) != 1 {
		return nil
	}

	i := items[0]

	// General information
	if err := availableIntegrationListTableOutput(items); err != nil {
		return err
	}

	// Schema information
	if err := renderSchemaTable("Registration inputs", i.Registration.Properties); err != nil {
		return err
	}

	if full {
		if err := renderSchemaRaw("Registration JSON schema", i.Registration.Raw); err != nil {
			return err
		}
	}

	if err := renderSchemaTable("Attachment inputs", i.Attachment.Properties); err != nil {
		return err
	}

	if full {
		if err := renderSchemaRaw("Attachment JSON schema", i.Attachment.Raw); err != nil {
			return err
		}
	}

	return nil
}

// render de-normalized schema format
func renderSchemaTable(tableTitle string, properties sdk.SchemaPropertiesMap) error {
	if len(properties) == 0 {
		return nil
	}

	t := output.NewTableWriter()
	t.SetTitle(tableTitle)
	t.AppendHeader(table.Row{"Field", "Type", "Required", "Description"})

	// Sort map
	keys := make([]string, 0, len(properties))
	for k := range properties {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	for _, k := range keys {
		v := properties[k]

		propertyType := v.Type
		if v.Format != "" {
			propertyType = fmt.Sprintf("%s (%s)", propertyType, v.Format)
		}

		color := text.Colors{}
		if v.Required {
			color = text.Colors{text.FgHiYellow}
		}

		var requiredInfo = color.Sprint(hBool(v.Required))
		if v.Default != "" && !v.Required {
			requiredInfo = fmt.Sprintf("%s (default: %s)", requiredInfo, v.Default)
		}

		t.AppendRow(table.Row{color.Sprint(v.Name), color.Sprint(propertyType), requiredInfo, v.Description})
	}

	t.Render()

	return nil
}

// render raw JSON schema document
func renderSchemaRaw(tableTitle string, s string) error {
	var prettyAttachmentJSON bytes.Buffer
	err := json.Indent(&prettyAttachmentJSON, []byte(s), "", "  ")
	if err != nil {
		return err
	}

	rt := output.NewTableWriter()
	rt.SetTitle(tableTitle)
	rt.AppendRow(table.Row{prettyAttachmentJSON.String()})
	rt.Render()

	return nil
}
