package flattens

import (
	"context"
	"fmt"

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

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

	diags := diag.Diagnostics{}

	accountId := uint64(state.AccountID.ValueInt64())

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

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

	id := uuid.New()
	*state = models.DataSourceAccountModel{
		AccountID: state.AccountID,
		Timeouts:  state.Timeouts,

		Id:                     types.StringValue(id.String()),
		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),
		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)),
		Desc:                   types.StringValue(recordAccount.Description),
		DisplayName:            types.StringValue(recordAccount.DisplayName),
		GUID:                   types.Int64Value(int64(recordAccount.GUID)),
		Machines:               flattenMachines(ctx, recordAccount.Machines),
		AccountName:            types.StringValue(recordAccount.Name),
		ResourceLimits:         flattenResourceLimits(ctx, recordAccount.ResourceLimits),
		SendAccessEmails:       types.BoolValue(recordAccount.SendAccessEmails),
		Status:                 types.StringValue(recordAccount.Status),
		UpdatedTime:            types.Int64Value(int64(recordAccount.UpdatedTime)),
		Version:                types.Int64Value(int64(recordAccount.Version)),
		VINSes:                 types.Int64Value(int64(recordAccount.VINSes)),
	}

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

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

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

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

func flattenComputes(ctx context.Context, computes account.Computes) types.Object {
	tflog.Info(ctx, "Start flattenComputes")
	temp := models.ComputesInAccountModel{
		Started: types.Int64Value(int64(computes.Started)),
		Stopped: types.Int64Value(int64(computes.Stopped)),
	}

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

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

func flattenMachines(ctx context.Context, machines account.Machines) types.Object {
	tflog.Info(ctx, "Start flattenMachines")
	temp := models.MachinesInAccountModel{
		Running: types.Int64Value(int64(machines.Running)),
		Halted:  types.Int64Value(int64(machines.Halted)),
	}

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

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

func flattenResourceLimits(ctx context.Context, limits account.ResourceLimits) types.Object {
	tflog.Info(ctx, "Start flattenResourceLimits")
	temp := models.ResourceLimitsInAccountModel{
		CUC:      types.Float64Value(limits.CUC),
		CUD:      types.Float64Value(limits.CUD),
		CUI:      types.Float64Value(limits.CUI),
		CUM:      types.Float64Value(limits.CUM),
		CUDM:     types.Float64Value(limits.CUDM),
		CUNP:     types.Float64Value(limits.CUNP),
		GPUUnits: types.Float64Value(limits.GPUUnits),
	}

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

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

func flattenACLInAccount(ctx context.Context, aclList []account.RecordACL) 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.IsExplicit),
			GUID:         types.StringValue(item.GUID),
			Right:        types.StringValue(item.Rights),
			Status:       types.StringValue(item.Status),
			Type:         types.StringValue(item.Type),
			UserGroupID:  types.StringValue(item.UgroupID),
			CanBeDeleted: types.BoolValue(item.CanBeDeleted),
		}
		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
}
