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

// RGResource flattens resource for rg (resource group).
// Return error in case resource is not found on the platform.
// Flatten errors are added to tflog.
func RGResource(ctx context.Context, plan *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start flattens.RGResource")

	diags := diag.Diagnostics{}

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

	recordRG, err := utilities.RGCheckPresence(ctx, rgId, c)
	if err != nil {
		diags.AddError(fmt.Sprintf("Cannot get info about resource group with ID %v", rgId), err.Error())
		return diags
	}

	tflog.Info(ctx, "flattens.RGResource: before flatten", map[string]any{"rg_id": rgId, "recordRG": recordRG})

	*plan = models.ResourceRGModel{
		AccountID: types.Int64Value(int64(recordRG.AccountID)),
		GID:       types.Int64Value(int64(recordRG.GID)),
		Name:      types.StringValue(recordRG.Name),

		DefNetType:  plan.DefNetType,
		IPCIDR:      plan.IPCIDR,
		Quota:       flattenQuota(ctx, &recordRG.ResourceLimits),
		ExtNetID:    plan.ExtNetID,
		ExtIP:       plan.ExtIP,
		Owner:       plan.Owner,
		Access:      flattenAccess(ctx, &recordRG.ACL),
		DefNet:      plan.DefNet,
		Description: types.StringValue(recordRG.Description),
		Force:       plan.Force,
		Permanently: plan.Permanently,
		Restore:     plan.Restore,
		Enable:      types.BoolValue(recordRG.Status == "ENABLED"),
		Timeouts:    plan.Timeouts,

		RGID:                   types.Int64Value(int64(recordRG.ID)),
		LastUpdated:            plan.LastUpdated,
		AccountName:            types.StringValue(recordRG.AccountName),
		ACL:                    flattenACL(ctx, &recordRG.ACL),
		CPUAllocationParameter: types.StringValue(recordRG.CPUAllocationParameter),
		CPUAllocationRatio:     types.Float64Value(recordRG.CPUAllocationRatio),
		DefNetID:               types.Int64Value(recordRG.DefNetID),
		CreatedBy:              types.StringValue(recordRG.CreatedBy),
		CreatedTime:            types.Int64Value(int64(recordRG.CreatedTime)),
		DeletedBy:              types.StringValue(recordRG.DeletedBy),
		DeletedTime:            types.Int64Value(int64(recordRG.DeletedTime)),
		Dirty:                  types.BoolValue(recordRG.Dirty),
		GUID:                   types.Int64Value(int64(recordRG.GUID)),
		Id:                     types.StringValue(strconv.Itoa(int(recordRG.ID))),
		LockStatus:             types.StringValue(recordRG.LockStatus),
		Milestones:             types.Int64Value(int64(recordRG.Milestones)),
		Secret:                 types.StringValue(recordRG.Secret),
		Status:                 types.StringValue(recordRG.Status),
		UpdatedBy:              types.StringValue(recordRG.UpdatedBy),
		UpdatedTime:            types.Int64Value(int64(recordRG.UpdatedTime)),
	}

	var diagsItem diag.Diagnostics
	plan.UniqPools, diagsItem = types.ListValueFrom(ctx, types.StringType, recordRG.UniqPools)
	if diagsItem != nil {
		tflog.Error(ctx, fmt.Sprint("flattens.RGResource: cannot flatten recordRG.UniqPools to plan.UniqPools", diags))
	}
	plan.VINS, diagsItem = types.ListValueFrom(ctx, types.Int64Type, recordRG.VINS)
	if diagsItem.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.RGResource: cannot flatten recordRG.VINS to plan.VINS", diags))
	}
	plan.ResTypes, diagsItem = types.ListValueFrom(ctx, types.StringType, recordRG.ResTypes)
	if diagsItem.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.RGResource: cannot flatten recordRG.ResTypes to plan.ResTypes", diags))
	}
	plan.VMS, diagsItem = types.ListValueFrom(ctx, types.Int64Type, recordRG.Computes)
	if diagsItem.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.RGResource: cannot flatten recordRG.Computes to plan.Computes", diags))
	}
	plan.ComputeFeatures, diagsItem = types.ListValueFrom(ctx, types.StringType, recordRG.ComputeFeatures)
	if diagsItem.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.RGResource: cannot flatten recordRG.ComputeFeatures to recordRG.ComputeFeatures", diags))
	}

	tflog.Info(ctx, "flattenResourceRG: after flatten", map[string]any{"rg_id": plan.Id.ValueString()})

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

func flattenACL(ctx context.Context, item *rg.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, diags := types.ObjectValueFrom(ctx, models.ItemACL, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenACLItems struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

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

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

func flattenQuota(ctx context.Context, item *rg.ResourceLimits) types.Object {
	tflog.Info(ctx, "Start flattenQuota")
	tempStruct := models.QuotaModel{
		CPU:        types.Int64Value(int64(item.CUC)),
		Ram:        types.Int64Value(int64(item.CUM)),
		Disk:       types.Int64Value(int64(item.CUDM)),
		ExtTraffic: types.Int64Value(int64(item.CUNP)),
		ExtIps:     types.Int64Value(int64(item.CUI)),
		GpuUnits:   types.Int64Value(int64(item.GPUUnits)),
		CuD:        types.Int64Value(int64(item.CUD)),
	}
	quota, diags := types.ObjectValueFrom(ctx, models.ItemQuota, tempStruct)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenQuota", diags))
	}
	return quota
}

func flattenAccess(ctx context.Context, aclList *rg.ListACL) types.Set {
	tflog.Info(ctx, "Start flattenAccess")
	tempSlice := make([]types.Object, 0, len(*aclList)-1)
	for i, item := range *aclList {
		if i == 0 {
			continue
		}
		temp := models.AccessModel{
			Right: types.StringValue(item.Right),
			User:  types.StringValue(item.UserGroupID),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemAccess, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenAccess struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, diags := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemAccess}, tempSlice)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenAccess", diags))
	}

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

func flattenDefNet(ctx context.Context, dnType string, dnId int64) types.Object {
	tflog.Info(ctx, "Start flattenDefNet")
	tempStruct := models.DefNetModel{
		NetType: types.StringValue(dnType),
		NetId:   types.Int64Value(int64(dnId)),
	}
	quota, diags := types.ObjectValueFrom(ctx, models.ItemDefNet, tempStruct)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenDefNet", diags))
	}
	return quota
}
