// Copyright 2019 Yunion
//
// 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 compute

import (
	"net/http"
	"reflect"
	"time"

	"yunion.io/x/cloudmux/pkg/apis/compute"
	"yunion.io/x/cloudmux/pkg/cloudprovider"
	"yunion.io/x/pkg/errors"
	"yunion.io/x/pkg/gotypes"
	"yunion.io/x/pkg/util/regutils"
	"yunion.io/x/pkg/utils"

	"yunion.io/x/onecloud/pkg/apis"
	"yunion.io/x/onecloud/pkg/httperrors"
)

const (
	BUCKET_OPS_STATS_CHANGE = "stats_change"

	BUCKET_STATUS_START_CREATE = "start_create"
	BUCKET_STATUS_CREATING     = "creating"
	BUCKET_STATUS_READY        = compute.BUCKET_STATUS_READY
	BUCKET_STATUS_CREATE_FAIL  = "create_fail"
	BUCKET_STATUS_START_DELETE = "start_delete"
	BUCKET_STATUS_DELETING     = "deleting"
	BUCKET_STATUS_DELETED      = "deleted"
	BUCKET_STATUS_DELETE_FAIL  = "delete_fail"
	BUCKET_STATUS_UNKNOWN      = "unknown"

	BUCKET_UPLOAD_OBJECT_KEY_HEADER          = "X-Yunion-Bucket-Upload-Key"
	BUCKET_UPLOAD_OBJECT_ACL_HEADER          = "X-Yunion-Bucket-Upload-Acl"
	BUCKET_UPLOAD_OBJECT_STORAGECLASS_HEADER = "X-Yunion-Bucket-Upload-Storageclass"
)

type BucketCreateInput struct {
	apis.SharableVirtualResourceCreateInput
	CloudregionResourceInput
	CloudproviderResourceInput

	StorageClass string `json:"storage_class"`

	EnablePerfMon *bool `json:"enable_perf_mon"`
}

type BucketDetails struct {
	apis.SharableVirtualResourceDetails
	ManagedResourceInfo
	CloudregionResourceInfo

	SBucket

	// 访问URL列表
	AccessUrls []cloudprovider.SBucketAccessUrl `json:"access_urls"`
}

func (bucket BucketDetails) GetMetricTags() map[string]string {
	ret := map[string]string{
		"id":             bucket.Id,
		"brand":          bucket.Brand,
		"cloudregion":    bucket.Cloudregion,
		"cloudregion_id": bucket.CloudregionId,
		"domain_id":      bucket.DomainId,
		"oss_id":         bucket.Id,
		"oss_name":       bucket.Name,
		"project_domain": bucket.ProjectDomain,
		"region_ext_id":  bucket.RegionExtId,
		"status":         bucket.Status,
		"tenant":         bucket.Project,
		"tenant_id":      bucket.ProjectId,
		"account":        bucket.Account,
		"account_id":     bucket.AccountId,
		"external_id":    bucket.ExternalId,
	}
	return AppendMetricTags(ret, bucket.MetadataResourceInfo, bucket.ProjectizedResourceInfo)
}

func (bucket BucketDetails) GetMetricPairs() map[string]string {
	ret := map[string]string{}
	return ret
}

type BucketObjectsActionInput struct {
	Key []string `json:"key"`
}

type BucketAclInput struct {
	BucketObjectsActionInput

	Acl cloudprovider.TBucketACLType
}

func (input *BucketAclInput) Validate() error {
	switch input.Acl {
	case cloudprovider.ACLPrivate, cloudprovider.ACLAuthRead, cloudprovider.ACLPublicRead, cloudprovider.ACLPublicReadWrite:
		// do nothing
	default:
		return errors.Wrap(httperrors.ErrInputParameter, "acl")
	}
	return nil
}

type BucketMetadataInput struct {
	BucketObjectsActionInput

	Metadata http.Header
}

func (input *BucketMetadataInput) Validate() error {
	if len(input.Key) == 0 {
		return errors.Wrap(httperrors.ErrEmptyRequest, "key")
	}
	if len(input.Metadata) == 0 {
		return errors.Wrap(httperrors.ErrEmptyRequest, "metadata")
	}
	return nil
}

type BucketListInput struct {
	apis.SharableVirtualResourceListInput
	apis.ExternalizedResourceBaseListInput

	ManagedResourceListInput
	RegionalFilterListInput

	// STORAGE_CLASS
	StorageClass []string `json:"storage_class"`

	// 位置
	Location []string `json:"location"`

	// ACL
	Acl []string `json:"acl"`
}

type BucketSyncstatusInput struct {
}

type BucketUpdateInput struct {
	apis.SharableVirtualResourceBaseUpdateInput

	EnablePerfMon *bool `json:"enable_perf_mon"`
}

type BucketPerformTempUrlInput struct {
	// 访问对象方法
	Method string `json:"method"`
	// 对象KEY
	// required:true
	Key string `json:"key"`
	// 过期时间，单位秒
	ExpireSeconds *int `json:"expire_seconds"`
}

type BucketPerformTempUrlOutput struct {
	// 生成的临时URL
	Url string `json:"url"`
}

type BucketPerformMakedirInput struct {
	// 目录对象KEY
	// required:true
	Key string `json:"key"`
}

type BucketPerformDeleteInput struct {
	// 待删除对象KEY
	// required:true
	Keys []string `json:"keys"`
}

type BucketGetAclInput struct {
	// 对象KEY
	// required:false
	Key string `json:"key"`
}

type BucketGetAclOutput struct {
	// ACL
	Acl string `json:"acl"`
}

type BucketGetObjectsInput struct {
	// Prefix
	Prefix string `json:"prefix"`
	// 是否模拟列举目录模式
	Recursive *bool `json:"recursive"`
	// 分页标识
	PagingMarker string `json:"paging_marker"`
	// 最大输出条目数
	Limit *int `json:"limit"`
}

type BucketGetObjectsOutput struct {
	// 对象列表
	Data []cloudprovider.SCloudObject `json:"data"`
	// 排序字段，总是key
	// example: key
	MarkerField string `json:"marker_field"`
	// 排序顺序，总是降序
	// example: DESC
	MarkerOrder string `json:"marker_order"`
	// 下一页请求的paging_marker标识
	NextMarker string `json:"next_marker"`
}

type BucketWebsiteRoutingRule struct {
	ConditionErrorCode string `json:"condition_error_code"`
	ConditionPrefix    string `json:"condition_prefix"`

	RedirectProtocol         string `json:"redirect_protocol"`
	RedirectReplaceKey       string `json:"redirect_replace_key"`
	RedirectReplaceKeyPrefix string `json:"redirect_replace_key_prefix"`
}

type BucketWebsiteConf struct {
	// 主页
	Index string `json:"index"`
	// 错误时返回的文档
	ErrorDocument string `json:"error_document"`
	// http或https
	Protocol string `json:"protocol"`

	Rules []BucketWebsiteRoutingRule `json:"rules"`
	// 访问网站url
	Url string `json:"url"`
}

func (input *BucketWebsiteConf) Validate() error {
	if len(input.Index) == 0 {
		return httperrors.NewMissingParameterError("index")
	}
	if len(input.ErrorDocument) == 0 {
		return httperrors.NewMissingParameterError("error_document")
	}
	if len(input.Protocol) == 0 {
		return httperrors.NewMissingParameterError("protocol")
	}
	return nil
}

type BucketCORSRule struct {
	AllowedMethods []string `json:"allowed_methods"`
	// 允许的源站，可以是*
	AllowedOrigins []string `json:"allowed_origins"`
	AllowedHeaders []string `json:"allowed_headers"`
	MaxAgeSeconds  int      `json:"max_age_seconds"`
	ExposeHeaders  []string `json:"expose_headers"`
	// 规则区别标识
	Id string `json:"id"`
}

type BucketCORSRules struct {
	Data []BucketCORSRule `json:"data"`
}

type BucketCORSRuleDeleteInput struct {
	Id []string `json:"id"`
}

type BucketPolicy struct {
	Data []BucketPolicyStatement `json:"data"`
}

type BucketPolicyStatement struct {
	// 授权的目标主体
	Principal map[string][]string `json:"Principal,omitempty"`
	// 授权的行为
	Action []string `json:"Action,omitempty"`
	// Allow|Deny
	Effect string `json:"Effect,omitempty"`
	// 被授权的资源地址
	Resource []string `json:"Resource,omitempty"`
	// 触发授权的条件
	Condition map[string]map[string]interface{} `json:"Condition,omitempty"`

	// 解析字段，主账号id:子账号id
	PrincipalId []string `json:"principal_id"`
	// map[主账号id:子账号id]子账号name
	PrincipalNames map[string]string `json:"principal_names"`

	// Read|ReadWrite|FullControl
	CannedAction string `json:"canned_action"`
	// 资源路径
	ResourcePath []string `json:"resource_path"`
	// 根据index 生成
	Id string `json:"id"`
}

type BucketPolicyStatementInput struct {
	// 主账号id:子账号id
	PrincipalId []string `json:"principal_id"`
	// Read|ReadWrite|FullControl
	CannedAction string `json:"canned_action"`
	// Allow|Deny
	Effect string `json:"effect"`
	// 被授权的资源地址,/*
	ResourcePath []string `json:"resource_path"`
	// ip 条件
	IpEquals    []string `json:"ip_equals"`
	IpNotEquals []string `json:"ip_not_equals"`
}

func (input *BucketPolicyStatementInput) Validate() error {
	cannedAction := []string{"Read", "ReadWrite", "FullControl"}
	if !utils.IsInStringArray(input.CannedAction, cannedAction) {
		return httperrors.NewInputParameterError("invalid CannedAction %s ", input.CannedAction)
	}
	if input.Effect != "Allow" && input.Effect != "Deny" {
		return httperrors.NewInputParameterError("invalid Effect %s ", input.Effect)
	}
	for i := range input.IpEquals {
		if !regutils.MatchIP4Addr(input.IpEquals[i]) && !regutils.MatchCIDR(input.IpEquals[i]) {
			return httperrors.NewInputParameterError("invalid ipv4 %s ", input.IpEquals[i])
		}
	}
	for i := range input.IpNotEquals {
		if !regutils.MatchIP4Addr(input.IpNotEquals[i]) && !regutils.MatchCIDR(input.IpNotEquals[i]) {
			return httperrors.NewInputParameterError("invalid ipv4 %s ", input.IpNotEquals[i])
		}
	}
	return nil
}

type BucketPolicyDeleteInput struct {
	Id []string `json:"id"`
}

func (input *BucketCORSRules) Validate() error {
	for i := range input.Data {
		if len(input.Data[i].AllowedOrigins) == 0 {
			return httperrors.NewMissingParameterError("allowed_origins")
		}
		if len(input.Data[i].AllowedMethods) == 0 {
			return httperrors.NewMissingParameterError("allowed_methods")
		}
	}
	return nil
}

type BucketRefererConf struct {
	// Referer Type
	// enmu: Black-List, White-List
	RefererType string `json:"referer_type"`
	// 域名列表
	DomainList []string `json:"domain_list"`
	// 是否允许空referer 访问
	AllowEmptyRefer bool `json:"allow_empty_refer"`

	// 是否开启
	Enabled bool `json:"enabled"`
}

func (input *BucketRefererConf) Validate() error {
	return nil
}

func init() {
	gotypes.RegisterSerializable(reflect.TypeOf(&SBackupStorageAccessInfo{}), func() gotypes.ISerializable {
		return &SBackupStorageAccessInfo{}
	})
}

type BucketProbeResult struct {
	UploadTime   time.Duration `json:"upload_time"`
	DownloadTime time.Duration `json:"download_time"`
	DeleteTime   time.Duration `json:"delete_time"`
}

func (result BucketProbeResult) UploadDelayMs() float64 {
	return float64(result.UploadTime) / float64(time.Millisecond)
}

func (result BucketProbeResult) DownloadDelayMs() float64 {
	return float64(result.DownloadTime) / float64(time.Millisecond)
}

func (result BucketProbeResult) DeleteDelayMs() float64 {
	return float64(result.DeleteTime) / float64(time.Millisecond)
}

func (result BucketProbeResult) UploadThroughputMbps(sizeMBytes int) float64 {
	return float64(sizeMBytes) * 8 / float64(result.UploadTime.Seconds())
}

func (result BucketProbeResult) DownloadThroughputMbps(sizeMBytes int) float64 {
	return float64(sizeMBytes) * 8 / float64(result.DownloadTime.Seconds())
}
