// Package client is an HTTP client for the Queryer.
package client

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	featurebase "github.com/featurebasedb/featurebase/v3"
	"github.com/featurebasedb/featurebase/v3/dax"
	"github.com/featurebasedb/featurebase/v3/errors"
	"github.com/featurebasedb/featurebase/v3/logger"
)

const (
	defaultScheme = "http"
)

// Client is an HTTP client that operates on the Controller endpoints exposed by
// the main Controller service.
type Client struct {
	client  *http.Client
	address dax.Address
	logger  logger.Logger
}

// New returns a new instance of Client.
func New(address dax.Address, logger logger.Logger) *Client {
	return &Client{
		address: address,
		logger:  logger,
		client: &http.Client{
			Timeout: time.Second * 30,
		},
	}
}

// Health returns true if the client address returns status OK at its /health
// endpoint.
func (c *Client) Health() bool {
	url := fmt.Sprintf("%s/health", c.address.WithScheme(defaultScheme))

	if resp, err := http.Get(url); err != nil {
		return false
	} else if resp.StatusCode != http.StatusOK {
		return false
	}

	return true
}

func (c *Client) QuerySQL(ctx context.Context, qdbid dax.QualifiedDatabaseID, sql io.Reader) (*featurebase.WireQueryResponse, error) {
	url := fmt.Sprintf("%s/databases/%s/sql", c.address.WithScheme(defaultScheme), qdbid.DatabaseID)
	if qdbid.DatabaseID == "" {
		url = fmt.Sprintf("%s/sql", c.address.WithScheme(defaultScheme))
	}

	// Post the request.
	c.logger.Debugf("POST query sql request: url: %s", url)
	req, err := http.NewRequest(http.MethodPost, url, sql)
	if err != nil {
		return nil, errors.Wrap(err, "creating new post request")
	}
	req.Header.Add("Content-Type", "text/plain")
	req.Header.Add("OrganizationID", string(qdbid.OrganizationID))

	var resp *http.Response
	if resp, err = c.client.Do(req); err != nil {
		return nil, errors.Wrap(err, "executing post request")
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		b, _ := io.ReadAll(resp.Body)
		return nil, errors.Errorf("status code: %d: %s", resp.StatusCode, b)
	}

	var wireResp *featurebase.WireQueryResponse
	if err := json.NewDecoder(resp.Body).Decode(&wireResp); err != nil {
		return nil, errors.Wrap(err, "reading response body")
	}

	return wireResp, nil
}
