package db

import (
	message "client/internal/msg"
	"database/sql"
	"fmt"
	"log"

	_ "github.com/mattn/go-sqlite3" // Import the sqlite3 driver
)

type SQLiteServer struct {
	DB *sql.DB
}

// NewSQLiteServer creates a new SQLite server instance with an open database connection
func NewSQLiteServer() *SQLiteServer {
	// Open (or create) the SQLite database
	db, err := sql.Open("sqlite3", "./mining_task.db")
	if err != nil {
		log.Println(err)
		return nil
	}

	// Create an SQLiteServer instance with the database connection
	s := &SQLiteServer{
		DB: db,
	}

	// Initialize the 'task' table
	if err := s.initTaskTable(); err != nil {
		log.Println(err)
		return nil
	}

	return s
}

// initTaskTable creates the 'task' table if it doesn't already exist
func (s *SQLiteServer) initTaskTable() error {
	// SQL statement to create the 'task' table if it doesn't exist
	str := `
	CREATE TABLE IF NOT EXISTS task (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		coin TEXT,
		algo TEXT,
		pool TEXT,
		wallet_mining TEXT,
		pool_url TEXT,
		wallet_address TEXT,
		pool_user TEXT,
		worker_id TEXT,
		end_timestamp INTEGER,
		state INTEGER
	);`

	// Execute the query to create the table
	_, err := s.DB.Exec(str)
	if err != nil {
		return fmt.Errorf("failed to create table: %w", err)
	}

	return nil
}

// Close closes the database connection manually when done with it
func (s *SQLiteServer) Close() error {
	// Close the database connection
	if err := s.DB.Close(); err != nil {
		return fmt.Errorf("failed to close database: %w", err)
	}
	return nil
}

// LoadMiningTask loads a mining task from the database
func (s *SQLiteServer) LoadMiningTask() (bool, message.ConfigurationMiningMsg) {
	str := "SELECT coin, algo, pool, wallet_mining, pool_url, wallet_address, pool_user, worker_id, end_timestamp FROM task WHERE state = ?;"
	params := []any{0}
	// Query the database
	rows, err := s.DB.Query(str, params...)
	if err != nil {
		log.Println(err)
	}
	defer rows.Close()

	// Check if exactly one row is returned
	var task message.ConfigurationMiningMsg
	if rows.Next() {
		// Scan the row into the task structure
		err := rows.Scan(&task.Coin, &task.Algo, &task.Pool, &task.WalletMining, &task.PoolUrl, &task.WalletAddress, &task.PoolUser, &task.WorkerID, &task.EndTimestamp)
		if err != nil {
			log.Println(err)
		}

		// Check if there's more than one row
		if rows.Next() {
			// If there are multiple rows, return false with an empty structure
			return false, message.ConfigurationMiningMsg{}
		}

		// If exactly one row is found, return true with the data
		return true, task
	}

	// No rows found, return false with an empty structure
	return false, message.ConfigurationMiningMsg{}
}

// InsertMiningTask inserts a new mining task into the database
func (s *SQLiteServer) InsertMiningTask(msg message.ConfigurationMiningMsg) error {
	// SQL INSERT statement
	str := `
		INSERT INTO task (coin, algo, pool, wallet_mining, pool_url, wallet_address, pool_user, worker_id, end_timestamp, state)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
	`

	// Set the state to 0 by default (active or pending)
	state := 0

	// Execute the query
	_, err := s.DB.Exec(str, msg.Coin, msg.Algo, msg.Pool, msg.WalletMining, msg.PoolUrl, msg.WalletAddress, msg.PoolUser, msg.WorkerID, msg.EndTimestamp, state)
	if err != nil {
		return fmt.Errorf("failed to insert mining task: %w", err)
	}

	return nil
}

// FinishMiningTask updates the state of a mining task to 'finished' (state = 1)
func (s *SQLiteServer) FinishMiningTask(msg message.ConfigurationMiningMsg) error {
	// SQL UPDATE statement, matching all fields
	str := `
		UPDATE task
		SET state = 1
		WHERE coin = ? AND algo = ? AND pool = ? AND wallet_mining = ? AND pool_url = ? AND wallet_address = ? AND pool_user = ? AND worker_id = ? AND end_timestamp = ?;
	`

	// Execute the query
	_, err := s.DB.Exec(str, msg.Coin, msg.Algo, msg.Pool, msg.WalletMining, msg.PoolUrl, msg.WalletAddress, msg.PoolUser, msg.WorkerID, msg.EndTimestamp)
	if err != nil {
		return fmt.Errorf("failed to finish mining task: %w", err)
	}

	return nil
}
