package test

import (
	"strings"

	"github.com/portainer/portainer/pkg/libhelm"
	"github.com/portainer/portainer/pkg/libhelm/options"
	"github.com/portainer/portainer/pkg/libhelm/release"

	"github.com/pkg/errors"
	"github.com/segmentio/encoding/json"
	"gopkg.in/yaml.v3"
)

const (
	MockDataIndex  = "mock-index"
	MockDataChart  = "mock-chart"
	MockDataReadme = "mock-readme"
	MockDataValues = "mock-values"
)

const (
	MockReleaseHooks    = "mock-release-hooks"
	MockReleaseManifest = "mock-release-manifest"
	MockReleaseNotes    = "mock-release-notes"
	MockReleaseValues   = "mock-release-values"
)

// helmMockPackageManager is a test package for helm related http handler testing
// Note: this package currently uses a slice in a way that is not thread safe.
// Do not use this package for concurrent tests.
type helmMockPackageManager struct{}

// NewMockHelmBinaryPackageManager initializes a new HelmPackageManager service (a mock instance)
func NewMockHelmBinaryPackageManager(binaryPath string) libhelm.HelmPackageManager {
	return &helmMockPackageManager{}
}

var mockCharts = []release.ReleaseElement{}

func newMockReleaseElement(installOpts options.InstallOptions) *release.ReleaseElement {
	return &release.ReleaseElement{
		Name:       installOpts.Name,
		Namespace:  installOpts.Namespace,
		Updated:    "date/time",
		Status:     "deployed",
		Chart:      installOpts.Chart,
		AppVersion: "1.2.3",
	}
}

func newMockRelease(re *release.ReleaseElement) *release.Release {
	return &release.Release{
		Name:      re.Name,
		Namespace: re.Namespace,
	}
}

// Install a helm chart (not thread safe)
func (hpm *helmMockPackageManager) Install(installOpts options.InstallOptions) (*release.Release, error) {

	releaseElement := newMockReleaseElement(installOpts)

	// Enforce only one chart with the same name per namespace
	for i, rel := range mockCharts {
		if rel.Name == installOpts.Name && rel.Namespace == installOpts.Namespace {
			mockCharts[i] = *releaseElement
			return newMockRelease(releaseElement), nil
		}
	}

	mockCharts = append(mockCharts, *releaseElement)
	return newMockRelease(releaseElement), nil
}

// Show values/readme/chart etc
func (hpm *helmMockPackageManager) Show(showOpts options.ShowOptions) ([]byte, error) {
	switch showOpts.OutputFormat {
	case options.ShowChart:
		return []byte(MockDataChart), nil
	case options.ShowReadme:
		return []byte(MockDataReadme), nil
	case options.ShowValues:
		return []byte(MockDataValues), nil
	}
	return nil, nil
}

// Get release details - all, hooks, manifest, notes and values
func (hpm *helmMockPackageManager) Get(getOpts options.GetOptions) ([]byte, error) {
	switch getOpts.ReleaseResource {
	case options.GetAll:
		return []byte(strings.Join([]string{MockReleaseHooks, MockReleaseManifest, MockReleaseNotes, MockReleaseValues}, "---\n")), nil
	case options.GetHooks:
		return []byte(MockReleaseHooks), nil
	case options.GetManifest:
		return []byte(MockReleaseManifest), nil
	case options.GetNotes:
		return []byte(MockReleaseNotes), nil
	case options.GetValues:
		return []byte(MockReleaseValues), nil
	default:
		return nil, errors.New("invalid release resource")
	}
}

// Uninstall a helm chart (not thread safe)
func (hpm *helmMockPackageManager) Uninstall(uninstallOpts options.UninstallOptions) error {
	for i, rel := range mockCharts {
		if rel.Name == uninstallOpts.Name && rel.Namespace == uninstallOpts.Namespace {
			mockCharts = append(mockCharts[:i], mockCharts[i+1:]...)
		}
	}
	return nil
}

// List a helm chart (not thread safe)
func (hpm *helmMockPackageManager) List(listOpts options.ListOptions) ([]release.ReleaseElement, error) {
	return mockCharts, nil
}

const mockPortainerIndex = `apiVersion: v1
entries:
  portainer:
  - apiVersion: v2
    appVersion: 2.0.0
    created: "2020-12-01T21:51:37.367634957Z"
    description: Helm chart used to deploy the Portainer for Kubernetes
    digest: f0e13dd3e7a05d17cb35c7879ffa623fd43b2c10ca968203e302b7a6c2764ddb
    home: https://www.portainer.io
    icon: https://github.com/portainer/portainer/raw/develop/app/assets/ico/apple-touch-icon.png
    maintainers:
    - email: davidy@funkypenguin.co.nz
      name: funkypenguin
      url: https://www.funkypenguin.co.nz
    name: portainer
    sources:
    - https://github.com/portainer/k8s
    type: application
    urls:
    - https://github.com/portainer/k8s/releases/download/portainer-1.0.6/portainer-1.0.6.tgz
    version: 1.0.6
generated: "2020-08-19T00:00:46.754739363Z"`

func (hbpm *helmMockPackageManager) SearchRepo(searchRepoOpts options.SearchRepoOptions) ([]byte, error) {
	// Always return the same repo data no matter what
	reader := strings.NewReader(mockPortainerIndex)

	var file release.File
	err := yaml.NewDecoder(reader).Decode(&file)
	if err != nil {
		return nil, errors.Wrap(err, "failed to decode index file")
	}

	result, err := json.Marshal(file)
	if err != nil {
		return nil, errors.Wrap(err, "failed to marshal index file")
	}

	return result, nil
}
