package storage

import (
	"context"
	"fmt"
	"sync"
	"time"

	"git.espin.casa/albert/cml04-falcon-ui/types"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type DBConfig struct {
	Username string
	Password string
	Host     string
	Port     int
	Name     string
}

type Storager interface {
	Barcode(ctx context.Context, reading string) (barcode *types.Barcode, err error)
	ListBarcode(ctx context.Context, lb types.LoadingBed, inicio, final time.Time) (barcodes []types.Barcode, err error)
	Bundle(ctx context.Context, ua string) (bundle *types.BundleData, err error)
	ListBundle(ctx context.Context, lb types.LoadingBed, inicio, final time.Time) (bundles []types.BundleData, err error)
}

type storage struct {
	db  *gorm.DB
	mux sync.RWMutex
}

// ListBundle implements Storager.
func (s *storage) ListBundle(ctx context.Context, lb types.LoadingBed, inicio time.Time, final time.Time) (bundles []types.BundleData, err error) {
	if lb == types.ALL {
		if err := s.db.Where("created_at >= ? and created_at < ?", inicio, final).Find(bundles).WithContext(ctx).Error; err != nil {
			return nil, err
		}
	} else {
		if err := s.db.Where("loading_bed = ? and created_at >= ? and created_at < ?", lb, inicio, final).Find(bundles).WithContext(ctx).Error; err != nil {
			return nil, err
		}
	}
	return
}

// Bundle implements Storager.
func (s *storage) Bundle(ctx context.Context, ua string) (bundle *types.BundleData, err error) {
	if err := s.db.First(bundle, ua).WithContext(ctx).Error; err != nil {
		return nil, err
	}
	return
}

// ListBarcode implements Storager.
func (s *storage) ListBarcode(ctx context.Context, lb types.LoadingBed, inicio time.Time, final time.Time) (barcodes []types.Barcode, err error) {
	if lb == types.ALL {
		if err := s.db.Where("created_at >= ? and created_at < ?", inicio, final).Find(&barcodes).WithContext(ctx).Error; err != nil {
			return nil, err
		}
	} else {
		if err := s.db.Where("loading_bed = ? and created_at >= ? and created_at < ?", lb, inicio, final).Find(&barcodes).WithContext(ctx).Error; err != nil {
			return nil, err
		}
	}
	return
}

// Barcode implements Storager.
func (s *storage) Barcode(ctx context.Context, reading string) (barcode *types.Barcode, err error) {
	if err := s.db.First(&barcode, reading).WithContext(ctx).Error; err != nil {
		return nil, err
	}
	return
}

func ProductionDataBase(conf *DBConfig) (*gorm.DB, error) {
	// create dsn string
	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Europe/Madrid",
		conf.Host,
		conf.Username,
		conf.Password,
		conf.Name,
		conf.Port,
	)
	// create open database connection
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		return nil, err
	}
	return db, nil
}

func New(conf *DBConfig) (Storager, error) {
	// database holder
	var db *gorm.DB
	// producctio
	db, err := ProductionDataBase(conf)
	if err != nil {
		return nil, err
	}
	// done
	return &storage{
		db:  db,
		mux: sync.RWMutex{},
	}, nil
}
