package store

import (
	"context"
	"fmt"
	"io"
	"strings"

	dto "github.com/prometheus/client_model/go"
	"github.com/tidbcloud/serverless-metering/pkg/config"
)

type FileType int

const (
	FileTypeUnknown FileType = iota
	FileTypeJson
	FileTypeParquet
)

type Metric struct {
	Timestamp int64             `json:"timestamp"`
	Name      string            `json:"name"`
	Type      int32             `json:"type"`
	Value     float64           `json:"value"`
	Labels    map[string]string `json:"labels"`
}

type LocalFile struct {
	Path string
	End  int64
}

type RemoteFile struct {
	Name string
	Body io.ReadCloser
}

type CheckPoint struct {
	Value int64 `json:"value"`
}

type RcuUsage struct {
	KeyspaceID string
	Timestamps []int64
	Values     []int64
}

type RcuCapacity struct {
	KeyspaceID          uint32 `json:"keyspace_id"`
	ClusterID           uint64 `json:"cluster_id"`
	ProvisionedCapacity int64  `json:"provisioned_capacity"`
	MinRcu              int64  `json:"min_rcu"`
	MaxRcu              int64  `json:"max_rcu"`
}

type LocalStorage interface {
	// Log stores metricFamilies to LocalStorage
	Log(labels map[string]string, metricFamilies map[string]*dto.MetricFamily) error
	// LocalFiles load files with end ts larger than cursor timestamp from local storage
	LoadFiles(cursor int64) ([]*LocalFile, error)
}

type RemoteScanner interface {
	// ScanMetrics fetch metrics from remote storage one by one
	ScanMetrics(ctx context.Context) (metrics []*Metric, more bool, err error)
	// ScanFiles fetch file body from remote storage one by one
	ScanRemoteFiles(ctx context.Context) (rf *RemoteFile, more bool, err error)
}

type RemoteStorage interface {
	// Upload uploads a local file to RemoteStorage
	Upload(ctx context.Context, file *LocalFile) error
	// BuildScanner finds the directory by ts, then build a scanner to scan the directory
	BuildScanner(ctx context.Context, ts int64) (scanner RemoteScanner, err error)
	// PutData write data to remote storage key
	PutData(ctx context.Context, key string, data []byte) error
	// GetData get data from remote storage key
	GetData(ctx context.Context, key string) ([]byte, error)
}

type Storage interface {
	// Log is a wrapper of LocalStorage's Log
	Log(labels map[string]string, metricFamilies map[string]*dto.MetricFamily) error
	// BuildRemoteScanner is a wrapper of RemoteStorage's BuildScanner
	BuildRemoteScanner(ctx context.Context, ts int64) (scanner RemoteScanner, err error)
	// PutCheckPoint write check point to remote storage
	PutCheckPoint(ctx context.Context, name string, checkpoint *CheckPoint) error
	// GetCheckPoint get check point from remote storage
	GetCheckPoint(ctx context.Context, name string) (*CheckPoint, error)
	// PutIntermediateResult write intermediate data to remote storage
	PutIntermediateResult(ctx context.Context, ts int64, digest string, data []byte) error
	// GetIntermediateResult get intermediate data from remote storage
	GetIntermediateResult(ctx context.Context, ts int64, digest string) ([]byte, error)
}

// RcuStore stores rcu metrics and capacity
type RcuStore interface {
	WriteMetrics(labels map[string]string, metricFamilies *dto.MetricFamily) error
	CalRcuUsages(keyspaceIds []uint32, startTs int64, endTs int64, useRUV2 bool) ([]*RcuUsage, error)
	WriteRcuCapacity(capacity *RcuCapacity) error
	ListRcuCapacities() ([]*RcuCapacity, error)
	DeleteRcuCapacity(keyspaceID uint32) error
}

// GetCheckPointName assemble checkpoint name by hostname
func GetCheckPointName(hostname string) string {
	return fmt.Sprintf("store-%s", hostname)
}

func GetFileType() FileType {
	cfg := config.Global().Store.Local
	if strings.HasSuffix(cfg.Filename, ".log") {
		return FileTypeJson
	} else if strings.HasSuffix(cfg.Filename, ".db") {
		return FileTypeParquet
	}
	return FileTypeUnknown
}
