// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package manager

import (
	"context"
	"encoding/base64"

	"golang.org/x/xerrors"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/proto"

	"github.com/gitpod-io/gitpod/common-go/kubernetes"
	wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
	"github.com/gitpod-io/gitpod/common-go/tracing"
	csapi "github.com/gitpod-io/gitpod/content-service/api"
	"github.com/gitpod-io/gitpod/content-service/pkg/layer"
	regapi "github.com/gitpod-io/gitpod/registry-facade/api"
)

// GetImageSpec provides the image spec for a particular workspace (instance) ID.
func (m *Manager) GetImageSpec(ctx context.Context, req *regapi.GetImageSpecRequest) (resp *regapi.GetImageSpecResponse, err error) {
	pod, err := m.findWorkspacePod(ctx, req.Id)
	if isKubernetesObjNotFoundError(err) {
		return nil, status.Error(codes.NotFound, "not found")
	}

	span, ctx := tracing.FromContext(ctx, "GetImageSpec")
	tracing.ApplyOWI(span, wsk8s.GetOWIFromObject(&pod.ObjectMeta))
	defer func() {
		tracing.LogMessageSafe(span, "resp", resp)
		tracing.FinishSpan(span, &err)
	}()

	ispec, ok := pod.Annotations[kubernetes.WorkspaceImageSpecAnnotation]
	if !ok {
		return nil, status.Error(codes.FailedPrecondition, "workspace has no image spec")
	}
	spec, err := regapi.ImageSpecFromBase64(ispec)
	if err != nil {
		return nil, status.Error(codes.Internal, err.Error())
	}

	_, fullWorkspaceBackupEnabled := pod.Labels[fullWorkspaceBackupLabel]
	_, pvcFeatureEnabled := pod.Labels[pvcWorkspaceFeatureLabel]

	if fullWorkspaceBackupEnabled || pvcFeatureEnabled {
		owner := pod.Labels[wsk8s.OwnerLabel]
		workspaceID := pod.Labels[wsk8s.MetaIDLabel]
		initializerRaw, ok := pod.Annotations[workspaceInitializerAnnotation]
		if !ok {
			return nil, xerrors.Errorf("pod %s has no %s annotation", pod.Name, workspaceInitializerAnnotation)
		}
		initializerPB, err := base64.StdEncoding.DecodeString(initializerRaw)
		if err != nil {
			return nil, xerrors.Errorf("cannot decode init config: %w", err)
		}
		var initializer csapi.WorkspaceInitializer
		err = proto.Unmarshal(initializerPB, &initializer)
		if err != nil {
			return nil, xerrors.Errorf("cannot unmarshal init config: %w", err)
		}
		var cl []layer.Layer
		if pvcFeatureEnabled {
			cl, _, err = m.Content.GetContentLayerPVC(ctx, owner, workspaceID, &initializer)
		} else {
			cl, _, err = m.Content.GetContentLayer(ctx, owner, workspaceID, &initializer)
		}
		if err != nil {
			return nil, xerrors.Errorf("cannot get content layer: %w", err)
		}

		contentLayer := make([]*regapi.ContentLayer, len(cl))
		for i, l := range cl {
			if len(l.Content) > 0 {
				contentLayer[i] = &regapi.ContentLayer{
					Spec: &regapi.ContentLayer_Direct{
						Direct: &regapi.DirectContentLayer{
							Content: l.Content,
						},
					},
				}
				continue
			}

			diffID := l.DiffID
			contentLayer[i] = &regapi.ContentLayer{
				Spec: &regapi.ContentLayer_Remote{
					Remote: &regapi.RemoteContentLayer{
						DiffId:    diffID,
						Digest:    l.Digest,
						MediaType: string(l.MediaType),
						Url:       l.URL,
						Size:      l.Size,
					},
				},
			}
		}
		spec.ContentLayer = contentLayer
	}

	return &regapi.GetImageSpecResponse{
		Spec: spec,
	}, nil
}
