using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Npgsql;

namespace SaasMediaWorker.Services;

/// <summary>
/// PostgreSQL veritabanı servisi — RenderJob ve Project durumlarını günceller.
/// NestJS Prisma schema'sıyla uyumlu SQL sorguları kullanır.
///
/// Neden doğrudan SQL (ORM yerine)?
/// - C# Worker minimum footprint olmalı (16GB RPi).
/// - Sadece UPDATE sorguları yapılıyor — ORM gereksiz overhead.
/// - Npgsql ARM64'te native çalışır.
/// </summary>
public class DatabaseService
{
    private readonly ILogger<DatabaseService> _logger;
    private readonly string _connectionString;

    public DatabaseService(
        ILogger<DatabaseService> logger,
        IConfiguration configuration)
    {
        _logger = logger;
        _connectionString = configuration.GetConnectionString("DefaultConnection")
            ?? throw new InvalidOperationException("DefaultConnection bağlantı dizesi bulunamadı");
    }

    /// <summary>
    /// RenderJob tablosunun durumunu günceller.
    /// </summary>
    public async Task UpdateRenderJobStatus(
        string renderJobId,
        string status,
        int progress,
        string? currentStage,
        string? errorMessage = null,
        string? errorStack = null,
        long? processingTimeMs = null,
        string? workerVersion = null,
        string? workerHostname = null)
    {
        await using var conn = new NpgsqlConnection(_connectionString);
        await conn.OpenAsync();

        var sql = @"
            UPDATE ""RenderJob""
            SET ""status"" = @status::""RenderJobStatus"",
                ""currentStage"" = CASE WHEN @stage IS NOT NULL THEN @stage::""RenderStage"" ELSE ""currentStage"" END,
                ""errorMessage"" = COALESCE(@errorMessage, ""errorMessage""),
                ""processingTimeMs"" = COALESCE(@processingTimeMs, ""processingTimeMs""),
                ""workerHostname"" = COALESCE(@workerHostname, ""workerHostname""),
                ""updatedAt"" = NOW()
            WHERE ""id"" = @id";

        await using var cmd = new NpgsqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("id", renderJobId);
        cmd.Parameters.AddWithValue("status", status);
        cmd.Parameters.AddWithValue("progress", progress);
        cmd.Parameters.AddWithValue("stage", (object?)currentStage ?? DBNull.Value);
        cmd.Parameters.AddWithValue("errorMessage", (object?)errorMessage ?? DBNull.Value);
        cmd.Parameters.AddWithValue("errorStack", (object?)errorStack ?? DBNull.Value);
        cmd.Parameters.AddWithValue("processingTimeMs", (object?)processingTimeMs ?? DBNull.Value);
        cmd.Parameters.AddWithValue("workerVersion", (object?)workerVersion ?? DBNull.Value);
        cmd.Parameters.AddWithValue("workerHostname", (object?)workerHostname ?? DBNull.Value);

        var affected = await cmd.ExecuteNonQueryAsync();
        _logger.LogDebug("RenderJob güncellendi: {Id} → {Status} ({Progress}%)", renderJobId, status, progress);
    }

    /// <summary>
    /// Project tablosunun durumunu günceller.
    /// </summary>
    public async Task UpdateProjectStatus(
        string projectId,
        string status,
        int progress,
        string? finalVideoUrl = null,
        string? errorMessage = null)
    {
        await using var conn = new NpgsqlConnection(_connectionString);
        await conn.OpenAsync();

        var sql = @"
            UPDATE ""Project""
            SET ""status"" = @status::""ProjectStatus"",
                ""progress"" = @progress,
                ""finalVideoUrl"" = COALESCE(@finalVideoUrl, ""finalVideoUrl""),
                ""errorMessage"" = @errorMessage,
                ""completedAt"" = CASE WHEN @status = 'COMPLETED' THEN NOW() ELSE ""completedAt"" END,
                ""updatedAt"" = NOW()
            WHERE ""id"" = @id";

        await using var cmd = new NpgsqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("id", projectId);
        cmd.Parameters.AddWithValue("status", status);
        cmd.Parameters.AddWithValue("progress", progress);
        cmd.Parameters.AddWithValue("finalVideoUrl", (object?)finalVideoUrl ?? DBNull.Value);
        cmd.Parameters.AddWithValue("errorMessage", (object?)errorMessage ?? DBNull.Value);

        await cmd.ExecuteNonQueryAsync();
        _logger.LogDebug("Project güncellendi: {Id} → {Status} ({Progress}%)", projectId, status, progress);
    }

    /// <summary>
    /// Render log kaydı ekler.
    /// </summary>
    public async Task AddRenderLog(
        string renderJobId,
        string stage,
        string message,
        string level = "info",
        int? durationMs = null,
        string? metadata = null)
    {
        await using var conn = new NpgsqlConnection(_connectionString);
        await conn.OpenAsync();

        var sql = @"
            INSERT INTO ""RenderLog"" (""id"", ""renderJobId"", ""stage"", ""message"", ""level"", ""durationMs"", ""metadata"", ""createdAt"")
            VALUES (gen_random_uuid(), @renderJobId, @stage::""RenderStage"", @message, @level, @durationMs, @metadata::jsonb, NOW())";

        await using var cmd = new NpgsqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("renderJobId", renderJobId);
        cmd.Parameters.AddWithValue("stage", stage);
        cmd.Parameters.AddWithValue("message", message);
        cmd.Parameters.AddWithValue("level", level);
        cmd.Parameters.AddWithValue("durationMs", (object?)durationMs ?? DBNull.Value);
        cmd.Parameters.AddWithValue("metadata", (object?)metadata ?? DBNull.Value);

        await cmd.ExecuteNonQueryAsync();
    }
}
