// Copyright Project Harbor 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 assembler

import (
	"context"

	"github.com/goharbor/harbor/src/controller/scan"
	"github.com/goharbor/harbor/src/lib"
	"github.com/goharbor/harbor/src/lib/log"
	v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
	"github.com/goharbor/harbor/src/server/v2.0/handler/model"
)

const (
	vulnerabilitiesAddition = "vulnerabilities"
	startTime               = "start_time"
	endTime                 = "end_time"
	scanStatus              = "scan_status"
	sbomDigest              = "sbom_digest"
	duration                = "duration"
)

// NewScanReportAssembler returns vul assembler
func NewScanReportAssembler(option *model.OverviewOptions, mimeTypes []string) *ScanReportAssembler {
	return &ScanReportAssembler{
		overviewOption: option,
		scanChecker:    scan.NewChecker(),
		scanCtl:        scan.DefaultController,
		mimeTypes:      mimeTypes,
	}
}

// ScanReportAssembler vul assembler
type ScanReportAssembler struct {
	scanChecker scan.Checker
	scanCtl     scan.Controller

	artifacts      []*model.Artifact
	mimeTypes      []string
	overviewOption *model.OverviewOptions
}

// WithArtifacts set artifacts for the assembler
func (assembler *ScanReportAssembler) WithArtifacts(artifacts ...*model.Artifact) *ScanReportAssembler {
	assembler.artifacts = artifacts

	return assembler
}

// Assemble assemble vul for the artifacts
func (assembler *ScanReportAssembler) Assemble(ctx context.Context) error {
	version := lib.GetAPIVersion(ctx)

	for _, artifact := range assembler.artifacts {
		isScannable, err := assembler.scanChecker.IsScannable(ctx, &artifact.Artifact)
		if err != nil {
			log.Errorf("check the scannable status of %s@%s failed, error: %v", artifact.RepositoryName, artifact.Digest, err)
			continue
		}

		if !isScannable {
			continue
		}

		artifact.SetAdditionLink(vulnerabilitiesAddition, version)

		if assembler.overviewOption.WithVuln {
			for _, mimeType := range assembler.mimeTypes {
				overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{mimeType})
				if err != nil {
					log.Warningf("get scan summary of artifact %s@%s for %s failed, error:%v", artifact.RepositoryName, artifact.Digest, mimeType, err)
				} else if len(overview) > 0 {
					artifact.ScanOverview = overview
					break
				}
			}
		}
		if assembler.overviewOption.WithSBOM {
			overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{v1.MimeTypeSBOMReport})
			if err != nil {
				log.Warningf("get scan summary of artifact %s@%s for %s failed, error:%v", artifact.RepositoryName, artifact.Digest, v1.MimeTypeSBOMReport, err)
			} else if len(overview) > 0 {
				artifact.SBOMOverView = map[string]interface{}{
					startTime:  overview[startTime],
					endTime:    overview[endTime],
					scanStatus: overview[scanStatus],
					sbomDigest: overview[sbomDigest],
					duration:   overview[duration],
				}
			}
		}
	}

	return nil
}
