package facade

import (
	"context"
	"fmt"
	"os"

	"github.com/tidbcloud/observability/o11ymetering/pkg/common/config"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/constants"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/models"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade/database"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade/iface"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade/o11ymeta"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade/o11ymetrics"
	"github.com/tidbcloud/observability/o11ymetering/pkg/facade/s3storage"
	"github.com/tidbcloud/observability/pkg/common/configs"
)

var statisticRW iface.StatisticReaderWriter
var currentPeriod models.Period

var statisticMergeRW iface.StatisticMergeReaderWriter
var currentMergeRWTimestamp int64

var statisticReportRW iface.StatisticReportReaderWriter

var o11yMetaQuery iface.O11yMetaQuery
var o11yMetricQuery iface.O11yMetricsQuery

var checkpointRW iface.CheckpointReaderWriter
var statisticDataSync iface.ScheduleTaskDataSync

func SwitchStatisticReaderWriter(ctx context.Context, period models.Period) iface.StatisticReaderWriter {
	if statisticRW == nil || currentPeriod != period {
		if statisticRW != nil {
			database.CloseSqliteDB(statisticRW.(*database.GormRW).DB)
		}
		var db *database.Database
		var err error

		timestamp := period.StartTime().Unix()

		dir := fmt.Sprintf("%s/%s/%s", "data", "metering", "statistic")
		err = os.MkdirAll(dir, 0755)
		if err != nil {
			panic(err)
		}
		localFilePath := fmt.Sprintf("%s/%d.%s", dir, timestamp, "sqlite")

		db, err = database.GetSqliteDB(localFilePath)
		if err != nil {
			panic(err)
		}
		if db == nil {
			panic("No valid database config found")
		}

		statisticRW = &database.GormRW{
			DB: db,
		}
		currentPeriod = period
	}
	return statisticRW
}

func SwitchStatisticMergeReaderWriter(ctx context.Context, timestamp int64) iface.StatisticMergeReaderWriter {
	if statisticMergeRW == nil || currentMergeRWTimestamp != timestamp {
		if statisticMergeRW != nil {
			database.CloseSqliteDB(statisticMergeRW.(*database.GormRW).DB)
		}
		var db *database.Database
		var err error

		dir := fmt.Sprintf("%s/%s/%s", "data", "metering", constants.StatisticMergeTaskName)
		err = os.MkdirAll(dir, 0755)
		if err != nil {
			panic(err)
		}
		localFilePath := fmt.Sprintf("%s/%d.%s", dir, timestamp, "sqlite")

		db, err = database.GetSqliteDB(localFilePath)

		if err != nil {
			panic(err)
		}
		if db == nil {
			panic("No valid database config found")
		}

		statisticMergeRW = &database.GormRW{
			DB: db,
		}
		currentMergeRWTimestamp = timestamp
	}
	return statisticMergeRW
}

func GetStatisticReportWriter() iface.StatisticReportReaderWriter {
	if statisticReportRW == nil {
		var db *database.Database
		var err error

		list := config.GetDatabases()
		for _, dbConfig := range list {
			if dbConfig.Use == "report" {
				if dbConfig.Type == "sqlite" {
					db, err = database.GetSqliteDB(dbConfig.Dsn)
					if err != nil {
						panic(err)
					}
				} else if dbConfig.Type == "tidb-serverless" {
					db, err = database.GetServerlessDB(dbConfig)
					if err != nil {
						panic(err)
					}
				}
			}
		}
		if db == nil {
			panic("No valid database config found")
		}

		statisticReportRW = &database.GormRW{
			DB: db,
		}
	}
	return statisticReportRW
}

func GetCurrentStatisticMergeReaderWriter() iface.StatisticMergeReaderWriter {
	if statisticMergeRW == nil {
		panic("statisticMergeRW == nil")
	}
	return statisticMergeRW
}

func GetCurrentStatisticReaderWriter() iface.StatisticReaderWriter {
	if statisticRW == nil {
		panic("statisticRW == nil")
	}
	return statisticRW
}

func GetO11yMetaQuery() iface.O11yMetaQuery {
	if o11yMetaQuery == nil {
		o11yMetaQuery = &o11ymeta.O11yMetaAPI{}
	}

	return o11yMetaQuery
}

func GetO11yMetricsQuery() iface.O11yMetricsQuery {
	if o11yMetricQuery == nil {
		o11yMetricQuery = &o11ymetrics.O11yRegionalDataApi{}
	}
	return o11yMetricQuery
}

func GetCheckpointReaderWriter() iface.CheckpointReaderWriter {
	if checkpointRW == nil {
		bucket := fmt.Sprintf("o11y-%s-shared-%s", configs.GetServiceInfo().Env, configs.GetServiceInfo().LogicalRegionName)
		checkpointRW = s3storage.BuildS3Checkpoint(bucket, "metering", GetS3Client())
	}
	return checkpointRW
}

func GetScheduleTaskDataSync() iface.ScheduleTaskDataSync {
	if statisticDataSync == nil {
		bucket := fmt.Sprintf("o11y-%s-shared-%s", configs.GetServiceInfo().Env, configs.GetServiceInfo().LogicalRegionName)
		statisticDataSync = s3storage.BuildS3ScheduleTaskData(bucket, "metering", "sqlite", GetS3Client(), "data")
	}
	return statisticDataSync
}

var cli s3storage.Client

func GetS3Client() s3storage.Client {
	if cli != nil {
		return cli
	}
	return s3storage.PrepareS3Client(context.TODO(), configs.GetServiceInfo().Vendor, configs.GetServiceInfo().Region, configs.GetServiceInfo().AccountInfo.Project)
}

func SetO11yMetaQuery(query iface.O11yMetaQuery) {
	o11yMetaQuery = query
}

func SetO11yMetricsQuery(query iface.O11yMetricsQuery) {
	o11yMetricQuery = query
}

func SetStatisticReaderWriter(rw iface.StatisticReaderWriter) {
	statisticRW = rw
}

func SetStatisticMergeReaderWriter(rw iface.StatisticMergeReaderWriter) {
	statisticMergeRW = rw
}

func SetStatisticReportReaderWriter(rw iface.StatisticReportReaderWriter) {
	statisticReportRW = rw
}

func SetCheckpointReaderWriter(rw iface.CheckpointReaderWriter) {
	checkpointRW = rw
}

func SetScheduleTaskDataSync(sync iface.ScheduleTaskDataSync) {
	statisticDataSync = sync
}

func SetS3Client(client s3storage.Client) {
	cli = client
}
