package com.writeoff.module.scheduler.repository;

import com.writeoff.module.scheduler.model.AsyncJob;
import com.writeoff.module.scheduler.model.AsyncJobStatus;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import com.writeoff.security.AuthContext;

import java.sql.PreparedStatement;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Repository
@ConditionalOnProperty(prefix = "app.repository", name = "mode", havingValue = "jdbc", matchIfMissing = true)
public class JdbcAsyncJobRepository implements AsyncJobRepository {
    private final JdbcTemplate jdbcTemplate;
    private static final RowMapper<AsyncJob> ROW_MAPPER = (rs, n) -> new AsyncJob(
            rs.getLong("id"),
            rs.getLong("tenant_id"),
            rs.getString("job_type"),
            rs.getString("payload"),
            AsyncJobStatus.valueOf(rs.getString("status")),
            rs.getTimestamp("next_run_at").toLocalDateTime(),
            rs.getInt("retry_count"),
            rs.getInt("max_retry"),
            rs.getString("idempotency_key"),
            rs.getString("locked_by"),
            rs.getTimestamp("locked_at") == null ? null : rs.getTimestamp("locked_at").toLocalDateTime()
    );

    public JdbcAsyncJobRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public AsyncJob save(AsyncJob job) {
        if (job.getId() == null) {
            KeyHolder keyHolder = new GeneratedKeyHolder();
            jdbcTemplate.update(connection -> {
                PreparedStatement ps = connection.prepareStatement(
                        "INSERT INTO async_job (tenant_id, job_type, payload, status, next_run_at, retry_count, max_retry, idempotency_key, locked_by, locked_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                        Statement.RETURN_GENERATED_KEYS
                );
                Long tenantId = job.getTenantId() == null ? tenantId() : job.getTenantId();
                ps.setLong(1, tenantId);
                ps.setString(2, job.getJobType());
                ps.setString(3, job.getPayload());
                ps.setString(4, job.getStatus().name());
                ps.setObject(5, job.getNextRunAt());
                ps.setInt(6, job.getRetryCount());
                ps.setInt(7, job.getMaxRetry());
                ps.setString(8, job.getIdempotencyKey());
                ps.setString(9, job.getLockedBy());
                ps.setObject(10, job.getLockedAt());
                return ps;
            }, keyHolder);
            Number key = keyHolder.getKey();
            Long id = key == null ? null : key.longValue();
            return new AsyncJob(id, job.getTenantId(), job.getJobType(), job.getPayload(), job.getStatus(), job.getNextRunAt(), job.getRetryCount(), job.getMaxRetry(), job.getIdempotencyKey(), job.getLockedBy(), job.getLockedAt());
        }
        jdbcTemplate.update(
                "UPDATE async_job SET status=?, next_run_at=?, retry_count=?, max_retry=?, locked_by=?, locked_at=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
                job.getStatus().name(),
                job.getNextRunAt(),
                job.getRetryCount(),
                job.getMaxRetry(),
                job.getLockedBy(),
                job.getLockedAt(),
                job.getId()
        );
        return job;
    }

    @Override
    public Optional<AsyncJob> findById(Long id) {
        List<AsyncJob> list = jdbcTemplate.query("SELECT * FROM async_job WHERE id=?", ROW_MAPPER, id);
        return list.stream().findFirst();
    }

    @Override
    public Optional<AsyncJob> findByIdempotencyKey(String idempotencyKey) {
        if (idempotencyKey == null || idempotencyKey.isEmpty()) {
            return Optional.empty();
        }
        List<AsyncJob> list = jdbcTemplate.query("SELECT * FROM async_job WHERE idempotency_key=?", ROW_MAPPER, idempotencyKey);
        return list.stream().findFirst();
    }

    @Override
    public List<AsyncJob> findReady(LocalDateTime now, int limit) {
        return jdbcTemplate.query(
                "SELECT * FROM async_job WHERE status='READY' AND next_run_at<=? ORDER BY next_run_at ASC LIMIT ?",
                ROW_MAPPER,
                now,
                limit
        );
    }

    private Long tenantId() {
        Long tenantId = AuthContext.tenantId();
        return tenantId == null ? 0L : tenantId;
    }
}
