package db

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"sync"

	"th7/data/config"
	"th7/data/core"
	"th7/data/thermocouple"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

type SQLite3Adapter struct {
	mu  sync.Mutex
	db  *sql.DB
	cfg config.Config
}

const (
	SQLITE3_MAX_INIT_DUR = 5000 * time.Millisecond
	SQLITE3_MAX_SAVE_DUR = 2000 * time.Millisecond
)

func NewSQLite3Adapter(config config.Config) (*SQLite3Adapter, error) {

	var adapter SQLite3Adapter

	// if path is given ...
	if _, ok := config.DB["path"]; !ok {
		return &adapter, errors.New("sqlite3 requires a path config variable")
	}

	path := config.DB["path"].(string)

	db, err := sql.Open("sqlite3", path)
	if err != nil {
		return &adapter, err
	}

	adapter.db = db
	adapter.cfg = config

	initContext, cancel := context.WithTimeout(context.Background(), SQLITE3_MAX_INIT_DUR)
	defer cancel()

	const create string = `
	CREATE TABLE IF NOT EXISTS logs (
		id     INTEGER NOT NULL,
		type      TEXT NOT NULL,
		gain      REAL NOT NULL,
		offset    REAL NOT NULL,
		value     REAL NOT NULL,
		timestamp TEXT NOT NULL
	);`

	if _, err := adapter.db.ExecContext(initContext, create); err != nil {
		return &adapter, err
	}

	return &adapter, nil
}

func (ad *SQLite3Adapter) Close() {
	ad.db.Close()
}

func (ad *SQLite3Adapter) Save(channels []core.Channel) error {
	ad.mu.Lock()
	defer ad.mu.Unlock()

	insertLogSQL := `INSERT INTO logs (id, type, gain, offset, value, timestamp) VALUES (?, ?, ?, ?, ?, ?)`
	statement, err := ad.db.Prepare(insertLogSQL)
	if err != nil {
		fmt.Println(err)
	}

	timestamp := time.Now().Format(time.DateTime)

	saveContext, cancel := context.WithTimeout(context.Background(), SQLITE3_MAX_SAVE_DUR)
	defer cancel()

	for c := range channels {

		id := channels[c].Id
		value := channels[c].Value
		gain := ad.cfg.Channels[c].Gain
		offset := ad.cfg.Channels[c].Offset
		thermo := thermocouple.ThermoStringLookup[ad.cfg.Channels[c].Thermo]

		_, err = statement.ExecContext(saveContext, id, thermo, gain, offset, value, timestamp)
		if err != nil {
			return err
		}

		fmt.Printf("[%s] SQLite3 log: id=%d, type=%s, gain=%.2f, offset=%.2f, value=%.2f\n", timestamp, id, thermo, gain, offset, value)

	}

	return nil
}