package teamcity

import (
	"context"
	"fmt"
	"github.com/hashicorp/terraform-plugin-framework/datasource"
	"github.com/hashicorp/terraform-plugin-framework/path"
	"github.com/hashicorp/terraform-plugin-framework/provider"
	"github.com/hashicorp/terraform-plugin-framework/provider/schema"
	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
	"math/big"
	"os"
	"terraform-provider-teamcity/client"
)

const MaxRetriesDefault int = 12

var (
	_ provider.Provider = &teamcityProvider{}
)

func New() provider.Provider {
	return &teamcityProvider{}
}

type teamcityProvider struct{}

func (p *teamcityProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
	resp.TypeName = "teamcity"
}

func (p *teamcityProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"host": schema.StringAttribute{
				Optional: true,
			},
			"token": schema.StringAttribute{
				Optional:  true,
				Sensitive: true,
			},
			"username": schema.StringAttribute{
				Optional: true,
			},
			"password": schema.StringAttribute{
				Optional:  true,
				Sensitive: true,
			},
			"max_retries": schema.NumberAttribute{
				Optional:    true,
				Description: "Maximum number of retries for requests to the server. Default is 12 (1 min). Each retry waits 5 seconds by default.",
			},
		},
	}
}

type teamcityProviderModel struct {
	Host       types.String          `tfsdk:"host"`
	Token      types.String          `tfsdk:"token"`
	Username   types.String          `tfsdk:"username"`
	Password   types.String          `tfsdk:"password"`
	MaxRetries basetypes.NumberValue `tfsdk:"max_retries"`
}

func (p *teamcityProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
	var config teamcityProviderModel
	diags := req.Config.Get(ctx, &config)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	if config.Host.IsUnknown() {
		resp.Diagnostics.AddAttributeError(
			path.Root("host"),
			"Unknown TeamCity Host",
			"",
		)
	}
	if config.Token.IsUnknown() {
		resp.Diagnostics.AddAttributeError(
			path.Root("token"),
			"Unknown TeamCity API token",
			"",
		)
	}
	if config.Token.IsUnknown() {
		resp.Diagnostics.AddAttributeError(
			path.Root("username"),
			"Unknown TeamCity username",
			"",
		)
	}
	if config.Token.IsUnknown() {
		resp.Diagnostics.AddAttributeError(
			path.Root("password"),
			"Unknown TeamCity password",
			"",
		)
	}
	if resp.Diagnostics.HasError() {
		return
	}

	host := os.Getenv("TEAMCITY_HOST")
	token := os.Getenv("TEAMCITY_TOKEN")
	username := os.Getenv("TEAMCITY_USERNAME")
	password := os.Getenv("TEAMCITY_PASSWORD")
	maxRetries := MaxRetriesDefault

	if !config.Host.IsNull() {
		host = config.Host.ValueString()
	}
	if !config.Token.IsNull() {
		token = config.Token.ValueString()
	}
	if !config.Username.IsNull() {
		username = config.Username.ValueString()
	}
	if !config.Password.IsNull() {
		password = config.Password.ValueString()
	}

	if host == "" {
		resp.Diagnostics.AddAttributeError(
			path.Root("host"),
			"Missing TeamCity Host",
			"",
		)
	}
	if token == "" && username == "" && password == "" {
		resp.Diagnostics.AddAttributeError(
			path.Root("token"),
			"Missing TeamCity API Token",
			"",
		)
	}

	if resp.Diagnostics.HasError() {
		return
	}

	if !config.MaxRetries.IsNull() {
		var bigInt big.Int
		config.MaxRetries.ValueBigFloat().Int(&bigInt)
		maxRetries = int(bigInt.Int64())
	}

	cl := client.NewClient(host, token, username, password, maxRetries)
	_, err := cl.VerifyConnection(ctx)

	if err != nil {
		resp.Diagnostics.AddError(
			"Could not verify connection to server",
			fmt.Sprint(err),
		)
	}

	if resp.Diagnostics.HasError() {
		return
	}

	resp.DataSourceData = &cl
	resp.ResourceData = &cl
}

func (p *teamcityProvider) DataSources(_ context.Context) []func() datasource.DataSource {
	return []func() datasource.DataSource{
		NewServerDataSource,
		NewBuildConfDataSource,
		NewPoolDataSource,
		NewSshKeyDataSource,
		NewGroupDataSource,
		NewUserDataSource,
	}
}

func (p *teamcityProvider) Resources(_ context.Context) []func() resource.Resource {
	return []func() resource.Resource{
		NewCleanupResource,
		NewPoolResource,
		NewProjectResource,
		NewSshKeyResource,
		NewVcsRootResource,
		NewVersionedSettingsResource,
		NewRoleResource,
		NewAuthResource,
		NewGlobalResource,
		NewEmailResource,
		NewUserResource,
		NewContextParamsResource,
		NewTokenResource,
		NewGroupResource,
		NewMemberResource,
		NewLicenseResource,
		NewParamResource,
		NewConnectionResource,
		NewGroupRoleAssignmentResource,
		NewUserRoleAssignmentResource,
	}
}
