//  Copyright 2019 Google Inc. All Rights Reserved.
//
//  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 policies

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/GoogleCloudPlatform/osconfig/clog"
	"github.com/GoogleCloudPlatform/osconfig/packages"

	"cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb"
)

func yumRepositories(ctx context.Context, repos []*agentendpointpb.YumRepository, repoFile string) error {
	// TODO: Would it be easier to just use templates?
	/*
		# Repo file managed by Google OSConfig agent
		[repo1]
		name=repo1-name
		baseurl=https://repo1-url
		enabled=1
		gpgcheck=1
		gpgkey=http://repo1-url/gpg
		[repo2]
		display_name=repo2-name
		baseurl=https://repo2-url
		enabled=1
		gpgcheck=1
	*/
	var buf bytes.Buffer
	buf.WriteString("# Repo file managed by Google OSConfig agent\n")
	for _, repo := range repos {
		buf.WriteString(fmt.Sprintf("\n[%s]\n", repo.Id))
		if repo.DisplayName == "" {
			buf.WriteString(fmt.Sprintf("name=%s\n", repo.Id))
		} else {
			buf.WriteString(fmt.Sprintf("name=%s\n", repo.DisplayName))
		}
		buf.WriteString(fmt.Sprintf("baseurl=%s\n", repo.BaseUrl))
		buf.WriteString("enabled=1\ngpgcheck=1\n")
		if len(repo.GpgKeys) > 0 {
			buf.WriteString(fmt.Sprintf("gpgkey=%s\n", repo.GpgKeys[0]))
			for _, k := range repo.GpgKeys[1:] {
				buf.WriteString(fmt.Sprintf("       %s\n", k))
			}
		}
	}

	return writeIfChanged(ctx, buf.Bytes(), repoFile)
}

func yumChanges(ctx context.Context, yumInstalled, yumRemoved, yumUpdated []*agentendpointpb.Package) error {
	var err error
	var errs []string

	var installed []*packages.PkgInfo
	if len(yumInstalled) > 0 || len(yumUpdated) > 0 || len(yumRemoved) > 0 {
		installed, err = packages.InstalledRPMPackages(ctx)
		if err != nil {
			return err
		}
	}

	var updates []*packages.PkgInfo
	if len(yumUpdated) > 0 {
		updates, err = packages.YumUpdates(ctx)
		if err != nil {
			return err
		}
	}

	changes := getNecessaryChanges(installed, updates, yumInstalled, yumRemoved, yumUpdated)

	if changes.packagesToInstall != nil {
		clog.Infof(ctx, "Installing packages %s", changes.packagesToInstall)
		if err := packages.InstallYumPackages(ctx, changes.packagesToInstall); err != nil {
			errs = append(errs, fmt.Sprintf("error installing yum packages: %v", err))
		}
	}

	if changes.packagesToUpgrade != nil {
		clog.Infof(ctx, "Upgrading packages %s", changes.packagesToUpgrade)
		if err := packages.InstallYumPackages(ctx, changes.packagesToUpgrade); err != nil {
			errs = append(errs, fmt.Sprintf("error upgrading yum packages: %v", err))
		}
	}

	if changes.packagesToRemove != nil {
		clog.Infof(ctx, "Removing packages %s", changes.packagesToRemove)
		if err := packages.RemoveYumPackages(ctx, changes.packagesToRemove); err != nil {
			errs = append(errs, fmt.Sprintf("error removing yum packages: %v", err))
		}
	}

	if errs == nil {
		return nil
	}
	return errors.New(strings.Join(errs, ",\n"))
}
