// Copyright 2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.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 ssmdocresource implements the methods to access resources from ssm
package ssmdocresource

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/aws/amazon-ssm-agent/agent/appconfig"
	"github.com/aws/amazon-ssm-agent/agent/context"
	"github.com/aws/amazon-ssm-agent/agent/fileutil/filemanager"
	"github.com/aws/amazon-ssm-agent/agent/framework/docparser"
	"github.com/aws/amazon-ssm-agent/agent/jsonutil"
	"github.com/aws/amazon-ssm-agent/agent/plugins/downloadcontent/remoteresource"
	"github.com/aws/amazon-ssm-agent/agent/plugins/downloadcontent/system"
	ssmsvc "github.com/aws/amazon-ssm-agent/agent/ssm"
	"github.com/aws/aws-sdk-go/service/ssm"
)

// S3Resource is a struct for the remote resource of type git
type SSMDocResource struct {
	context   context.T
	Info      SSMDocInfo
	ssmdocdep ssmdeps
}

// S3Info represents the sourceInfo type sent by runcommand
type SSMDocInfo struct {
	DocName string `json:"name"`
}

// NewS3Resource is a constructor of type GitResource
func NewSSMDocResource(context context.T, info string) (*SSMDocResource, error) {
	ssmDocInfo, err := parseSourceInfo(info)
	if err != nil {
		return nil, fmt.Errorf("SSMDocument SourceInfo parsing failed. %v", err)
	}

	return &SSMDocResource{
		context: context,
		Info:    ssmDocInfo,
		ssmdocdep: &ssmDocDepImpl{
			ssmSvc: ssmsvc.NewService(context),
		},
	}, nil
}

// parseSourceInfo unmarshals the information in sourceInfo of type GitInfo and returns it
func parseSourceInfo(sourceInfo string) (ssmdoc SSMDocInfo, err error) {

	if err = jsonutil.Unmarshal(sourceInfo, &ssmdoc); err != nil {
		return ssmdoc, errors.New("SourceInfo could not be unmarshalled for SourceType SSMDocument. Please check JSON format of SourceInfo")
	}

	return ssmdoc, nil
}

// DownloadRemoteResource calls download to pull down files or directory from s3
func (ssmdoc *SSMDocResource) DownloadRemoteResource(filesys filemanager.FileSystem, destinationPath string) (err error, result *remoteresource.DownloadResult) {
	log := ssmdoc.context.Log()
	if destinationPath == "" {
		destinationPath = appconfig.DownloadRoot
	}

	result = &remoteresource.DownloadResult{}

	docName, docVersion := docparser.ParseDocumentNameAndVersion(ssmdoc.Info.DocName)
	log.Debug("Making a call to get document", docName, docVersion)
	var docResponse *ssm.GetDocumentOutput
	if docResponse, err = ssmdoc.ssmdocdep.GetDocument(log, docName, docVersion); err != nil {
		log.Errorf("Unable to get ssm document. %v", err)
		return err, nil
	}

	var destinationFilePath string
	if filesys.Exists(destinationPath) && filesys.IsDirectory(destinationPath) || os.IsPathSeparator(destinationPath[len(destinationPath)-1]) {
		destinationFilePath = filepath.Join(destinationPath, filepath.Base(docName)+remoteresource.JSONExtension)

	} else {
		destinationFilePath = destinationPath
	}
	if err = system.SaveFileContent(log, filesys, destinationFilePath, *docResponse.Content); err != nil {
		log.Errorf("Error saving file - %v", err)
		return err, nil
	}

	result.Files = append(result.Files, destinationFilePath)

	return nil, result
}

// ValidateLocationInfo ensures that the required parameters of SourceInfo are specified
func (s3 *SSMDocResource) ValidateLocationInfo() (valid bool, err error) {
	if s3.Info.DocName == "" {
		return false, errors.New("SSM Document name in SourceType must be specified")
	}
	return true, nil
}
