package bigquery

import (
	"context"
	"net/http"
	"time"

	bg "cloud.google.com/go/bigquery"
	"github.com/google/wire"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/ecosystem-service/config"
	"github.com/tidbcloud/ecosystem-service/internal/service/bigquery/client"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"google.golang.org/api/googleapi"
)

const (
	datasetID          = "tidbcloud_ecosystem"
	clusterConnTableID = "cluster_connections"
)

var BigqueryProvider = wire.NewSet(NewConnectionUploader)

type TiDBVercelConnection struct {
	ID        uint64
	CreatedAt time.Time
	UpdatedAt time.Time

	VercelProjectID string
	TiDBOrgID       uint64
	TiDBProjectID   uint64
	TiDBClusterID   uint64
	FrameworkType   string

	VercelIntegrationConfigurationID string
}

func (i *TiDBVercelConnection) Save() (map[string]bg.Value, string, error) {
	return map[string]bg.Value{
		"id":                                  i.ID,
		"vercel_project_id":                   i.VercelProjectID,
		"tidb_org_id":                         i.TiDBOrgID,
		"tidb_project_id":                     i.TiDBProjectID,
		"tidb_cluster_id":                     i.TiDBClusterID,
		"vercel_integration_configuration_id": i.VercelIntegrationConfigurationID,
		"framework_type":                      i.FrameworkType,
		"created_at":                          i.CreatedAt.Format("2006-01-02 15:04:05"),
		"updated_at":                          i.UpdatedAt.Format("2006-01-02 15:04:05"),
	}, bg.NoDedupeID, nil
}

type ConnectionUploader struct {
	*client.Client
}

func NewConnectionUploader(conf *config.ConfigWrapper) (*ConnectionUploader, error) {
	if !conf.BigQueryConfig.Enable {
		return &ConnectionUploader{}, nil
	}

	bigqueryClient, err := client.NewBigqueryClient(conf)
	if err != nil {
		return nil, err
	}
	uploader := &ConnectionUploader{
		bigqueryClient,
	}
	return uploader, nil
}

// UploadConnections uploads the connections to bigquery. It will create table with date suffix if not exists and then upload
// connections to the table.
func (u ConnectionUploader) UploadConnections(ctx context.Context, items []*TiDBVercelConnection) error {
	if len(items) == 0 {
		tilog.Warn(ctx, "no connections to upload")
	}

	// Create template table
	tableTemplate := u.Dataset(datasetID).Table(clusterConnTableID)
	schema := bg.Schema{
		{Name: "id", Type: bg.NumericFieldType},
		{Name: "vercel_project_id", Type: bg.StringFieldType},
		{Name: "tidb_org_id", Type: bg.NumericFieldType},
		{Name: "tidb_project_id", Type: bg.NumericFieldType},
		{Name: "tidb_cluster_id", Type: bg.NumericFieldType},
		{Name: "vercel_integration_configuration_id", Type: bg.StringFieldType},
		{Name: "framework_type", Type: bg.StringFieldType},
		{Name: "created_at", Type: bg.DateTimeFieldType},
		{Name: "updated_at", Type: bg.DateTimeFieldType},
	}
	meta := &bg.TableMetadata{
		Name:   clusterConnTableID,
		Schema: schema,
	}
	if _, err := tableTemplate.Metadata(u.Ctx); err != nil {
		if e, ok := err.(*googleapi.Error); ok {
			if e.Code == http.StatusNotFound {
				if err := tableTemplate.Create(u.Ctx, meta); err != nil {
					return errors.Trace(err)
				}
			}
		}
	}

	// Creating a table is an eventually consistent operation. So we use TableTemplateSuffix as a way to create the table automatically
	// see https://github.com/googleapis/google-cloud-go/issues/975
	inserter := tableTemplate.Inserter()
	inserter.TableTemplateSuffix = time.Now().Format("_2006_01_02")
	if err := inserter.Put(u.Ctx, items); err != nil {
		return errors.Trace(err)
	}

	// Update table expiration time to 30 days
	table := u.Dataset(datasetID).Table(clusterConnTableID + inserter.TableTemplateSuffix)
	metadata, err := table.Metadata(u.Ctx)
	if err != nil {
		return errors.Trace(err)
	}
	update := bg.TableMetadataToUpdate{
		ExpirationTime: time.Now().Add(time.Duration(30*24) * time.Hour), // table expiration in 30 days.
	}
	_, err = table.Update(u.Ctx, update, metadata.ETag)
	if err != nil {
		return errors.Trace(err)
	}

	return nil
}
