package services

import (
	"sync"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/db"
	q "github.com/fiskerinc/cloud-services/pkg/db/queries"
	"github.com/fiskerinc/cloud-services/pkg/logger"
)

var (
	dbOnce     sync.Once
	dbInstance *DB
)

type DB struct {
	client                     *db.DBClient
	cars                       q.CarsInterface
	carVersionsLog             q.CarVersionsLogInterface
	certificates               q.CertificatesInterface
	updateManifest             q.UpdateManifestsInterface
	updateManifestSUMSVersions q.SUMSVersionsInterface

	onceClient                     sync.Once
	onceCars                       sync.Once
	onceCarVersionsLog             sync.Once
	onceCertificates               sync.Once
	onceUpdateManifest             sync.Once
	onceUpdateManifestSUMSVersions sync.Once
}

func GetDB() *DB {
	dbOnce.Do(func() {
		if dbInstance != nil {
			return
		}
		logger.Info().Msg("init DB instance")
		dbInstance = &DB{}
	})
	return dbInstance
}

func SetDB(db *DB) {
	if dbInstance != nil {
		dbInstance.Close()
	}
	dbInstance = db
}

func (d *DB) GetDBClient() *db.DBClient {
	d.onceClient.Do(func() {
		if d.client != nil {
			return
		}
		logger.Info().Msg("init DBClient instance")
		client := &db.DBClient{}

		err := client.InitSchema([]interface{}{
			(*common.UpdateManifest)(nil),
			(*common.CarUpdate)(nil),
			(*common.CarToDriver)(nil),
			(*common.CarSetting)(nil),
			(*common.SUMSVersion)(nil),
		})
		if err != nil {
			logger.Error().Err(err).Send()
		}
		d.client = client
	})
	return d.client
}

func (d *DB) SetDBClient(client *db.DBClient) {
	if d.client != nil {
		d.client.Close()
	}
	d.client = client
}

func (d *DB) Close() {
	if d.client == nil {
		return
	}
	d.client.Close()
}

///----------

func (d *DB) GetCars() q.CarsInterface {
	d.onceCars.Do(func() {
		if d.cars != nil {
			return
		}
		instance := &q.Cars{}
		instance.SetClient(d.GetDBClient())
		d.cars = instance
	})
	return d.cars
}

func (d *DB) SetCars(cars q.CarsInterface) {
	d.cars = cars
}

func (d *DB) GetCarVersionsLog() q.CarVersionsLogInterface {
	d.onceCarVersionsLog.Do(func() {
		if d.carVersionsLog != nil {
			return
		}
		instance := &q.CarVersionsLog{}
		instance.SetClient(d.GetDBClient())
		d.carVersionsLog = instance
	})
	return d.carVersionsLog
}

func (d *DB) SetCarVersionsLog(log q.CarVersionsLogInterface) {
	d.carVersionsLog = log
}

func (d *DB) GetCertificates() q.CertificatesInterface {
	d.onceCertificates.Do(func() {
		if d.certificates != nil {
			return
		}
		logger.Debug().Msg("Init Certificates instance")
		certificates := &q.Certificates{}
		certificates.SetClient(d.GetDBClient())
		d.certificates = certificates
	})
	return d.certificates
}

func (d *DB) SetCertificates(certificates q.CertificatesInterface) {
	d.certificates = certificates
}

func (d *DB) GetUpdateManifests() q.UpdateManifestsInterface {
	d.onceUpdateManifest.Do(func() {
		if d.updateManifest != nil {
			return
		}
		logger.Debug().Msg("Init UpdateManifest instance")
		updateManifest := q.NewUpdateManifest(nil)
		updateManifest.SetClient(d.GetDBClient())
		d.updateManifest = updateManifest
	})
	return d.updateManifest
}

func (d *DB) SetUpdateManifests(updateManifest q.UpdateManifestsInterface) {
	d.updateManifest = updateManifest
}

func (d *DB) GetUpdateManifestSUMSVersions() q.SUMSVersionsInterface {
	d.onceUpdateManifestSUMSVersions.Do(func() {
		if d.updateManifestSUMSVersions != nil {
			return
		}
		instance := &q.SUMSVersions{}
		instance.SetClient(d.GetDBClient())
		d.updateManifestSUMSVersions = instance
	})
	return d.updateManifestSUMSVersions
}

func (d *DB) SetUpdateManifestVersions(umv q.SUMSVersionsInterface) {
	d.updateManifestSUMSVersions = umv
}
