package gcp

import (
	"bytes"
	"context"
	"fmt"
	"html/template"
	"os"

	"cloud.google.com/go/bigquery"
	"github.com/pingcap/log"
	"github.com/tidbcloud/billing-verify-bills/pkg/global"
	gcpauth "github.com/tidbcloud/billing-verify-bills/pkg/provider/gcp/auth"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
)

const (
	TIME_TEMPLATE       = "2006-01-02 15:04:05"
	BIGQUERY_TABLE_NAME = `gcp-digital-bi.gcp_billing.gcp_billing_export_v1_010870_592F9D_20D797*`
)

type BigQueryParam struct {
	global.Request

	BigQuerySharedParam
}

type BigQuerySharedParam struct {
	Table       string
	BillProject string // A project that generates bills
}

type BigQueryRows []map[string]bigquery.Value

type BigQueryClient struct {
	client      *bigquery.Client
	location    string
	SharedParam BigQuerySharedParam
}

// NewBigQueryClient new a bigquery client
func NewBigQueryClient(providerProjectID string, location string, sharedParam BigQuerySharedParam) (*BigQueryClient, error) {
	if providerProjectID == "" || location == "" ||
		sharedParam.Table == "" || sharedParam.BillProject == "" {
		err := fmt.Errorf("gcp config error")
		return nil, err
	}
	ctx := context.Background()
	var opts []option.ClientOption
	if os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") == "" && os.Getenv("USE_DEFAULT_GCP_CREDENTIAL") == "false" {
		// init opts for kubernetes iam
		var err error
		err = gcpauth.InitWebIdentityEnv()
		if err != nil {
			return nil, err
		}
		opts, err = gcpauth.DefaultClientOptions()
		if err != nil {
			return nil, err
		}
	}
	client, err := bigquery.NewClient(ctx, providerProjectID, opts...)
	if err != nil {
		return nil, fmt.Errorf("bigquery.NewClient: %v", err)
	}
	c := BigQueryClient{
		client:      client,
		location:    location,
		SharedParam: sharedParam,
	}
	return &c, nil
}

func (t BigQueryClient) Query(ctx context.Context, sql string) (BigQueryRows, error) {
	q := t.client.Query(sql)
	q.Location = t.location

	job, err := q.Run(ctx)
	if err != nil {
		return nil, err
	}
	// wait job
	status, err := job.Wait(ctx)
	if err != nil {
		return nil, err
	}
	// check status
	if err := status.Err(); err != nil {
		return nil, err
	}

	var rows BigQueryRows

	it, err := job.Read(ctx)
	if err != nil {
		return nil, err
	}
	for {
		var row map[string]bigquery.Value
		err2 := it.Next(&row)
		if err2 == iterator.Done {
			break
		}
		if err2 != nil {
			return nil, err2
		}
		rows = append(rows, row)
	}
	return rows, nil
}

func CreateBigquerySQL(sql string, args BigQueryParam) (string, error) {
	tpl, err := template.New("bigquery").Parse(sql)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)
	err = tpl.Execute(buf, args)

	return buf.String(), err
}

func (t BigQueryClient) ExecQuery(ctx context.Context, verifySql string, request global.Request) (BigQueryRows, error) {
	args := BigQueryParam{Request: request, BigQuerySharedParam: t.SharedParam}
	sql, err := CreateBigquerySQL(verifySql, args)
	if err != nil {
		return nil, err
	}

	rows, err := t.Query(ctx, sql)
	if err != nil {
		log.S().Errorf("sql:%s", sql)
		return nil, err
	}
	return rows, err
}
