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-framework/types/basetypes"
	"github.com/hashicorp/terraform-plugin-log/tflog"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/lb"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/k8s/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/k8s/utilities"
)

func K8SCPResource(ctx context.Context, plan *models.ResourceK8SCPModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start FlattenK8SCPResource", map[string]any{"k8s_id": plan.Id.ValueString()})

	diags := diag.Diagnostics{}

	k8SID, err := strconv.ParseUint(plan.Id.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("Cannot parsed ID cluster from state", err.Error())
		return diags
	}

	cluster, err := utilities.K8SCPResourceCheckPresence(ctx, k8SID, c)
	if err != nil {
		diags.AddError(fmt.Sprintf("Cannot get info about cluster with ID %d", k8SID), err.Error())
		return diags
	}

	k8sList, err := utilities.K8sListForResourceCheckPresence(ctx, k8SID, c)
	if err != nil || len(k8sList.Data) == 0 {
		diags.AddError(fmt.Sprintf("Cluster with ID %v not found in List", k8SID), err.Error())
		return diags
	}

	masterComputeList := make([]*compute.RecordCompute, 0, len(cluster.K8SGroups.Masters.DetailedInfo))
	for _, masterNode := range cluster.K8SGroups.Masters.DetailedInfo {
		compute, err := utilities.ComputeCheckPresence(ctx, masterNode.ID, c)
		if err != nil {
			diags.AddError(fmt.Sprintf("Cannot get info about masterNode with ID %d", masterNode.ID), err.Error())
			return diags
		}
		masterComputeList = append(masterComputeList, compute)
	}

	*plan = models.ResourceK8SCPModel{
		Name:                   types.StringValue(cluster.Name),
		RGID:                   types.Int64Value(int64(cluster.RGID)),
		K8SCIID:                types.Int64Value(int64(cluster.CIID)),
		NetworkPlugin:          types.StringValue(cluster.NetworkPlugin),
		SEPID:                  plan.SEPID,
		SEPPool:                plan.SEPPool,
		WithLB:                 types.BoolValue(cluster.WithLB),
		HighlyAvailable:        types.BoolValue(cluster.HighlyAvailableLB),
		AdditionalSANs:         plan.AdditionalSANs,
		InitConfiguration:      plan.InitConfiguration,
		ClusterConfiguration:   plan.ClusterConfiguration,
		KubeletConfiguration:   plan.KubeletConfiguration,
		KubeProxyConfiguration: plan.KubeProxyConfiguration,
		JoinConfiguration:      plan.JoinConfiguration,
		Description:            types.StringValue(cluster.Description),
		ExtNetOnly:             types.BoolValue(cluster.ExtnetOnly),
		OidcCertificate:        plan.OidcCertificate,
		Chipset:                plan.Chipset,
		Start:                  types.BoolValue(cluster.TechStatus == "STARTED"),
		Enabled:                types.BoolValue(cluster.Status == "ENABLED"),
		Permanently:            plan.Permanently,
		Restore:                plan.Restore,
		Timeouts:               plan.Timeouts,
		LBSysctlParams:         plan.LBSysctlParams,
		Id:                     types.StringValue(strconv.Itoa(int(cluster.ID))),
		LastUpdated:            plan.LastUpdated,
		DetailedInfo:           flattenDetailedInfo(ctx, &cluster.K8SGroups.Masters.DetailedInfo, masterComputeList),
		MasterGroupId:          types.Int64Value(int64(cluster.K8SGroups.Masters.ID)),
		MasterGroupName:        types.StringValue(cluster.K8SGroups.Masters.Name),
		Num:                    types.Int64Value(int64(cluster.K8SGroups.Masters.Num)),
		CPU:                    types.Int64Value(int64(cluster.K8SGroups.Masters.CPU)),
		RAM:                    types.Int64Value(int64(cluster.K8SGroups.Masters.RAM)),
		Disk:                   types.Int64Value(int64(cluster.K8SGroups.Masters.Disk)),
		ACL:                    flattenACL(ctx, &cluster.ACL),
		AccountID:              types.Int64Value(int64(cluster.AccountID)),
		AccountName:            types.StringValue(cluster.AccountName),
		BServiceID:             types.Int64Value(int64(cluster.BServiceID)),
		CreatedBy:              types.StringValue(cluster.CreatedBy),
		CreatedTime:            types.Int64Value(int64(cluster.CreatedTime)),
		DeletedBy:              types.StringValue(cluster.DeletedBy),
		DeletedTime:            types.Int64Value(int64(cluster.DeletedTime)),
		K8SAddressVIP:          flattenK8SAddressVIP(ctx, cluster.AddressVIP),
		K8SID:                  types.Int64Value(int64(cluster.ID)),
		K8CIName:               types.StringValue(cluster.K8CIName),
		LBID:                   types.Int64Value(int64(cluster.LBID)),
		RGName:                 types.StringValue(cluster.RGName),
		Status:                 types.StringValue(cluster.Status),
		TechStatus:             types.StringValue(cluster.TechStatus),
		UpdatedBy:              types.StringValue(cluster.UpdatedBy),
		UpdatedTime:            types.Int64Value(int64(cluster.UpdatedTime)),
		VinsId:                 types.Int64Value(int64(k8sList.Data[0].VINSID)),
	}

	if plan.AdditionalSANs.IsNull() {
		plan.AdditionalSANs = types.ListNull(types.StringType)
	}

	if cluster.LBID != 0 {
		lb, err := c.CloudAPI().LB().Get(ctx, lb.GetRequest{LBID: cluster.LBID})
		if err != nil {
			tflog.Error(ctx, fmt.Sprintf("Cannot get info about LB with ID %d", cluster.LBID)+err.Error())
		}
		plan.ExtNetID = types.Int64Value(int64(lb.ExtNetID))
		plan.LBIP = types.StringValue(lb.PrimaryNode.FrontendIP)
	} else {
		plan.ExtNetID = types.Int64Value(0)
	}

	kubeconfig, err := c.CloudAPI().K8S().GetConfig(ctx, k8s.GetConfigRequest{K8SID: k8SID})
	if err != nil {
		tflog.Error(ctx, "Could not get kubeconfig:"+err.Error())
	}
	plan.Kubeconfig = types.StringValue(kubeconfig)

	tflog.Info(ctx, "End FlattenK8SCPResource", map[string]any{"k8s_id": plan.K8SID.ValueInt64()})
	return nil
}

func flattenK8SAddressVIP(ctx context.Context, addressedVip k8s.K8SAddressVIP) basetypes.ObjectValue {
	tflog.Info(ctx, "Start flattenAddressVIP")

	temp := models.AddressVIPModel{
		BackendIP:  types.StringValue(addressedVip.BackendIP),
		FrontendIP: types.StringValue(addressedVip.FrontendIP),
	}

	res, err := types.ObjectValueFrom(ctx, models.AddressVIP, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenK8SAddressVIP struct to obj", err))
	}

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

func flattenDetailedInfo(ctx context.Context, di *k8s.ListDetailedInfo, computes []*compute.RecordCompute) types.List {
	tflog.Info(ctx, "Start flattenDetailedInfo")

	tempSlice := make([]types.Object, 0, len(*di))
	for i, detailedInfo := range *di {
		temp := models.ItemDetailedInfoModel{
			ComputeId:          types.Int64Value(int64(detailedInfo.ID)),
			Name:               types.StringValue(detailedInfo.Name),
			Status:             types.StringValue(detailedInfo.Status),
			TechStatus:         types.StringValue(detailedInfo.TechStatus),
			Interfaces:         flattenInterfaces(ctx, &computes[i].Interfaces),
			NatableVinsIp:      types.StringValue(computes[i].NatableVINSIP),
			NatableVinsNetwork: types.StringValue(computes[i].NatableVINSNetwork),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemDetailedInfo, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenDetailedInfo struct to obj", err))
		}

		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemDetailedInfo}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenDetailedInfo", err))
	}

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

func flattenInterfaces(ctx context.Context, interfaces *compute.ListInterfaces) types.List {
	tflog.Info(ctx, "Start flattenInterfaces")

	tempSlice := make([]types.Object, 0, len(*interfaces))
	for _, interfaceCompute := range *interfaces {
		temp := models.ItemInterfacesModel{
			DefGw:     types.StringValue(interfaceCompute.DefGW),
			IpAddress: types.StringValue(interfaceCompute.IPAddress),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemInterfaces, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenInterfaces struct to obj", err))
		}

		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemInterfaces}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenInterfaces", err))
	}

	tflog.Info(ctx, "End flattenInterfaces")

	return res
}

func flattenACL(ctx context.Context, acl *k8s.RecordACL) types.Object {
	tflog.Info(ctx, "Start flattenACL")

	temp := models.RecordACLModel{
		AccountACL: flattenACLItems(ctx, &acl.AccountACL),
		K8SACL:     flattenACLItems(ctx, &acl.K8SACL),
		RGACL:      flattenACLItems(ctx, &acl.RGACL),
	}

	res, err := types.ObjectValueFrom(ctx, models.ListACL, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenACL struct to obj", err))
	}

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

func flattenACLItems(ctx context.Context, item *k8s.ListACL) types.List {
	tflog.Info(ctx, "Start flattenACLItems")
	tempSlice := make([]types.Object, 0, len(*item))
	for _, aclItem := range *item {
		temp := models.ItemACLModel{
			Explicit:    types.BoolValue(aclItem.Explicit),
			GUID:        types.StringValue(aclItem.GUID),
			Right:       types.StringValue(aclItem.Right),
			Status:      types.StringValue(aclItem.Status),
			Type:        types.StringValue(aclItem.Type),
			UserGroupID: types.StringValue(aclItem.UserGroupID),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemAcl, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenACLItems struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemAcl}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenACLItems", err))
	}

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