package flattens

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/google/uuid"
	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-log/tflog"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/flattens"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/utilities"
)

func DataSourceComputeList(ctx context.Context, state *models.ListComputesModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start FlattenDataSourceComputeList")

	diags := diag.Diagnostics{}

	list, err := utilities.ComputeListCheckPresence(ctx, state, c)
	if err != nil {
		diags.AddError("Error get compute list info", err.Error())
		return diags
	}

	id := uuid.New()

	*state = models.ListComputesModel{
		ByID:           state.ByID,
		Name:           state.Name,
		AccountID:      state.AccountID,
		RGName:         state.RGName,
		RGID:           state.RGID,
		TechStatus:     state.TechStatus,
		Status:         state.Status,
		IPAddress:      state.IPAddress,
		ExtNetName:     state.ExtNetName,
		ExtNetID:       state.ExtNetID,
		IncludeDeleted: state.IncludeDeleted,
		SortBy:         state.SortBy,
		Page:           state.Page,
		Size:           state.Size,
		IgnoreK8s:      state.IgnoreK8s,
		Timeouts:       state.Timeouts,
		Id:             types.StringValue(id.String()),
		Items:          flattenItemsList(ctx, list),
		EntryCount:     types.Int64Value(int64(list.EntryCount)),
	}

	tflog.Info(ctx, "End FlattenDataSourceComputeList")
	return nil
}

func flattenItemsList(ctx context.Context, computes *compute.ListComputes) []models.ItemComputeModel {
	tflog.Info(ctx, "Start flattenItemsList")

	var diags diag.Diagnostics

	res := make([]models.ItemComputeModel, 0, len(computes.Data))
	for _, item := range computes.Data {
		customFields, _ := json.Marshal(item.CustomFields)
		devices, _ := json.Marshal(item.Devices)
		temp := models.ItemComputeModel{
			ACL:                    flattenACLInList(ctx, &item.ACL),
			AccountID:              types.Int64Value(int64(item.AccountID)),
			AccountName:            types.StringValue(item.AccountName),
			AffinityLabel:          types.StringValue(item.AffinityLabel),
			AffinityRules:          flattenAffinityRuleInList(ctx, &item.AffinityRules),
			AffinityWeight:         types.Int64Value(int64(item.AffinityWeight)),
			AntiAffinityRules:      flattenAffinityRuleInList(ctx, &item.AntiAffinityRules),
			Architecture:           types.StringValue(item.Architecture),
			AutoStartWithNode:      types.BoolValue(item.AutoStart),
			BootDiskSize:           types.Int64Value(int64(item.BootDiskSize)),
			BootOrder:              flattens.FlattenSimpleTypeToList(ctx, types.StringType, &item.BootOrder),
			BootType:               types.StringValue(item.BootType),
			CdImageId:              types.Int64Value(int64(item.CdImageId)),
			Clones:                 flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &item.Clones),
			CloneReference:         types.Int64Value(int64(item.CloneReference)),
			Chipset:                types.StringValue(item.Chipset),
			ComputeCIID:            types.Int64Value(int64(item.ComputeCIID)),
			CPU:                    types.Int64Value(int64(item.CPU)),
			CPUPin:                 types.BoolValue(item.CPUPin),
			CreatedBy:              types.StringValue(item.CreatedBy),
			CreatedTime:            types.Int64Value(int64(item.CreatedTime)),
			CustomFields:           types.StringValue(string(customFields)),
			DeletedBy:              types.StringValue(item.DeletedBy),
			DeletedTime:            types.Int64Value(int64(item.DeletedTime)),
			Description:            types.StringValue(item.Description),
			Devices:                types.StringValue(string(devices)),
			Disks:                  flattenDisksInList(ctx, &item.Disks),
			Driver:                 types.StringValue(item.Driver),
			GID:                    types.Int64Value(int64(item.GID)),
			GUID:                   types.Int64Value(int64(item.GUID)),
			HPBacked:               types.BoolValue(item.HPBacked),
			HotResize:              types.BoolValue(item.HotResize),
			ComputeId:              types.Int64Value(int64(item.ID)),
			ImageID:                types.Int64Value(int64(item.ImageID)),
			Interfaces:             flattenInterfaceInList(ctx, &item.Interfaces),
			LockStatus:             types.StringValue(item.LockStatus),
			LoaderType:             types.StringValue(item.LoaderType),
			ManagerID:              types.Int64Value(int64(item.ManagerID)),
			ManagerType:            types.StringValue(item.ManagerType),
			MigrationJob:           types.Int64Value(int64(item.MigrationJob)),
			Milestones:             types.Int64Value(int64(item.Milestones)),
			Name:                   types.StringValue(item.Name),
			NeedReboot:             types.BoolValue(item.NeedReboot),
			NetworkInterfaceNaming: types.StringValue(item.NetworkInterfaceNaming),
			NumaAffinity:           types.StringValue(item.NumaAffinity),
			NumaNodeId:             types.Int64Value(int64(item.NumaNodeId)),
			//Pinned:            types.BoolValue(item.Pinned),
			PreferredCPU:     flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &item.PreferredCPU),
			RAM:              types.Int64Value(int64(item.RAM)),
			ReferenceID:      types.StringValue(item.ReferenceID),
			Registered:       types.BoolValue(item.Registered),
			ResName:          types.StringValue(item.ResName),
			ReservedNodeCpus: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &item.ReservedNodeCpus),
			RGID:             types.Int64Value(int64(item.RGID)),
			RGName:           types.StringValue(item.RGName),
			SnapSets:         flattenSnapSetInList(ctx, &item.SnapSets),
			StatelessSepID:   types.Int64Value(int64(item.StatelessSepID)),
			StatelessSepType: types.StringValue(item.StatelessSepType),
			Status:           types.StringValue(item.Status),
			TechStatus:       types.StringValue(item.TechStatus),
			TotalDiskSize:    types.Int64Value(int64(item.TotalDiskSize)),
			UpdatedBy:        types.StringValue(item.UpdatedBy),
			UpdatedTime:      types.Int64Value(int64(item.UpdatedTime)),
			UserManaged:      types.BoolValue(item.UserManaged),
			VINSConnected:    types.Int64Value(int64(item.VINSConnected)),
			VirtualImageID:   types.Int64Value(int64(item.VirtualImageID)),
			VGPUs:            flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &item.VGPUs),
		}

		temp.Tags, diags = types.MapValueFrom(ctx, types.StringType, item.Tags)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenItemsList", diags))
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenItemsList")
	return res
}

func flattenACLInList(ctx context.Context, acl *compute.ListACL) []models.ItemACLInListModel {
	tflog.Info(ctx, "Start flattenACLInList")

	res := make([]models.ItemACLInListModel, 0, len(*acl))
	for _, item := range *acl {
		temp := models.ItemACLInListModel{
			Explicit:    types.BoolValue(bool(item.Explicit)),
			GUID:        types.StringValue(item.GUID),
			Right:       types.StringValue(item.Right),
			Status:      types.StringValue(item.Status),
			Type:        types.StringValue(item.Type),
			UserGroupID: types.StringValue(item.UserGroupID),
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenACLInList")
	return res
}

func flattenAffinityRuleInList(ctx context.Context, rules *compute.ListRules) []models.ItemRuleInListModel {
	tflog.Info(ctx, "Start flattenAffinityRuleInList")

	res := make([]models.ItemRuleInListModel, 0, len(*rules))
	for _, item := range *rules {
		temp := models.ItemRuleInListModel{
			GUID:     types.StringValue(item.GUID),
			Key:      types.StringValue(item.Key),
			Mode:     types.StringValue(item.Mode),
			Policy:   types.StringValue(item.Policy),
			Topology: types.StringValue(item.Topology),
			Value:    types.StringValue(item.Value),
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenAffinityRuleInList")
	return res
}

func flattenDisksInList(ctx context.Context, disks *compute.ListInfoDisks) []models.DiskInListModel {
	tflog.Info(ctx, "Start flattenDisksInList")

	res := make([]models.DiskInListModel, 0, len(*disks))
	for _, item := range *disks {
		temp := models.DiskInListModel{
			//BusNumber: types.Int64Value(int64(item.BusNumber)),
			DiskId: types.Int64Value(int64(item.ID)),
			//PCISlot:   types.Int64Value(item.PCISlot),
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenDisksInList")
	return res
}

func flattenInterfaceInList(ctx context.Context, interfaces *compute.ListInterfaces) []models.ItemVNFInterfaceInListModel {
	tflog.Info(ctx, "Start flattenInterfaceInList")

	var diags diag.Diagnostics

	res := make([]models.ItemVNFInterfaceInListModel, 0, len(*interfaces))
	for _, item := range *interfaces {
		temp := models.ItemVNFInterfaceInListModel{
			BusNumber:   types.Int64Value(int64(item.BusNumber)),
			ConnID:      types.Int64Value(int64(item.ConnID)),
			ConnType:    types.StringValue(item.ConnType),
			DefGW:       types.StringValue(item.DefGW),
			Enabled:     types.BoolValue(item.Enabled),
			FLIPGroupID: types.Int64Value(int64(item.FLIPGroupID)),
			GUID:        types.StringValue(item.GUID),
			IPAddress:   types.StringValue(item.IPAddress),
			LibvirtSettings: &models.LibvirtModel{
				GUID:        types.StringValue(item.LibvirtSettings.GUID),
				TXMode:      types.StringValue(item.LibvirtSettings.TXMode),
				IOEventFD:   types.StringValue(item.LibvirtSettings.IOEventFD),
				EventIDx:    types.StringValue(item.LibvirtSettings.EventIDx),
				Queues:      types.Int64Value(int64(item.LibvirtSettings.Queues)),
				RXQueueSize: types.Int64Value(int64(item.LibvirtSettings.RXQueueSize)),
				TXQueueSize: types.Int64Value(int64(item.LibvirtSettings.TXQueueSize)),
			},
			ListenSSH: types.BoolValue(item.ListenSSH),
			MAC:       types.StringValue(item.MAC),
			Name:      types.StringValue(item.Name),
			NetID:     types.Int64Value(int64(item.NetID)),
			NetMask:   types.Int64Value(int64(item.NetMask)),
			NetType:   types.StringValue(item.NetType),
			NodeID:    types.Int64Value(int64(item.NodeID)),
			PCISlot:   types.Int64Value(item.PCISlot),
			QOS: &models.QOSInListModel{
				ERate:   types.Int64Value(int64(item.QOS.ERate)),
				GUID:    types.StringValue(item.QOS.GUID),
				InBurst: types.Int64Value(int64(item.QOS.InBurst)),
				InRate:  types.Int64Value(int64(item.QOS.InRate)),
			},
			Target: types.StringValue(item.Target),
			Type:   types.StringValue(item.Type),
		}
		temp.VNFs, diags = types.ListValueFrom(ctx, types.Int64Type, item.VNFs)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenInterfaceInList", diags))
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenInterfaceInList")
	return res
}

func flattenSnapSetInList(ctx context.Context, snapSet *compute.ListSnapSets) []models.ItemSnapSetInListModel {
	tflog.Info(ctx, "Start flattenSnapSetInList")

	var diags diag.Diagnostics

	res := make([]models.ItemSnapSetInListModel, 0, len(*snapSet))
	for _, item := range *snapSet {
		temp := models.ItemSnapSetInListModel{
			GUID:      types.StringValue(item.GUID),
			Label:     types.StringValue(item.Label),
			Timestamp: types.Int64Value(int64(item.Timestamp)),
		}
		temp.Disks, diags = types.ListValueFrom(ctx, types.Int64Type, item.Disks)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenSnapSetInList", diags))
		}
		res = append(res, temp)
	}

	tflog.Info(ctx, "End flattenSnapSetInList")
	return res
}
