package rg

import (
	log "github.com/sirupsen/logrus"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/rg"
)

func flattenResgroup(d *schema.ResourceData, rgData *rg.RecordRG) {
	log.Debugf("flattenResgroup: decoded RG name %q / ID %d, account ID %d",
		rgData.Name, rgData.ID, rgData.AccountID)

	d.Set("account_id", rgData.AccountID)
	d.Set("account_name", rgData.AccountName)
	d.Set("acl", flattenRgAcl(rgData.ACL))
	d.Set("compute_features", rgData.ComputeFeatures)
	d.Set("cpu_allocation_parameter", rgData.CPUAllocationParameter)
	d.Set("cpu_allocation_ratio", rgData.CPUAllocationRatio)
	d.Set("created_by", rgData.CreatedBy)
	d.Set("created_time", rgData.CreatedTime)
	d.Set("def_net_id", rgData.DefNetID)
	d.Set("def_net_type", rgData.DefNetType)
	d.Set("deleted_by", rgData.DeletedBy)
	d.Set("deleted_time", rgData.DeletedTime)
	d.Set("desc", rgData.Description)
	d.Set("dirty", rgData.Dirty)
	d.Set("gid", rgData.GID)
	d.Set("guid", rgData.GUID)
	d.Set("rg_id", rgData.ID)
	d.Set("lock_status", rgData.LockStatus)
	d.Set("milestones", rgData.Milestones)
	d.Set("name", rgData.Name)
	d.Set("resource_limits", flattenRgResourceLimits(rgData.ResourceLimits))
	d.Set("resource_types", rgData.ResTypes)
	d.Set("secret", rgData.Secret)
	d.Set("status", rgData.Status)
	d.Set("uniq_pools", rgData.UniqPools)
	d.Set("updated_by", rgData.UpdatedBy)
	d.Set("updated_time", rgData.UpdatedTime)
	d.Set("vins", rgData.VINS)
	d.Set("computes", rgData.VMs)
	d.Set("sdn_access_group_id", rgData.SDNAccessGroupID)
	d.Set("storage_policy_ids", rgData.StoragePolicyIDs)
}

func flattenRgAcl(rgACLs rg.ListACL) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(rgACLs))
	for _, acl := range rgACLs {
		temp := map[string]interface{}{
			"email":         acl.Email,
			"explicit":      acl.Explicit,
			"guid":          acl.GUID,
			"right":         acl.Right,
			"status":        acl.Status,
			"type":          acl.Type,
			"user_group_id": acl.UserGroupID,
		}

		res = append(res, temp)
	}

	return res
}

func flattenRgResourceLimits(rl rg.ResourceLimits) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"cu_c":           rl.CUC,
		"cu_d":           rl.CuD,
		"cu_dm":          rl.CUDM,
		"cu_i":           rl.CUI,
		"cu_m":           rl.CUM,
		"gpu_units":      rl.GPUUnits,
		"storage_policy": flattenRgStoragePolicy(rl.StoragePolicies),
	}
	res = append(res, temp)

	return res

}

func flattenRgAudits(rgAudits rg.ListAudits) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(rgAudits))
	for _, rgAudit := range rgAudits {
		temp := map[string]interface{}{
			"call":         rgAudit.Call,
			"responsetime": rgAudit.ResponseTime,
			"statuscode":   rgAudit.StatusCode,
			"timestamp":    rgAudit.Timestamp,
			"user":         rgAudit.User,
		}

		res = append(res, temp)
	}

	return res
}

func flattenRgListGroups(list *rg.ListAffinityGroup) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(list.Data))
	for _, item := range list.Data {
		for key, vals := range item {
			for _, v := range vals {
				temp := map[string]interface{}{
					"label":   key,
					"id":      v.ID,
					"node_id": v.NodeID,
				}
				res = append(res, temp)
			}
		}
	}

	return res
}

func flattenRgAffinityGroupComputes(list rg.ListAffinityGroupCompute) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(list))

	for _, item := range list {
		temp := map[string]interface{}{
			"compute_id":               item.ComputeID,
			"other_node":               item.OtherNode,
			"other_node_indirect":      item.OtherNodeIndirect,
			"other_node_indirect_soft": item.OtherNodeIndirectSoft,
			"other_node_soft":          item.OtherNodeSoft,
			"same_node":                item.SameNode,
			"same_node_soft":           item.SameNodeSoft,
		}
		res = append(res, temp)
	}

	return res
}

func flattenRGResourceConsumption(d *schema.ResourceData, rg *rg.ItemResourceConsumption) {
	d.Set("consumed", flattenResource(rg.Consumed))
	d.Set("reserved", flattenResource(rg.Reserved))
	d.Set("resource_limits", flattenRgResourceLimits(rg.ResourceLimits))
	d.Set("rg_id", rg.RGID)
}

func flattenRGResourceConsumptionList(rg *rg.ListResourceConsumption) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(rg.Data))
	for _, rc := range rg.Data {
		temp := map[string]interface{}{
			"consumed":        flattenResource(rc.Consumed),
			"reserved":        flattenResource(rc.Reserved),
			"resource_limits": flattenRgResourceLimits(rc.ResourceLimits),
			"rg_id":           rc.RGID,
		}
		res = append(res, temp)
	}
	return res
}

func flattenRgUsageResource(d *schema.ResourceData, usage rg.Reservation) {
	d.Set("cpu", usage.CPU)
	d.Set("disk_size", usage.DiskSize)
	d.Set("disk_size_max", usage.DiskSizeMax)
	d.Set("extips", usage.ExtIPs)
	d.Set("gpu", usage.GPU)
	d.Set("ram", usage.RAM)
	d.Set("seps", flattenRGSeps(usage.SEPs))
}

func flattenResource(resource rg.Reservation) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"cpu":           resource.CPU,
		"disk_size":     resource.DiskSize,
		"disk_size_max": resource.DiskSizeMax,
		"extips":        resource.ExtIPs,
		"gpu":           resource.GPU,
		"ram":           resource.RAM,
		"seps":          flattenRGSeps(resource.SEPs),
	}

	res = append(res, temp)

	return res
}

func flattenRGSeps(seps map[string]map[string]rg.DiskUsage) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	for sepKey, sepVal := range seps {
		for dataKey, dataVal := range sepVal {
			temp := map[string]interface{}{
				"sep_id":        sepKey,
				"data_name":     dataKey,
				"disk_size":     dataVal.DiskSize,
				"disk_size_max": dataVal.DiskSizeMax,
			}
			res = append(res, temp)
		}
	}

	return res
}

func flattenRgListComputes(lc *rg.ListComputes) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(lc.Data))
	for _, compute := range lc.Data {
		temp := map[string]interface{}{
			"account_id":         compute.AccountID,
			"account_name":       compute.AccountName,
			"affinity_label":     compute.AffinityLabel,
			"affinity_rules":     flattenRules(compute.AffinityRules),
			"affinity_weight":    compute.AffinityWeight,
			"antiaffinity_rules": flattenRules(compute.AntiAffinityRules),
			"cpus":               compute.CPUs,
			"created_by":         compute.CreatedBy,
			"created_time":       compute.CreatedTime,
			"deleted_by":         compute.DeletedBy,
			"deleted_time":       compute.DeletedTime,
			"id":                 compute.ID,
			"name":               compute.Name,
			"ram":                compute.RAM,
			"registered":         compute.Registered,
			"rg_id":              compute.RGID,
			"rg_name":            compute.RGName,
			"status":             compute.Status,
			"tech_status":        compute.TechStatus,
			"total_disks_size":   compute.TotalDisksSize,
			"updated_by":         compute.UpdatedBy,
			"updated_time":       compute.UpdatedTime,
			"user_managed":       compute.UserManaged,
			"vins_connected":     compute.VINSConnected,
		}

		res = append(res, temp)
	}

	return res
}

func flattenRules(list rg.ListRules) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(list))
	for _, rule := range list {
		temp := map[string]interface{}{
			"guid":     rule.GUID,
			"key":      rule.Key,
			"mode":     rule.Mode,
			"policy":   rule.Policy,
			"topology": rule.Topology,
			"value":    rule.Value,
		}

		res = append(res, temp)
	}

	return res
}

func flattenRgListLb(listLb *rg.ListLB) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(listLb.Data))
	for _, lb := range listLb.Data {
		temp := map[string]interface{}{
			"ha_mode":        lb.HAMode,
			"acl":            lb.ACL,
			"backends":       flattenBackends(lb.Backends),
			"created_by":     lb.CreatedBy,
			"created_time":   lb.CreatedTime,
			"deleted_by":     lb.DeletedBy,
			"deleted_time":   lb.DeletedTime,
			"desc":           lb.Description,
			"dp_api_user":    lb.DPAPIUser,
			"extnet_id":      lb.ExtNetID,
			"frontends":      flattenFrontends(lb.Frontends),
			"gid":            lb.GID,
			"guid":           lb.GUID,
			"id":             lb.ID,
			"image_id":       lb.ImageID,
			"milestones":     lb.Milestones,
			"name":           lb.Name,
			"primary_node":   flattenNode(lb.PrimaryNode),
			"rg_name":        lb.RGName,
			"secondary_node": flattenNode(lb.SecondaryNode),
			"status":         lb.Status,
			"tech_status":    lb.TechStatus,
			"updated_by":     lb.UpdatedBy,
			"updated_time":   lb.UpdatedTime,
			"vins_id":        lb.VINSID,
		}
		res = append(res, temp)
	}
	return res
}

func flattenNode(node rg.RecordNode) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"backend_ip":  node.BackendIP,
		"compute_id":  node.ComputeID,
		"frontend_ip": node.FrontendIP,
		"guid":        node.GUID,
		"mgmt_ip":     node.MGMTIP,
		"network_id":  node.NetworkID,
	}
	res = append(res, temp)
	return res
}

func flattenFrontends(list rg.ListFrontends) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(list))
	for _, front := range list {
		temp := map[string]interface{}{
			"backend":  front.Backend,
			"bindings": flattenBindings(front.Bindings),
			"guid":     front.GUID,
			"name":     front.Name,
		}
		res = append(res, temp)
	}

	return res
}

func flattenBindings(list rg.ListBindings) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(list))
	for _, bind := range list {
		temp := map[string]interface{}{
			"address": bind.Address,
			"guid":    bind.GUID,
			"name":    bind.Name,
			"port":    bind.Port,
		}
		res = append(res, temp)
	}

	return res
}

func flattenBackends(b rg.ListBackends) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(b))
	for _, item := range b {
		temp := map[string]interface{}{
			"algorithm":               item.Algorithm,
			"guid":                    item.GUID,
			"name":                    item.Name,
			"server_default_settings": flattenServerSettings(item.ServerDefaultSettings),
			"servers":                 flattenListServers(item.Servers),
		}
		res = append(res, temp)
	}
	return res
}

func flattenListServers(list rg.ListServers) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(list))
	for _, serv := range list {
		temp := map[string]interface{}{
			"address":         serv.Address,
			"check":           serv.Check,
			"guid":            serv.GUID,
			"name":            serv.Name,
			"port":            serv.Port,
			"server_settings": flattenServerSettings(serv.ServerSettings),
		}
		res = append(res, temp)
	}

	return res
}

func flattenServerSettings(settings rg.ServerSettings) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"inter":      settings.Inter,
		"guid":       settings.GUID,
		"down_inter": settings.DownInter,
		"rise":       settings.Rise,
		"fall":       settings.Fall,
		"slow_start": settings.SlowStart,
		"max_conn":   settings.MaxConn,
		"max_queue":  settings.MaxQueue,
		"weight":     settings.Weight,
	}
	res = append(res, temp)
	return res
}

func flattenRgList(rgl *rg.ListRG) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(rgl.Data))
	for _, rg := range rgl.Data {
		temp := map[string]interface{}{
			"account_id":               rg.AccountID,
			"account_name":             rg.AccountName,
			"acl":                      flattenRgAcl(rg.ACL),
			"compute_features":         rg.ComputeFeatures,
			"cpu_allocation_parameter": rg.CPUAllocationParameter,
			"cpu_allocation_ratio":     rg.CPUAllocationRatio,
			"created_by":               rg.CreatedBy,
			"created_time":             rg.CreatedTime,
			"def_net_id":               rg.DefNetID,
			"def_net_type":             rg.DefNetType,
			"deleted_by":               rg.DeletedBy,
			"deleted_time":             rg.DeletedTime,
			"desc":                     rg.Description,
			"dirty":                    rg.Dirty,
			"gid":                      rg.GID,
			"guid":                     rg.GUID,
			"rg_id":                    rg.ID,
			"lock_status":              rg.LockStatus,
			"milestones":               rg.Milestones,
			"name":                     rg.Name,
			"resource_limits":          flattenRgResourceLimits(rg.ResourceLimits),
			"secret":                   rg.Secret,
			"status":                   rg.Status,
			"updated_by":               rg.UpdatedBy,
			"updated_time":             rg.UpdatedTime,
			"vins":                     rg.VINS,
			"vms":                      rg.VMs,
			"sdn_access_group_id":      rg.SDNAccessGroupID,
			"storage_policy_ids":       rg.StoragePolicyIDs,
		}
		res = append(res, temp)
	}
	return res

}

func flattenRgListVins(lv *rg.ListVINS) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(lv.Data))
	for _, vins := range lv.Data {
		temp := map[string]interface{}{
			"account_id":     vins.AccountID,
			"account_name":   vins.AccountName,
			"computes":       vins.Computes,
			"created_by":     vins.CreatedBy,
			"created_time":   vins.CreatedTime,
			"deleted_by":     vins.DeletedBy,
			"deleted_time":   vins.DeletedTime,
			"external_ip":    vins.ExternalIP,
			"extnet_id":      vins.ExtnetId,
			"free_ips":       vins.FreeIPs,
			"id":             vins.ID,
			"name":           vins.Name,
			"network":        vins.Network,
			"pri_vnf_dev_id": vins.PriVNFDevID,
			"rg_id":          vins.RGID,
			"rg_name":        vins.RGName,
			"status":         vins.Status,
			"updated_by":     vins.UpdatedBy,
			"updated_time":   vins.UpdatedTime,
		}

		res = append(res, temp)
	}

	return res
}

func flattenRgListPfw(listPfw *rg.ListPFW) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(listPfw.Data))
	for _, pfw := range listPfw.Data {
		temp := map[string]interface{}{
			"public_port_end":   pfw.PublicPortEnd,
			"public_port_start": pfw.PublicPortStart,
			"vm_id":             pfw.VMID,
			"vm_ip":             pfw.VMIP,
			"vm_name":           pfw.VMName,
			"vm_port":           pfw.VMPort,
			"vins_id":           pfw.VINSID,
			"vins_name":         pfw.VINSName,
		}
		res = append(res, temp)
	}

	return res
}

func flattenResourceRG(d *schema.ResourceData, rgData *rg.RecordRG) {
	d.Set("account_id", rgData.AccountID)
	d.Set("gid", rgData.GID)
	d.Set("rg_name", rgData.Name)
	d.Set("resource_limits", flattenRgResourceLimits(rgData.ResourceLimits))
	d.Set("storage_policy", flattenRgStoragePolicy(rgData.ResourceLimits.StoragePolicies))
	d.Set("description", rgData.Description)
	d.Set("uniq_pools", rgData.UniqPools)
	d.Set("cpu_allocation_parameter", rgData.CPUAllocationParameter)
	d.Set("cpu_allocation_ratio", rgData.CPUAllocationRatio)
	d.Set("acl", flattenRgAcl(rgData.ACL))
	d.Set("account_name", rgData.AccountName)
	d.Set("created_by", rgData.CreatedBy)
	d.Set("created_time", rgData.CreatedTime)
	d.Set("def_net_id", rgData.DefNetID)
	d.Set("deleted_by", rgData.DeletedBy)
	d.Set("deleted_time", rgData.DeletedTime)
	d.Set("guid", rgData.GUID)
	d.Set("rg_id", rgData.ID)
	d.Set("lock_status", rgData.LockStatus)
	d.Set("milestones", rgData.Milestones)
	d.Set("resource_types", rgData.ResTypes)
	d.Set("secret", rgData.Secret)
	d.Set("status", rgData.Status)
	d.Set("updated_by", rgData.UpdatedBy)
	d.Set("updated_time", rgData.UpdatedTime)
	d.Set("vins", rgData.VINS)
	d.Set("vms", rgData.VMs)
	d.Set("sdn_access_group_id", rgData.SDNAccessGroupID)
	d.Set("storage_policy_ids", rgData.StoragePolicyIDs)
}

func flattenRgStoragePolicy(spList []rg.StoragePolicy) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(spList))
	for _, sp := range spList {
		temp := map[string]interface{}{
			"id":    sp.ID,
			"limit": sp.Limit,
		}

		res = append(res, temp)
	}

	return res
}
