package flattens

import (
	"context"
	"fmt"
	"strconv"

	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/flattens"

	"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/account"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/account/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/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{
		// request fields
		AccountName:      types.StringValue(recordAccount.Name),
		SendAccessEmails: types.BoolValue(recordAccount.SendAccessEmails),
		Users:            flattenUsers(ctx, recordAccount.ACL),
		Restore:          state.Restore,
		Permanently:      state.Permanently,
		Desc:             types.StringValue(recordAccount.Description),
		Enable:           state.Enable,
		Reason:           state.Reason,
		ResourceLimits:   flattenResourceLimitsInAccountResource(ctx, recordAccount.ResourceLimits, state),
		Timeouts:         state.Timeouts,

		// response fields
		Id:                     types.StringValue(strconv.Itoa(int(accountId))),
		LastUpdated:            state.LastUpdated,
		AccountID:              types.Int64Value(int64(recordAccount.ID)),
		DCLocation:             types.StringValue(recordAccount.DCLocation),
		CKey:                   types.StringValue(recordAccount.CKey),
		ACL:                    flattenACLInAccount(ctx, recordAccount.ACL),
		Company:                types.StringValue(recordAccount.Company),
		CompanyURL:             types.StringValue(recordAccount.CompanyURL),
		Computes:               flattenComputes(ctx, recordAccount.Computes),
		ComputeFeatures:        flattens.FlattenSimpleTypeToList(ctx, types.StringType, recordAccount.ComputeFeatures),
		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)),
		//Description:            types.StringValue(recordAccount.Description),
		DisplayName: types.StringValue(recordAccount.DisplayName),
		GUID:        types.Int64Value(int64(recordAccount.GUID)),
		Machines:    flattenMachines(ctx, recordAccount.Machines),
		Status:      types.StringValue(recordAccount.Status),
		UpdatedTime: types.Int64Value(int64(recordAccount.UpdatedTime)),
		Version:     types.Int64Value(int64(recordAccount.Version)),
		VINS:        flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordAccount.VINS),
		VINSes:      types.Int64Value(int64(recordAccount.VINSes)),
	}

	if state.Enable == types.BoolNull() {
		state.Enable = types.BoolValue(false)
		if recordAccount.Status == "CONFIRMED" {
			state.Enable = types.BoolValue(true)
		}
	}

	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 flattenUsers(ctx context.Context, aclList []account.RecordACL) types.Set {
	tflog.Info(ctx, "Start flattenUsers")
	tempSlice := make([]types.Object, 0, len(aclList))
	for i, item := range aclList {
		if i == 0 {
			continue
		}
		temp := models.UsersModel{
			UserID:     types.StringValue(item.UgroupID),
			AccessType: types.StringValue(item.Rights),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemUsersResource, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenUsers struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

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

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