package flattens

import (
	"context"
	"fmt"
	"strconv"

	"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/cloudbroker/node"
	"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/cloudbroker/node/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudbroker/node/utilities"
)

func NodeDataSource(ctx context.Context, state *models.DataSourceNode, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start flattens.NodeDataSource")

	diags := diag.Diagnostics{}

	nodeID := uint64(state.NodeID.ValueInt64())

	recordNode, diags := utilities.NodeDataSourceCheckPresence(ctx, nodeID, c)
	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "flattens.NodeDataSource: before flatten", map[string]any{"node_id": nodeID})

	*state = models.DataSourceNode{
		NodeID:   state.NodeID,
		Timeouts: state.Timeouts,

		Consumption:        flattenConsumpion(ctx, &recordNode.Consumption),
		CpuInfo:            flattenCpuInfo(ctx, &recordNode.CpuInfo),
		CPUAllocationRatio: types.Int64Value(int64(recordNode.CPUAllocationRatio)),
		DPDK:               flattenDPDK(ctx, &recordNode.DPDK),
		GID:                types.Int64Value(int64(recordNode.GID)),
		ID:                 types.StringValue(strconv.Itoa(int(recordNode.ID))),
		IPAddr:             flattens.FlattenSimpleTypeToList(ctx, types.StringType, &recordNode.IPAddr),
		IsolatedCPUs:       flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &recordNode.IsolatedCpus),
		Name:               types.StringValue(recordNode.Name),
		NeedReboot:         types.BoolValue(recordNode.NeedReboot),
		NetworkMode:        types.StringValue(recordNode.NetworkMode),
		NicInfo:            flattenNicInfo(ctx, recordNode.NicInfo),
		NumaTopology:       flattenNumaTopology(ctx, &recordNode.NumaTopology),
		ReservedCPUs:       flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &recordNode.ReservedCPUs),
		Roles:              flattens.FlattenSimpleTypeToList(ctx, types.StringType, &recordNode.Roles),
		SriovEnabled:       types.BoolValue(recordNode.SriovEnabled),
		StackId:            types.Int64Value(int64(recordNode.StackID)),
		Status:             types.StringValue(recordNode.Status),
		ToActive:           flattenRole(ctx, &recordNode.ToActive),
		ToInstalling:       flattenRole(ctx, &recordNode.ToInstalling),
		ToMaintenance:      flattenRole(ctx, &recordNode.ToMaintenance),
		ToRestricted:       flattenRole(ctx, &recordNode.ToRestricted),
		Version:            types.StringValue(recordNode.Version),
	}

	tflog.Info(ctx, "End flattens.NodeDataSource", map[string]any{"Node_id": state.NodeID.ValueInt64()})
	return nil
}

func flattenConsumpion(ctx context.Context, consumpion *node.ConsumptionInfo) *models.ConsumptionModel {
	tflog.Info(ctx, "Start flattenConsumpion")

	res := models.ConsumptionModel{
		Consumed: &models.ConsumedModel{
			RAM:      types.Int64Value(int64(consumpion.Consumed.RAM)),
			Computes: types.Int64Value(int64(consumpion.Consumed.Computes)),
			Routers:  types.Int64Value(int64(consumpion.Consumed.Routers)),
			VCPU:     types.Int64Value(int64(consumpion.Consumed.VCPU)),
		},
		Free: &models.ResourcesInfo{
			RAM: types.Int64Value(int64(consumpion.Free.RAM)),
		},
		Reserved: &models.ResourcesInfo{
			RAM: types.Int64Value(int64(consumpion.Reserved.RAM)),
		},
		Total: &models.ResourcesInfo{
			RAM: types.Int64Value(int64(consumpion.Total.RAM)),
		},
		Hostname: types.StringValue(consumpion.Hostname),
	}

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

func flattenCpuInfo(ctx context.Context, cpuInfo *node.CpuInfo) *models.CpuInfoModel {
	tflog.Info(ctx, "Start flattenCpuInfo")

	res := models.CpuInfoModel{
		ClockSpeed: types.Int64Value(int64(cpuInfo.ClockSpeed)),
		CoreCount:  types.Int64Value(int64(cpuInfo.CoreCount)),
		PhysCount:  types.Int64Value(int64(cpuInfo.PhysCount)),
	}

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

func flattenNicInfo(ctx context.Context, nicInfo []node.ItemNicInfo) []models.NicInfoModel {
	tflog.Info(ctx, "Start flattenNicInfo")

	res := make([]models.NicInfoModel, 0, len(nicInfo))

	for _, item := range nicInfo {
		temp := models.NicInfoModel{
			Driver:   types.StringValue(item.Driver),
			MaxVFS:   types.Int64Value(int64(item.MaxVFS)),
			NumaNode: types.Int64Value(int64(item.NumaNode)),
			NumVFS:   types.Int64Value(int64(item.NumVFS)),
			OSName:   types.StringValue(item.OSName),
			PCISlot:  types.StringValue(item.PCISlot),
			VFList:   flattenVFList(ctx, item.VFList),
		}

		res = append(res, temp)
	}

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

func flattenVFList(ctx context.Context, vfMap []interface{}) []models.VFList {
	tflog.Info(ctx, "Start flattenVFList")

	vfList := make([]models.VFList, 0, len(vfMap))

	for _, item := range vfMap {
		itemMap := item.(map[string]interface{})
		vf := models.VFList{
			FnID:    types.Int64Value(int64(itemMap["fnId"].(float64))),
			PCISlot: types.StringValue(itemMap["pciSlot"].(string)),
		}
		vfList = append(vfList, vf)
	}

	tflog.Info(ctx, "End flattenVFList")
	return vfList
}

func flattenNumaTopology(ctx context.Context, numaTopolpgy *node.NumaTopologyInfo) *models.NumaTopologyModel {
	tflog.Info(ctx, "Start flattenNumaTopology")

	res := models.NumaTopologyModel{
		NodeNum: types.Int64Value(int64(numaTopolpgy.NodeNum)),
		Nodes:   flattenNumaTopologyNodes(ctx, numaTopolpgy.Nodes),
	}

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

func flattenNumaTopologyNodes(ctx context.Context, nodes map[string]node.NodeInfo) []models.NumaTopologyNodes {
	tflog.Info(ctx, "Start flattenNumaTopologyNodes")

	res := make([]models.NumaTopologyNodes, 0, len(nodes))

	for _, item := range nodes {
		temp := models.NumaTopologyNodes{
			CPUList: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &item.CPUList),
			Memory: &models.NumaTopologyNodesMemory{
				OneG:  types.Int64Value(int64(item.Memory.OneG)),
				TwoM:  types.Int64Value(int64(item.Memory.TwoM)),
				Total: types.Int64Value(int64(item.Memory.Total)),
			},
		}
		res = append(res, temp)
	}

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

func flattenDPDK(ctx context.Context, dpdk *node.DPDK) *models.DPDKModel {
	tflog.Info(ctx, "Start flattenDPDK")
	hpMemory, diags := types.MapValueFrom(ctx, types.Int64Type, dpdk.HPMemory)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenDPDK", diags))
	}
	res := models.DPDKModel{
		Bridges: &models.Bridges{
			Backplane1: &models.Backplane1{
				Interfaces: flattens.FlattenSimpleTypeToList(ctx, types.StringType, &dpdk.Bridges.Backplane1.Interfaces),
				NumaNode:   types.Int64Value(int64(dpdk.Bridges.Backplane1.NumaNode)),
			},
		},
		HPMemory: hpMemory,
		PMDCPU:   flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, &dpdk.PMDCPU),
	}

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

func flattenRole(ctx context.Context, role *node.Role) *models.Role {
	tflog.Info(ctx, "Start flattenRole")

	res := models.Role{
		Actor:  types.StringValue(role.Actor),
		Reason: types.StringValue(role.Reason),
		Time:   types.Int64Value(int64(role.Time)),
	}

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