package com.clanstop.healing.persistence;

import com.clanstop.healing.config.FrameworkConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Types;

/**
 * Writes {@code test_run} and {@code step_result} rows. Best-effort: on any DB failure the
 * relevant id comes back as -1 / a no-op, and the test run continues unaffected.
 */
public final class RunLogDao {

    private static final Logger log = LoggerFactory.getLogger(RunLogDao.class);

    public long createRun() {
        if (!Db.available()) {
            return -1;
        }
        FrameworkConfig cfg = FrameworkConfig.get();
        String sql = "INSERT INTO test_run (env, git_sha, status) VALUES (?, ?, 'RUNNING')";
        try (Connection c = Db.connection();
             PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            ps.setString(1, cfg.env());
            if (cfg.gitSha() != null) {
                ps.setString(2, cfg.gitSha());
            } else {
                ps.setNull(2, Types.VARCHAR);
            }
            ps.executeUpdate();
            try (ResultSet keys = ps.getGeneratedKeys()) {
                if (keys.next()) {
                    return keys.getLong(1);
                }
            }
        } catch (Exception e) {
            log.warn("createRun failed: {}", e.getMessage());
        }
        return -1;
    }

    public void finishRun(long runId, String status) {
        if (!Db.available() || runId < 0) {
            return;
        }
        String sql = "UPDATE test_run SET finished_at = now(), status = ? WHERE id = ?";
        try (Connection c = Db.connection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, status);
            ps.setLong(2, runId);
            ps.executeUpdate();
        } catch (Exception e) {
            log.warn("finishRun failed: {}", e.getMessage());
        }
    }

    /** Insert a RUNNING step row and return its id (so heals during the step can FK to it). */
    public long startStep(long runId, String feature, String scenario, String stepText, int ordinal) {
        if (!Db.available() || runId < 0) {
            return -1;
        }
        String sql = """
            INSERT INTO step_result (run_id, feature, scenario, step_text, step_ordinal, status)
            VALUES (?, ?, ?, ?, ?, 'RUNNING')
            """;
        try (Connection c = Db.connection();
             PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
            ps.setLong(1, runId);
            ps.setString(2, feature);
            ps.setString(3, scenario);
            ps.setString(4, stepText);
            ps.setInt(5, ordinal);
            ps.executeUpdate();
            try (ResultSet keys = ps.getGeneratedKeys()) {
                if (keys.next()) {
                    return keys.getLong(1);
                }
            }
        } catch (Exception e) {
            log.warn("startStep failed: {}", e.getMessage());
        }
        return -1;
    }

    public void finishStep(long stepId, String status, long durationMs, String error, String screenshotPath) {
        if (!Db.available() || stepId < 0) {
            return;
        }
        String sql = "UPDATE step_result SET status = ?, duration_ms = ?, error_message = ?, screenshot_path = ? WHERE id = ?";
        try (Connection c = Db.connection(); PreparedStatement ps = c.prepareStatement(sql)) {
            ps.setString(1, status);
            ps.setLong(2, durationMs);
            if (error != null) {
                ps.setString(3, error.length() > 4000 ? error.substring(0, 4000) : error);
            } else {
                ps.setNull(3, Types.VARCHAR);
            }
            if (screenshotPath != null) {
                ps.setString(4, screenshotPath);
            } else {
                ps.setNull(4, Types.VARCHAR);
            }
            ps.setLong(5, stepId);
            ps.executeUpdate();
        } catch (Exception e) {
            log.warn("finishStep failed: {}", e.getMessage());
        }
    }
}
