package v1

import (
	"context"
	"net/http"
	"net/url"

	"github.com/superfly/fly-go"
	"github.com/superfly/flyctl/internal/uiex"
	"github.com/superfly/flyctl/internal/uiex/mpg"
)

type contextKey struct{}

var clientContextKey = &contextKey{}

type ClientV1 interface {
	// List clusters. NOTE: Odd but the v1 client endpoint returns both v1 and v2 clusters,
	// they are just identified with the `Version` field being 1 or 2.
	ListManagedClusters(ctx context.Context, orgSlug string, deleted bool) (ListManagedClustersResponse, error)
	GetManagedCluster(ctx context.Context, orgSlug string, id string) (GetManagedClusterResponse, error)
	GetManagedClusterById(ctx context.Context, id string) (GetManagedClusterResponse, error)
	CreateUser(ctx context.Context, id string, input CreateUserInput) (CreateUserResponse, error)
	CreateUserWithRole(ctx context.Context, id string, input CreateUserWithRoleInput) (CreateUserWithRoleResponse, error)
	UpdateUserRole(ctx context.Context, id string, username string, input UpdateUserRoleInput) (UpdateUserRoleResponse, error)
	DeleteUser(ctx context.Context, id string, username string) error
	GetUserCredentials(ctx context.Context, id string, username string) (GetUserCredentialsResponse, error)
	ListUsers(ctx context.Context, id string) (ListUsersResponse, error)
	ListDatabases(ctx context.Context, id string) (ListDatabasesResponse, error)
	CreateDatabase(ctx context.Context, id string, input CreateDatabaseInput) (CreateDatabaseResponse, error)
	ListExtensions(ctx context.Context, id, database string) (ListExtensionsResponse, error)
	EnableExtension(ctx context.Context, id, database string, input EnableExtensionInput) error
	DisableExtension(ctx context.Context, id, database, name string, force bool) error
	DestroyCluster(ctx context.Context, orgSlug string, id string) error
	ListManagedClusterBackups(ctx context.Context, clusterID string) (ListManagedClusterBackupsResponse, error)
	CreateManagedClusterBackup(ctx context.Context, clusterID string, input CreateManagedClusterBackupInput) (CreateManagedClusterBackupResponse, error)
	RestoreManagedClusterBackup(ctx context.Context, clusterID string, input RestoreManagedClusterBackupInput) (RestoreManagedClusterBackupResponse, error)
	CreateAttachment(ctx context.Context, clusterId string, input CreateAttachmentInput) (CreateAttachmentResponse, error)
	DeleteAttachment(ctx context.Context, clusterId string, appName string) (DeleteAttachmentResponse, error)
}

type Client struct {
	*uiex.Client
}

func NewClientWithOptions(ctx context.Context, opts uiex.NewClientOpts) (*Client, error) {
	uiex, err := uiex.NewWithOptions(ctx, opts)
	if err != nil {
		return nil, err
	}

	return &Client{
		Client: uiex,
	}, nil
}

func (c *Client) BaseURL() *url.URL {
	return c.Client.BaseURL()
}

func (c *Client) HTTPClient() *http.Client {
	return c.Client.HTTPClient()
}

// NewContext derives a Context that carries c from ctx.
func NewContextWithClient(ctx context.Context, c ClientV1) context.Context {
	return context.WithValue(ctx, clientContextKey, c)
}

// ClientFromContext returns the ClientV1 ctx carries.
func ClientFromContext(ctx context.Context) ClientV1 {
	c, _ := ctx.Value(clientContextKey).(ClientV1)

	return c
}

type ManagedCluster struct {
	Id            string                          `json:"id"`
	ClusterId     string                          `json:"mpgd_cluster_id"`
	Version       int                             `json:"version"`
	Name          string                          `json:"name"`
	Region        string                          `json:"region"`
	Status        string                          `json:"status"`
	Plan          string                          `json:"plan"`
	Disk          int                             `json:"disk"`
	Replicas      int                             `json:"replicas"`
	Organization  fly.Organization                `json:"organization"`
	IpAssignments mpg.ManagedClusterIpAssignments `json:"ip_assignments"`
	AttachedApps  []mpg.AttachedApp               `json:"attached_apps"`
}

type ListManagedClustersResponse struct {
	Data []ManagedCluster `json:"data"`
}

type GetManagedClusterCredentialsResponse struct {
	Status        string `json:"status"`
	User          string `json:"user"`
	Password      string `json:"password"`
	DBName        string `json:"dbname"`
	ConnectionUri string `json:"pgbouncer_uri"`
}

type GetManagedClusterResponse struct {
	Data        ManagedCluster                       `json:"data"`
	Credentials GetManagedClusterCredentialsResponse `json:"credentials"`
}

type ManagedClusterBackup struct {
	Id     string `json:"id"`
	Status string `json:"status"`
	Type   string `json:"type"`
	Start  string `json:"start"`
	Stop   string `json:"stop"`
}

type ListManagedClusterBackupsResponse struct {
	Data []ManagedClusterBackup `json:"data"`
}

type CreateManagedClusterBackupInput struct {
	Type string `json:"type"`
}

type CreateManagedClusterBackupResponse struct {
	Data ManagedClusterBackup `json:"data"`
}

type RestoreManagedClusterBackupInput struct {
	BackupId string `json:"backup_id"`
	Name     string `json:"name,omitempty"`
}

type RestoreManagedClusterBackupResponse struct {
	Data ManagedCluster `json:"data"`
}

type GetUserCredentialsResponse struct {
	Data struct {
		User     string `json:"user"`
		Password string `json:"password"`
	} `json:"data"`
}

type CreateUserInput struct {
	DbName   string `json:"db_name"`
	UserName string `json:"user_name"`
}

type CreateUserResponse struct {
	ConnectionUri string              `json:"connection_uri"`
	Ok            bool                `json:"ok"`
	Errors        uiex.DetailedErrors `json:"errors"`
}

type User struct {
	Name string `json:"name"`
	Role string `json:"role"`
}

type ListUsersResponse struct {
	Data []User `json:"data"`
}

type CreateUserWithRoleInput struct {
	UserName string `json:"user_name"`
	Role     string `json:"role"` // 'schema_admin' | 'writer' | 'reader'
}

type CreateUserWithRoleResponse struct {
	Data User `json:"data"`
}

type UpdateUserRoleInput struct {
	Role string `json:"role"` // 'schema_admin' | 'writer' | 'reader'
}

type UpdateUserRoleResponse struct {
	Data User `json:"data"`
}

type Database struct {
	Name string `json:"name"`
}

type ListDatabasesResponse struct {
	Data []Database `json:"data"`
}

type CreateDatabaseInput struct {
	Name string `json:"name"`
}

type CreateDatabaseResponse struct {
	Data Database `json:"data"`
}

type InstalledExtension struct {
	Version string `json:"version"`
	Schema  string `json:"schema"`
}

type Extension struct {
	Name           string              `json:"name"`
	Description    string              `json:"description"`
	DocsURL        string              `json:"docs_url"`
	DefaultVersion string              `json:"default_version"`
	IsSystem       bool                `json:"is_system"`
	Installed      *InstalledExtension `json:"installed"`
}

type ListExtensionsResponse struct {
	Data []Extension `json:"data"`
}

type EnableExtensionInput struct {
	Name                 string `json:"name"`
	Schema               string `json:"schema,omitempty"`
	CreateSchemaIfNeeded bool   `json:"create_schema_if_needed"`
}

type CreateAttachmentInput struct {
	AppName string `json:"app_name"`
}

type CreateAttachmentResponse struct {
	Data struct {
		Id               int64  `json:"id"`
		AppId            int64  `json:"app_id"`
		ManagedServiceId int64  `json:"managed_service_id"`
		AttachedAt       string `json:"attached_at"`
	} `json:"data"`
}

type DeleteAttachmentResponse struct {
	Data struct {
		Message string `json:"message"`
	} `json:"data"`
}
