// Copyright 2020 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 testing

import (
	"path/filepath"

	"android/soong/android"
	"android/soong/testing/code_metadata_internal_proto"
	"github.com/google/blueprint"

	"google.golang.org/protobuf/proto"
)

func CodeMetadataFactory() android.Module {
	module := &CodeMetadataModule{}

	android.InitAndroidModule(module)
	android.InitDefaultableModule(module)
	module.AddProperties(&module.properties)

	return module
}

type CodeMetadataModule struct {
	android.ModuleBase
	android.DefaultableModuleBase

	// Properties for "code_metadata"
	properties struct {
		// Specifies the name of the code_config.
		Name string
		// Specifies the team ID.
		TeamId string
		// Specifies the list of modules that this code_metadata covers.
		Code []string
		// An optional field to specify if multiple ownerships for source files is allowed.
		MultiOwnership bool
	}
}

type codeDepTagType struct {
	blueprint.BaseDependencyTag
}

var codeDepTag = codeDepTagType{}

func (module *CodeMetadataModule) DepsMutator(ctx android.BottomUpMutatorContext) {
	// Validate Properties
	if len(module.properties.TeamId) == 0 {
		ctx.PropertyErrorf(
			"TeamId",
			"Team Id not found in the code_metadata module. Hint: Maybe the teamId property hasn't been properly specified.",
		)
	}
	if !isInt(module.properties.TeamId) {
		ctx.PropertyErrorf(
			"TeamId", "Invalid value for Team ID. The Team ID must be an integer.",
		)
	}
	if len(module.properties.Code) == 0 {
		ctx.PropertyErrorf(
			"Code",
			"Targets to be attributed cannot be empty. Hint: Maybe the code property hasn't been properly specified.",
		)
	}
	ctx.AddDependency(ctx.Module(), codeDepTag, module.properties.Code...)
}

// Provider published by CodeMetadata
type CodeMetadataProviderData struct {
	IntermediatePath android.WritablePath
}

var CodeMetadataProviderKey = blueprint.NewProvider[CodeMetadataProviderData]()

func (module *CodeMetadataModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	metadataList := make(
		[]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0,
		len(module.properties.Code),
	)
	bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile())

	for _, m := range ctx.GetDirectDepsWithTag(codeDepTag) {
		targetName := m.Name()
		var moduleSrcs []string
		if srcsFileInfo, ok := android.OtherModuleProvider(ctx, m, blueprint.SrcsFileProviderKey); ok {
			moduleSrcs = srcsFileInfo.SrcPaths
		}
		if module.properties.MultiOwnership {
			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
				TargetName:     &targetName,
				TrendyTeamId:   &module.properties.TeamId,
				Path:           &bpFilePath,
				MultiOwnership: &module.properties.MultiOwnership,
				SourceFiles:    moduleSrcs,
			}
			metadataList = append(metadataList, metadata)
		} else {
			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
				TargetName:   &targetName,
				TrendyTeamId: &module.properties.TeamId,
				Path:         &bpFilePath,
				SourceFiles:  moduleSrcs,
			}
			metadataList = append(metadataList, metadata)
		}

	}
	codeMetadata := &code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
	protoData, err := proto.Marshal(codeMetadata)
	if err != nil {
		ctx.ModuleErrorf("Error marshaling code metadata: %s", err.Error())
		return
	}
	intermediatePath := android.PathForModuleOut(
		ctx, "intermediateCodeMetadata.pb",
	)
	android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData))

	android.SetProvider(ctx,
		CodeMetadataProviderKey,
		CodeMetadataProviderData{IntermediatePath: intermediatePath},
	)
}
