package vassistent.service;

import vassistent.model.DatabaseEntry;
import vassistent.util.Logger;

import java.io.File;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Handles SQLite initialization and read/write operations for binary event data.
 */
public class DataPersistenceService {

    private static final String DB_FOLDER = "data";
    private final String DB_URL;

    /**
     * Creates a persistence service using the default database location.
     */
    public DataPersistenceService() {
        DB_URL = "jdbc:sqlite:data/health.db";
        createDatabaseFolder();
        init();
    }

    // For tests
    /**
     * Creates a persistence service using a custom JDBC URL (primarily for tests).
     *
     * @param DB_URL JDBC URL to use for all database operations
     */
    public DataPersistenceService(String DB_URL) {
        this.DB_URL = DB_URL;
        createDatabaseFolder();
        init();
    }

    /**
     * Ensures that the local database directory exists.
     */
    public void createDatabaseFolder() {
        File folder = new File(DB_FOLDER);
        if (!folder.exists()) {
            boolean created = folder.mkdirs();
            if (created) {
                Logger.info("DB", "Database folder created");
            } else {
                Logger.warn("DB", "Could not create database folder");
            }
        }
    }

    /**
     * Initializes the database schema if it does not exist.
     */
    private void init() {
        try (
                Connection c = DriverManager.getConnection(DB_URL); Statement s = c.createStatement()) {

            s.execute("""
                    CREATE TABLE IF NOT EXISTS binary_event (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        value INTEGER NOT NULL,
                        timestamp DATETIME NOT NULL
                      )
                      """);

            Logger.info("DB", "Database initialized");

        } catch (SQLException e) {
            Logger.error("DB", "Database initialization failed", e);
        }
    }

    /**
     * Stores a binary prediction value with the current timestamp.
     *
     * @param value prediction value (expected {@code 0} or {@code 1})
     */
    public void store(int value) {
        try (
                Connection c = DriverManager.getConnection(DB_URL); PreparedStatement ps = c.prepareStatement(
                "INSERT INTO binary_event (value, timestamp) VALUES (?, ?)"
        )) {
            ps.setInt(1, value);
            ps.setString(2, LocalDateTime.now().toString());
            ps.executeUpdate();
            //Logger.debug("DB", "Database insert successful: " + ps.toString());
        } catch (SQLException e) {
            Logger.error("DB", "Database insert failed", e);
        }
    }

    /**
     * Loads the newest entries and returns them in chronological order.
     *
     * @param count maximum number of entries to return
     * @return list of entries ordered from oldest to newest within the selected window
     */
    public List<DatabaseEntry> getLastEntries(int count) {
        List<DatabaseEntry> result = new ArrayList<>();

        String query = """
                SELECT value, timestamp
                FROM binary_event
                ORDER BY id DESC
                LIMIT ?
                """;
        try (Connection c = DriverManager.getConnection(DB_URL); PreparedStatement ps = c.prepareStatement(query)) {

            ps.setInt(1, count);

            ResultSet rs = ps.executeQuery();

            while (rs.next()) {

                String ts = rs.getString("timestamp");

                LocalDateTime timestamp =
                        LocalDateTime.parse(ts);

                double value = rs.getDouble("value");

                result.add(new DatabaseEntry(timestamp, value));
            }

        } catch (SQLException e) {
            Logger.error("DB",
                    "Failed to load latest entries", e);
        }

        Collections.reverse(result);
        return result;
    }

    /**
     * Returns the configured JDBC connection URL.
     *
     * @return JDBC URL used by this service
     */
    public String getDbUrl() {
        return DB_URL;
    }
}
