package flattens

import (
	"context"
	"fmt"
	"strconv"

	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"

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

// AccountResource flattens resource for account.
// Return error in case data source is not found on the platform.
// Flatten errors are added to tflog.
func AccountResource(ctx context.Context, state *models.ResourceAccountModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start flattens.AccountResource")

	diags := diag.Diagnostics{}

	accountId := uint64(state.AccountID.ValueInt64())
	if accountId == 0 {
		id, err := strconv.Atoi(state.Id.ValueString())
		if err != nil {
			diags.AddError(
				"flattens.AccountResource: cannot parse resource ID from state",
				err.Error())
			return diags
		}
		accountId = uint64(id)
	}

	recordAccount, err := utilities.AccountResourceCheckPresence(ctx, accountId, c)
	if err != nil {
		diags.AddError(fmt.Sprintf("flattens.AccountResource: Cannot get info about resource with ID %v", accountId), err.Error())
		return diags
	}

	tflog.Info(ctx, "flattens.AccountResource: before flatten", map[string]any{"account_id": accountId, "recordAccount": recordAccount})

	*state = models.ResourceAccountModel{
		AccountName:            types.StringValue(recordAccount.Name),
		Username:               state.Username,
		EmailAddress:           state.EmailAddress,
		SendAccessEmails:       state.SendAccessEmails,
		Users:                  state.Users,
		Restore:                state.Restore,
		Permanently:            state.Permanently,
		Enable:                 state.Enable,
		ResourceLimits:         flattenResourceLimitsInAccountResource(ctx, recordAccount.ResourceLimits, state),
		AvailableTemplates:     state.AvailableTemplates,
		Timeouts:               state.Timeouts,
		Id:                     types.StringValue(strconv.Itoa(int(accountId))),
		AccountID:              types.Int64Value(int64(recordAccount.ID)),
		DCLocation:             types.StringValue(recordAccount.DCLocation),
		CKey:                   types.StringValue(recordAccount.CKey),
		ACL:                    resourceFlattenACLInAccount(ctx, recordAccount.ACL),
		Company:                types.StringValue(recordAccount.Company),
		CompanyURL:             types.StringValue(recordAccount.CompanyURL),
		CPUAllocationParameter: types.StringValue(recordAccount.CPUAllocationParameter),
		CPUAllocationRatio:     types.Float64Value(recordAccount.CPUAllocationRatio),
		CreatedBy:              types.StringValue(recordAccount.CreatedBy),
		CreatedTime:            types.Int64Value(int64(recordAccount.CreatedTime)),
		DeactivationTime:       types.Float64Value(recordAccount.DeactivationTime),
		DeletedBy:              types.StringValue(recordAccount.DeletedBy),
		DeletedTime:            types.Int64Value(int64(recordAccount.DeletedTime)),
		DisplayName:            types.StringValue(recordAccount.DisplayName),
		GUID:                   types.Int64Value(int64(recordAccount.GUID)),
		Status:                 types.StringValue(recordAccount.Status),
		UpdatedTime:            types.Int64Value(int64(recordAccount.UpdatedTime)),
		Version:                types.Int64Value(int64(recordAccount.Version)),
	}

	state.VINS, diags = types.ListValueFrom(ctx, types.Int64Type, recordAccount.VINS)
	if diags.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.AccountResource: cannot flatten recordAccount.VINS to state.VINS", diags))
	}

	state.UniqPools, diags = types.ListValueFrom(ctx, types.StringType, recordAccount.UniqPools)
	if diags.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.AccountResource: cannot flatten recordAccount.UniqPools to state.UniqPools", diags))
	}

	state.ResourceTypes, diags = types.ListValueFrom(ctx, types.Int64Type, recordAccount.ResTypes)
	if diags.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.AccountResource: cannot flatten recordAccount.ResTypes to state.ResourceTypes", diags))
	}

	state.ComputeFeatures, diags = types.SetValueFrom(ctx, types.StringType, recordAccount.ComputeFeatures)
	if diags.HasError() {
		tflog.Error(ctx, fmt.Sprint("flattens.AccountResource: cannot flatten recordAccount.ComputeFeatures to state.ComputeFeatures", diags))
	}

	tflog.Info(ctx, "flattens.AccountResource: after flatten", map[string]any{"account_id": state.Id.ValueString()})

	tflog.Info(ctx, "End flattens.AccountResource", map[string]any{"account_id": state.Id.ValueString()})
	return nil
}

func flattenResourceLimitsInAccountResource(ctx context.Context, limits account.ResourceLimits, state *models.ResourceAccountModel) types.Object {
	tflog.Info(ctx, "Start flattenResourceLimitsInAccountResource")

	diags := diag.Diagnostics{}

	var resourceLimits models.ResourceLimitsInAccountResourceModel
	diags.Append(state.ResourceLimits.As(ctx, &resourceLimits, basetypes.ObjectAsOptions{})...)
	if diags.HasError() {
		tflog.Error(ctx, "flattenResourceLimitsInAccountResource: cannot populate resourceLimits with plan.ResourceLimits object element")
	}

	if resourceLimits.CUC.ValueFloat64() == 0 {
		resourceLimits.CUC = types.Float64Value(limits.CuC)
	}
	if resourceLimits.CUD.ValueFloat64() == 0 {
		resourceLimits.CUD = types.Float64Value(limits.CuD)
	}
	if resourceLimits.CUI.ValueFloat64() == 0 {
		resourceLimits.CUI = types.Float64Value(limits.CuI)
	}
	if resourceLimits.CUM.ValueFloat64() == 0 {
		resourceLimits.CUM = types.Float64Value(limits.CuM)
	}
	if resourceLimits.CUNP.ValueFloat64() == 0 {
		resourceLimits.CUNP = types.Float64Value(limits.CuNP)
	}
	if resourceLimits.GPUUnits.ValueFloat64() == 0 {
		resourceLimits.GPUUnits = types.Float64Value(limits.GPUUnits)
	}

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

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

func resourceFlattenACLInAccount(ctx context.Context, aclList []account.ACL) types.List {
	tflog.Info(ctx, "Start flattenACLInAccount")
	tempSlice := make([]types.Object, 0, len(aclList))
	for _, item := range aclList {
		temp := models.ACLInAccountModel{
			Explicit:    types.BoolValue(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),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemACLInAccount, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenACLInAccount struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

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

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