// Copyright (c) 2025 Aiven, Helsinki, Finland. https://aiven.io/
// Code generated by user config generator. DO NOT EDIT.

package project

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/hashicorp/terraform-plugin-framework/attr"
	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/types"

	"github.com/aiven/terraform-provider-aiven/internal/plugin/util"
)

const aivenName = "aiven_organization_project"

type tfModel struct {
	ID              types.String `tfsdk:"id"`
	BasePort        types.Int64  `tfsdk:"base_port"`
	BillingGroupID  types.String `tfsdk:"billing_group_id"`
	CaCert          types.String `tfsdk:"ca_cert"`
	OrganizationID  types.String `tfsdk:"organization_id"`
	ParentID        types.String `tfsdk:"parent_id"`
	ProjectID       types.String `tfsdk:"project_id"`
	Tag             types.Set    `tfsdk:"tag"`
	TechnicalEmails types.Set    `tfsdk:"technical_emails"`
}

func (tf *tfModel) SetID(vOrganizationID string, vProjectID string) {
	tf.OrganizationID = types.StringValue(vOrganizationID)
	tf.ProjectID = types.StringValue(vProjectID)
	tf.ID = types.StringValue(filepath.Join(vOrganizationID, vProjectID))
}

type tfModelTag struct {
	Key   types.String `tfsdk:"key"`
	Value types.String `tfsdk:"value"`
}

type apiModel struct {
	BasePort        *int64          `json:"base_port,omitempty"`
	BillingGroupID  *string         `json:"billing_group_id,omitempty"`
	CaCert          *string         `json:"certificate,omitempty"`
	OrganizationID  *string         `json:"organization_id,omitempty"`
	ParentID        *string         `json:"parent_id,omitempty"`
	ProjectID       *string         `json:"project_id,omitempty"`
	Tag             *[]*apiModelTag `json:"tags,omitempty"`
	TechnicalEmails *[]string       `json:"tech_emails,omitempty"`
}

type apiModelTag struct {
	Key   *string `json:"key,omitempty"`
	Value *string `json:"value,omitempty"`
}

// composeID the ID attribute fields, i.e.:
// terraform import aiven_organization_project.foo ORGANIZATION_ID/PROJECT_ID
func composeID() []string {
	return []string{"organization_id", "project_id"}
}

// expandData turns TF object into Request
func expandData[R any](ctx context.Context, plan, state *tfModel, req *R, modifiers ...util.MapModifier[apiModel]) diag.Diagnostics {
	api := new(apiModel)
	if !plan.Tag.IsNull() || state != nil && !state.Tag.IsNull() {
		vTag, diags := util.ExpandSetNested(ctx, expandTag, plan.Tag)
		if diags.HasError() {
			return diags
		}
		api.Tag = &vTag
	}
	if !plan.TechnicalEmails.IsNull() || state != nil && !state.TechnicalEmails.IsNull() {
		vTechnicalEmails := make([]string, 0)
		diags := plan.TechnicalEmails.ElementsAs(ctx, &vTechnicalEmails, false)
		if diags.HasError() {
			return diags
		}
		api.TechnicalEmails = &vTechnicalEmails
	}
	if !plan.BasePort.IsNull() && !plan.BasePort.IsUnknown() {
		vBasePort := plan.BasePort.ValueInt64()
		api.BasePort = &vBasePort
	}
	if !plan.BillingGroupID.IsNull() || state != nil && !state.BillingGroupID.IsNull() {
		vBillingGroupID := plan.BillingGroupID.ValueString()
		api.BillingGroupID = &vBillingGroupID
	}
	if !plan.OrganizationID.IsNull() || state != nil && !state.OrganizationID.IsNull() {
		vOrganizationID := plan.OrganizationID.ValueString()
		api.OrganizationID = &vOrganizationID
	}
	if !plan.ParentID.IsNull() || state != nil && !state.ParentID.IsNull() {
		vParentID := plan.ParentID.ValueString()
		api.ParentID = &vParentID
	}
	if !plan.ProjectID.IsNull() || state != nil && !state.ProjectID.IsNull() {
		vProjectID := plan.ProjectID.ValueString()
		api.ProjectID = &vProjectID
	}
	err := util.Unmarshal(api, req, modifiers...)
	if err != nil {
		var diags diag.Diagnostics
		diags.AddError("Unmarshal error", fmt.Sprintf("Failed to unmarshal dtoModel to Request: %s", err.Error()))
		return diags
	}
	return nil
}

func expandTag(ctx context.Context, plan *tfModelTag) (*apiModelTag, diag.Diagnostics) {
	api := new(apiModelTag)
	if !plan.Key.IsNull() {
		vKey := plan.Key.ValueString()
		api.Key = &vKey
	}
	if !plan.Value.IsNull() {
		vValue := plan.Value.ValueString()
		api.Value = &vValue
	}
	return api, nil
}

// flattenData turns Response into TF object
func flattenData[R any](ctx context.Context, state *tfModel, rsp *R, modifiers ...util.MapModifier[R]) diag.Diagnostics {
	api := new(apiModel)
	err := util.Unmarshal(rsp, api, modifiers...)
	if err != nil {
		var diags diag.Diagnostics
		diags.AddError("Unmarshal error", fmt.Sprintf("Failed to unmarshal Response to dtoModel: %s", err.Error()))
		return diags
	}
	if api.Tag != nil {
		vTag, diags := util.FlattenSetNested(ctx, flattenTag, *api.Tag, attrsTag())
		if diags.HasError() {
			return diags
		}
		state.Tag = vTag
	}
	if api.TechnicalEmails != nil && (len(*api.TechnicalEmails) > 0 || !state.TechnicalEmails.IsNull()) {
		vTechnicalEmails, diags := types.SetValueFrom(ctx, types.StringType, api.TechnicalEmails)
		if diags.HasError() {
			return diags
		}
		state.TechnicalEmails = vTechnicalEmails
	}
	if api.BasePort != nil {
		state.BasePort = types.Int64PointerValue(api.BasePort)
	}
	if api.BillingGroupID != nil && (*api.BillingGroupID != "" || !state.BillingGroupID.IsNull()) {
		state.BillingGroupID = types.StringPointerValue(api.BillingGroupID)
	}
	if api.CaCert != nil && (*api.CaCert != "" || !state.CaCert.IsNull()) {
		state.CaCert = types.StringPointerValue(api.CaCert)
	}
	if api.OrganizationID != nil && (*api.OrganizationID != "" || !state.OrganizationID.IsNull()) {
		state.OrganizationID = types.StringPointerValue(api.OrganizationID)
	}
	if api.ParentID != nil && (*api.ParentID != "" || !state.ParentID.IsNull()) {
		state.ParentID = types.StringPointerValue(api.ParentID)
	}
	if api.ProjectID != nil && (*api.ProjectID != "" || !state.ProjectID.IsNull()) {
		state.ProjectID = types.StringPointerValue(api.ProjectID)
	}
	// Response may not contain ID fields.
	// In that case, `terraform import` won't be able to set them. Gets values from the ID.
	if state.ID.ValueString() != "" {
		var parts [2]string
		for i, v := range strings.SplitN(state.ID.ValueString(), "/", 2) {
			parts[i] = v
		}
		if state.OrganizationID.ValueString() == "" {
			state.OrganizationID = types.StringValue(parts[0])
		}
		if state.ProjectID.ValueString() == "" {
			state.ProjectID = types.StringValue(parts[1])
		}
	}
	state.SetID(state.OrganizationID.ValueString(), state.ProjectID.ValueString())
	return nil
}

func flattenTag(ctx context.Context, api *apiModelTag) (*tfModelTag, diag.Diagnostics) {
	state := new(tfModelTag)
	if api.Key != nil {
		state.Key = types.StringPointerValue(api.Key)
	}
	if api.Value != nil {
		state.Value = types.StringPointerValue(api.Value)
	}
	return state, nil
}

func attrsTag() types.ObjectType {
	return types.ObjectType{AttrTypes: map[string]attr.Type{
		"key":   types.StringType,
		"value": types.StringType,
	}}
}
