package teamcity

import (
	"context"
	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"terraform-provider-teamcity/client"
)

var (
	_ resource.Resource                = &emailResource{}
	_ resource.ResourceWithConfigure   = &emailResource{}
	_ resource.ResourceWithImportState = &emailResource{}
)

func NewEmailResource() resource.Resource {
	return &emailResource{}
}

type emailResource struct {
	client *client.Client
}

type emailResourceModel struct {
	Enabled          types.Bool   `tfsdk:"enabled"`
	Host             types.String `tfsdk:"host"`
	Port             types.Int64  `tfsdk:"port"`
	From             types.String `tfsdk:"from"`
	Login            types.String `tfsdk:"login"`
	Password         types.String `tfsdk:"password"`
	SecureConnection types.String `tfsdk:"secure_connection"`
}

func (r *emailResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
	if req.ProviderData == nil {
		return
	}
	r.client = req.ProviderData.(*client.Client)
}

func (r *emailResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
	resp.TypeName = req.ProviderTypeName + "_email_settings"
}

func (r *emailResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Description: "TeamCity can notify users about various events using email. More info [here](https://www.jetbrains.com/help/teamcity/configuring-notifications.html)",
		Attributes: map[string]schema.Attribute{
			"enabled": schema.BoolAttribute{Required: true},
			"host":    schema.StringAttribute{Required: true},
			"port":    schema.Int64Attribute{Required: true},
			"from":    schema.StringAttribute{Required: true},
			"login": schema.StringAttribute{
				Optional: true,
				Computed: true,
				Default:  stringdefault.StaticString(""),
			},
			"password": schema.StringAttribute{Optional: true, Sensitive: true},
			"secure_connection": schema.StringAttribute{
				Optional: true,
				Computed: true,
				Default:  stringdefault.StaticString("NONE"),
				Validators: []validator.String{
					stringvalidator.OneOf([]string{"NONE", "STARTTLS", "SSL"}...),
				},
			},
		},
	}
}

func (r *emailResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	var plan emailResourceModel
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	newState, err := r.update(plan)
	if err != nil {
		resp.Diagnostics.AddError(
			"Error setting email settings",
			err.Error(),
		)
		return
	}
	newState.Password = plan.Password

	diags = resp.State.Set(ctx, newState)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

func (r *emailResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
	var oldState emailResourceModel
	diags := req.State.Get(ctx, &oldState)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	result, err := r.client.GetEmailSettings()
	if err != nil {
		resp.Diagnostics.AddError(
			"Unable to read email settings",
			err.Error(),
		)
		return
	}

	newState, err := r.readState(*result)
	if err != nil {
		resp.Diagnostics.AddError(
			"Unable to read email settings",
			err.Error(),
		)
		return
	}
	newState.Password = oldState.Password

	diags = resp.State.Set(ctx, newState)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

func (r *emailResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var plan emailResourceModel
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	newState, err := r.update(plan)
	if err != nil {
		resp.Diagnostics.AddError(
			"Error setting email settings",
			err.Error(),
		)
		return
	}
	newState.Password = plan.Password

	diags = resp.State.Set(ctx, newState)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

func (r *emailResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
}

func (r *emailResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
	resp.State.Set(ctx, emailResourceModel{})
}

func (r *emailResource) update(plan emailResourceModel) (*emailResourceModel, error) {
	password := plan.Password.ValueString()
	settings := client.EmailSettings{
		Enabled:          plan.Enabled.ValueBool(),
		Host:             plan.Host.ValueString(),
		Port:             int(plan.Port.ValueInt64()),
		From:             plan.From.ValueString(),
		Login:            plan.Login.ValueString(),
		Password:         &password,
		SecureConnection: plan.SecureConnection.ValueString(),
	}

	result, err := r.client.SetEmailSettings(settings)
	if err != nil {
		return nil, err
	}

	return r.readState(*result)
}

func (r *emailResource) readState(result client.EmailSettings) (*emailResourceModel, error) {
	var state emailResourceModel

	state.Enabled = types.BoolValue(result.Enabled)
	state.Host = types.StringValue(result.Host)
	state.Port = types.Int64Value(int64(result.Port))
	state.From = types.StringValue(result.From)
	state.Login = types.StringValue(result.Login)
	state.SecureConnection = types.StringValue(result.SecureConnection)

	return &state, nil
}
