using System.Globalization;
using System.Data;
using GestionaDenuncias.Shared.Models;
using MySqlConnector;

namespace ApiDenuncias.Services;

public sealed class InboxTrackingService : IInboxTrackingService
{
    private readonly IDenunciaStore _denunciaStore;
    private readonly MySqlConnectionStringProvider _connectionStringProvider;

    public InboxTrackingService(
        IDenunciaStore denunciaStore,
        MySqlConnectionStringProvider connectionStringProvider)
    {
        _denunciaStore = denunciaStore;
        _connectionStringProvider = connectionStringProvider;
    }

    public async Task<InboxUserState> GetUserStateAsync(string username, CancellationToken cancellationToken = default)
    {
        await _denunciaStore.EnsureSchemaAsync(cancellationToken);

        if (string.IsNullOrWhiteSpace(username))
        {
            return new InboxUserState();
        }

        await using var connection = await OpenConnectionAsync(cancellationToken);
        var userId = await EnsureUserAsync(connection, username, cancellationToken);

        const string sql = """
            SELECT
                username,
                last_successful_download_at_utc,
                last_downloaded_report_moment_utc
            FROM app_users
            WHERE id = @userId;
            """;

        await using var command = new MySqlCommand(sql, connection);
        command.Parameters.AddWithValue("@userId", userId);

        await using var reader = await command.ExecuteReaderAsync(cancellationToken);
        if (!await reader.ReadAsync(cancellationToken))
        {
            return new InboxUserState { Username = username };
        }

        return new InboxUserState
        {
            Username = reader.GetString(reader.GetOrdinal("username")),
            LastSuccessfulDownloadAtUtc = GetDateTimeOffset(reader, "last_successful_download_at_utc"),
            LastDownloadedReportMomentUtc = GetDateTimeOffset(reader, "last_downloaded_report_moment_utc"),
        };
    }

    public async Task<IReadOnlyList<ReportDto>> RegisterSnapshotAsync(
        string username,
        IEnumerable<ReportDto> reports,
        CancellationToken cancellationToken = default)
    {
        await _denunciaStore.EnsureSchemaAsync(cancellationToken);

        var reportList = reports.ToList();
        if (string.IsNullOrWhiteSpace(username) || reportList.Count == 0)
        {
            return reportList;
        }

        await using var connection = await OpenConnectionAsync(cancellationToken);
        var userId = await EnsureUserAsync(connection, username, cancellationToken);
        await using var transaction = await connection.BeginTransactionAsync(cancellationToken);

        try
        {
            foreach (var report in reportList)
            {
                await UpsertInboxReportAsync(connection, transaction, report, cancellationToken);
                await UpsertUserSnapshotAsync(connection, transaction, userId, report.Id, cancellationToken);
            }

            await transaction.CommitAsync(cancellationToken);
        }
        catch
        {
            await SafeRollbackAsync(transaction, cancellationToken);
            throw;
        }

        await EnsureConnectionOpenAsync(connection, cancellationToken);
        var metadata = await LoadMetadataAsync(connection, userId, reportList.Select(r => r.Id).ToList(), cancellationToken);

        return reportList
            .Select(report =>
            {
                metadata.TryGetValue(report.Id, out var meta);
                return report with
                {
                    DownloadedByCurrentUser = meta?.DownloadedByCurrentUser ?? false,
                    DownloadedByAnotherUser = meta?.DownloadedByAnotherUser ?? false,
                    LastDownloadedByUsername = meta?.LastDownloadedByUsername,
                    LastDownloadedAt = meta?.LastDownloadedAtUtc?.ToString("O", CultureInfo.InvariantCulture),
                    AlreadyImported = meta?.AlreadyImported ?? false,
                    AlreadyInGestiona = meta?.AlreadyInGestiona ?? false,
                    TrackingNote = BuildTrackingNote(meta)
                };
            })
            .Where(report => !IsLockedByAnotherUser(report))
            .ToArray();
    }

    public async Task EnsureReportCanBeImportedByUserAsync(
        string username,
        ReportDto report,
        CancellationToken cancellationToken = default)
    {
        await _denunciaStore.EnsureSchemaAsync(cancellationToken);

        if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(report.Id))
        {
            throw new InvalidOperationException("No se ha podido validar la propiedad de la denuncia.");
        }

        await using var connection = await OpenConnectionAsync(cancellationToken);
        var userId = await EnsureUserAsync(connection, username, cancellationToken);
        await using var transaction = await connection.BeginTransactionAsync(cancellationToken);

        try
        {
            await UpsertInboxReportAsync(connection, (MySqlTransaction)transaction, report, cancellationToken);
            await transaction.CommitAsync(cancellationToken);
        }
        catch
        {
            await SafeRollbackAsync(transaction, cancellationToken);
            throw;
        }

        await EnsureConnectionOpenAsync(connection, cancellationToken);
        var metadata = await LoadMetadataAsync(connection, userId, [report.Id], cancellationToken);
        if (metadata.TryGetValue(report.Id, out var meta) && meta.LockedByAnotherUser)
        {
            var owner = string.IsNullOrWhiteSpace(meta.LastDownloadedByUsername)
                ? "otro usuario"
                : meta.LastDownloadedByUsername;

            throw new InvalidOperationException(
                $"La denuncia ya fue importada por {owner}. Solo ese usuario puede ver e importar sus actualizaciones.");
        }
    }

    public async Task MarkReportImportedAsync(
        string username,
        ReportDto report,
        int? complaintId,
        CancellationToken cancellationToken = default)
    {
        await _denunciaStore.EnsureSchemaAsync(cancellationToken);

        if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(report.Id))
        {
            return;
        }

        var reportMoment = ResolveReportMoment(report);
        var nowUtc = DateTime.UtcNow;

        await using var connection = await OpenConnectionAsync(cancellationToken);
        var userId = await EnsureUserAsync(connection, username, cancellationToken);
        await using var transaction = await connection.BeginTransactionAsync(cancellationToken);

        try
        {
            await UpsertInboxReportAsync(connection, transaction, report, cancellationToken);

            const string updateInboxSql = """
                UPDATE inbox_reports
                SET
                    last_downloaded_at_utc = @nowUtc,
                    last_downloaded_by_user_id = @userId,
                    imported_complaint_report_id = COALESCE(@complaintId, imported_complaint_report_id),
                    imported_to_store_at_utc = COALESCE(imported_to_store_at_utc, @nowUtc),
                    updated_at_utc = CURRENT_TIMESTAMP(6)
                WHERE global_report_uuid = @reportId;
                """;

            await using (var updateInbox = new MySqlCommand(updateInboxSql, connection, (MySqlTransaction)transaction))
            {
                updateInbox.Parameters.AddWithValue("@nowUtc", nowUtc);
                updateInbox.Parameters.AddWithValue("@userId", userId);
                updateInbox.Parameters.AddWithValue("@complaintId", complaintId.HasValue ? complaintId.Value : DBNull.Value);
                updateInbox.Parameters.AddWithValue("@reportId", report.Id);
                await updateInbox.ExecuteNonQueryAsync(cancellationToken);
            }

            const string updateUserReportSql = """
                INSERT INTO user_inbox_reports (
                    app_user_id,
                    inbox_report_id,
                    first_seen_at_utc,
                    last_seen_at_utc,
                    first_downloaded_at_utc,
                    last_downloaded_at_utc,
                    download_count
                )
                SELECT
                    @userId,
                    ir.id,
                    CURRENT_TIMESTAMP(6),
                    CURRENT_TIMESTAMP(6),
                    @nowUtc,
                    @nowUtc,
                    1
                FROM inbox_reports ir
                WHERE ir.global_report_uuid = @reportId
                ON DUPLICATE KEY UPDATE
                    last_seen_at_utc = CURRENT_TIMESTAMP(6),
                    first_downloaded_at_utc = COALESCE(first_downloaded_at_utc, VALUES(first_downloaded_at_utc)),
                    last_downloaded_at_utc = VALUES(last_downloaded_at_utc),
                    download_count = download_count + 1;
                """;

            await using (var updateUserReport = new MySqlCommand(updateUserReportSql, connection, (MySqlTransaction)transaction))
            {
                updateUserReport.Parameters.AddWithValue("@userId", userId);
                updateUserReport.Parameters.AddWithValue("@nowUtc", nowUtc);
                updateUserReport.Parameters.AddWithValue("@reportId", report.Id);
                await updateUserReport.ExecuteNonQueryAsync(cancellationToken);
            }

            const string updateUserSql = """
                UPDATE app_users
                SET
                    last_successful_download_at_utc = @nowUtc,
                    last_downloaded_report_moment_utc =
                        CASE
                            WHEN @reportMoment IS NULL THEN last_downloaded_report_moment_utc
                            WHEN last_downloaded_report_moment_utc IS NULL THEN @reportMoment
                            WHEN @reportMoment > last_downloaded_report_moment_utc THEN @reportMoment
                            ELSE last_downloaded_report_moment_utc
                        END,
                    updated_at_utc = CURRENT_TIMESTAMP(6)
                WHERE id = @userId;
                """;

            await using (var updateUser = new MySqlCommand(updateUserSql, connection, (MySqlTransaction)transaction))
            {
                updateUser.Parameters.AddWithValue("@nowUtc", nowUtc);
                updateUser.Parameters.AddWithValue("@reportMoment", ToDbDate(reportMoment));
                updateUser.Parameters.AddWithValue("@userId", userId);
                await updateUser.ExecuteNonQueryAsync(cancellationToken);
            }

            await transaction.CommitAsync(cancellationToken);
        }
        catch
        {
            await SafeRollbackAsync(transaction, cancellationToken);
            throw;
        }
    }

    public async Task MarkReportHandledInGestionaAsync(
        string username,
        int denunciaId,
        DateTime uploadedAtUtc,
        CancellationToken cancellationToken = default)
    {
        await _denunciaStore.EnsureSchemaAsync(cancellationToken);

        if (string.IsNullOrWhiteSpace(username) || denunciaId <= 0)
        {
            return;
        }

        var handledAtUtc = uploadedAtUtc.Kind switch
        {
            DateTimeKind.Utc => uploadedAtUtc,
            DateTimeKind.Local => uploadedAtUtc.ToUniversalTime(),
            _ => DateTime.SpecifyKind(uploadedAtUtc, DateTimeKind.Utc)
        };

        await using var connection = await OpenConnectionAsync(cancellationToken);
        var userId = await EnsureUserAsync(connection, username, cancellationToken);
        await using var transaction = await connection.BeginTransactionAsync(cancellationToken);

        try
        {
            const string updateInboxSql = """
                UPDATE inbox_reports
                SET
                    last_downloaded_at_utc =
                        CASE
                            WHEN last_downloaded_at_utc IS NULL THEN @handledAtUtc
                            WHEN @handledAtUtc > last_downloaded_at_utc THEN @handledAtUtc
                            ELSE last_downloaded_at_utc
                        END,
                    last_downloaded_by_user_id = @userId,
                    imported_complaint_report_id = COALESCE(imported_complaint_report_id, @denunciaId),
                    imported_to_store_at_utc = COALESCE(imported_to_store_at_utc, @handledAtUtc),
                    updated_at_utc = CURRENT_TIMESTAMP(6)
                WHERE progressive_id = @denunciaId
                   OR imported_complaint_report_id = @denunciaId;
                """;

            await using (var updateInbox = new MySqlCommand(updateInboxSql, connection, (MySqlTransaction)transaction))
            {
                updateInbox.Parameters.AddWithValue("@handledAtUtc", handledAtUtc);
                updateInbox.Parameters.AddWithValue("@userId", userId);
                updateInbox.Parameters.AddWithValue("@denunciaId", denunciaId);
                await updateInbox.ExecuteNonQueryAsync(cancellationToken);
            }

            const string updateUserReportSql = """
                INSERT INTO user_inbox_reports (
                    app_user_id,
                    inbox_report_id,
                    first_seen_at_utc,
                    last_seen_at_utc,
                    first_downloaded_at_utc,
                    last_downloaded_at_utc,
                    download_count
                )
                SELECT
                    @userId,
                    ir.id,
                    CURRENT_TIMESTAMP(6),
                    CURRENT_TIMESTAMP(6),
                    @handledAtUtc,
                    @handledAtUtc,
                    1
                FROM inbox_reports ir
                WHERE ir.progressive_id = @denunciaId
                   OR ir.imported_complaint_report_id = @denunciaId
                ON DUPLICATE KEY UPDATE
                    last_seen_at_utc = CURRENT_TIMESTAMP(6),
                    first_downloaded_at_utc = COALESCE(first_downloaded_at_utc, VALUES(first_downloaded_at_utc)),
                    last_downloaded_at_utc =
                        CASE
                            WHEN last_downloaded_at_utc IS NULL THEN VALUES(last_downloaded_at_utc)
                            WHEN VALUES(last_downloaded_at_utc) > last_downloaded_at_utc THEN VALUES(last_downloaded_at_utc)
                            ELSE last_downloaded_at_utc
                        END;
                """;

            await using (var updateUserReport = new MySqlCommand(updateUserReportSql, connection, (MySqlTransaction)transaction))
            {
                updateUserReport.Parameters.AddWithValue("@userId", userId);
                updateUserReport.Parameters.AddWithValue("@handledAtUtc", handledAtUtc);
                updateUserReport.Parameters.AddWithValue("@denunciaId", denunciaId);
                await updateUserReport.ExecuteNonQueryAsync(cancellationToken);
            }

            await transaction.CommitAsync(cancellationToken);
        }
        catch
        {
            await SafeRollbackAsync(transaction, cancellationToken);
            throw;
        }
    }

    private async Task<long> EnsureUserAsync(MySqlConnection connection, string username, CancellationToken cancellationToken)
    {
        const string insertSql = """
            INSERT INTO app_users (username)
            VALUES (@username)
            ON DUPLICATE KEY UPDATE
                updated_at_utc = CURRENT_TIMESTAMP(6);
            """;

        await using (var insert = new MySqlCommand(insertSql, connection))
        {
            insert.Parameters.AddWithValue("@username", username.Trim());
            await insert.ExecuteNonQueryAsync(cancellationToken);
        }

        const string selectSql = """
            SELECT id
            FROM app_users
            WHERE username = @username
            LIMIT 1;
            """;

        await using var select = new MySqlCommand(selectSql, connection);
        select.Parameters.AddWithValue("@username", username.Trim());
        var result = await select.ExecuteScalarAsync(cancellationToken);
        return Convert.ToInt64(result, CultureInfo.InvariantCulture);
    }

    private static async Task UpsertInboxReportAsync(
        MySqlConnection connection,
        MySqlTransaction transaction,
        ReportDto report,
        CancellationToken cancellationToken)
    {
        const string sql = """
            INSERT INTO inbox_reports (
                global_report_uuid,
                progressive_id,
                context_id,
                context_name,
                creation_date_utc,
                update_date_utc,
                access_date_utc,
                last_access_utc,
                gl_status,
                gl_label,
                is_updated
            ) VALUES (
                @reportId,
                @progressiveId,
                @contextId,
                @contextName,
                @creationDateUtc,
                @updateDateUtc,
                @accessDateUtc,
                @lastAccessUtc,
                @glStatus,
                @glLabel,
                @isUpdated
            )
            ON DUPLICATE KEY UPDATE
                progressive_id = VALUES(progressive_id),
                context_id = VALUES(context_id),
                context_name = VALUES(context_name),
                creation_date_utc = VALUES(creation_date_utc),
                update_date_utc = VALUES(update_date_utc),
                access_date_utc = VALUES(access_date_utc),
                last_access_utc = VALUES(last_access_utc),
                gl_status = VALUES(gl_status),
                gl_label = VALUES(gl_label),
                is_updated = VALUES(is_updated),
                last_seen_at_utc = CURRENT_TIMESTAMP(6),
                updated_at_utc = CURRENT_TIMESTAMP(6);
            """;

        await using var command = new MySqlCommand(sql, connection, transaction);
        command.Parameters.AddWithValue("@reportId", report.Id);
        command.Parameters.AddWithValue("@progressiveId", report.Progressive.HasValue ? report.Progressive.Value : DBNull.Value);
        command.Parameters.AddWithValue("@contextId", ToDbString(report.ContextId));
        command.Parameters.AddWithValue("@contextName", ToDbString(report.ContextName));
        command.Parameters.AddWithValue("@creationDateUtc", ToDbDate(ParseDate(report.CreationDate)));
        command.Parameters.AddWithValue("@updateDateUtc", ToDbDate(ParseDate(report.UpdateDate)));
        command.Parameters.AddWithValue("@accessDateUtc", ToDbDate(ParseDate(report.AccessDate)));
        command.Parameters.AddWithValue("@lastAccessUtc", ToDbDate(ParseDate(report.LastAccess)));
        command.Parameters.AddWithValue("@glStatus", ToDbString(report.Status));
        command.Parameters.AddWithValue("@glLabel", ToDbString(report.Label));
        command.Parameters.AddWithValue("@isUpdated", report.Updated);
        await command.ExecuteNonQueryAsync(cancellationToken);
    }

    private static async Task UpsertUserSnapshotAsync(
        MySqlConnection connection,
        MySqlTransaction transaction,
        long userId,
        string reportId,
        CancellationToken cancellationToken)
    {
        const string sql = """
            INSERT INTO user_inbox_reports (
                app_user_id,
                inbox_report_id,
                first_seen_at_utc,
                last_seen_at_utc
            )
            SELECT
                @userId,
                ir.id,
                CURRENT_TIMESTAMP(6),
                CURRENT_TIMESTAMP(6)
            FROM inbox_reports ir
            WHERE ir.global_report_uuid = @reportId
            ON DUPLICATE KEY UPDATE
                last_seen_at_utc = CURRENT_TIMESTAMP(6);
            """;

        await using var command = new MySqlCommand(sql, connection, transaction);
        command.Parameters.AddWithValue("@userId", userId);
        command.Parameters.AddWithValue("@reportId", reportId);
        await command.ExecuteNonQueryAsync(cancellationToken);
    }

    private async Task<Dictionary<string, ReportMetadata>> LoadMetadataAsync(
        MySqlConnection connection,
        long userId,
        List<string> reportIds,
        CancellationToken cancellationToken)
    {
        await EnsureConnectionOpenAsync(connection, cancellationToken);

        var metadata = new Dictionary<string, ReportMetadata>(StringComparer.OrdinalIgnoreCase);
        if (reportIds.Count == 0)
        {
            return metadata;
        }

        await using var command = connection.CreateCommand();
        var parameterNames = new List<string>(reportIds.Count);
        for (var i = 0; i < reportIds.Count; i++)
        {
            var parameterName = $"@reportId{i}";
            parameterNames.Add(parameterName);
            command.Parameters.AddWithValue(parameterName, reportIds[i]);
        }

        command.Parameters.AddWithValue("@userId", userId);
        command.CommandText = $"""
            SELECT
                ir.global_report_uuid,
                ir.last_downloaded_at_utc,
                downloader.username AS last_downloaded_by_username,
                ir.imported_to_store_at_utc,
                COALESCE(c.is_in_gestiona, 0) AS already_in_gestiona,
                CASE WHEN uir.last_downloaded_at_utc IS NULL THEN 0 ELSE 1 END AS downloaded_by_current_user
            FROM inbox_reports ir
            LEFT JOIN app_users downloader ON downloader.id = ir.last_downloaded_by_user_id
            LEFT JOIN user_inbox_reports uir
                ON uir.inbox_report_id = ir.id
               AND uir.app_user_id = @userId
            LEFT JOIN complaints c
                ON c.external_report_id = COALESCE(ir.imported_complaint_report_id, ir.progressive_id)
            WHERE ir.global_report_uuid IN ({string.Join(", ", parameterNames)});
            """;

        await using var reader = await command.ExecuteReaderAsync(cancellationToken);
        while (await reader.ReadAsync(cancellationToken))
        {
            var reportId = GetStringValue(reader, "global_report_uuid");
            var lastDownloadedByUsername = reader.IsDBNull(reader.GetOrdinal("last_downloaded_by_username"))
                ? null
                : reader.GetString(reader.GetOrdinal("last_downloaded_by_username"));
            var downloadedByCurrentUser = reader.GetInt32(reader.GetOrdinal("downloaded_by_current_user")) == 1;
                var lockedByAnotherUser =
                    !downloadedByCurrentUser &&
                    !reader.IsDBNull(reader.GetOrdinal("imported_to_store_at_utc")) &&
                    !string.IsNullOrWhiteSpace(lastDownloadedByUsername);

                var downloadedByAnotherUser =
                    !downloadedByCurrentUser &&
                    !string.IsNullOrWhiteSpace(lastDownloadedByUsername);

            metadata[reportId] = new ReportMetadata
            {
                DownloadedByCurrentUser = downloadedByCurrentUser,
                DownloadedByAnotherUser = downloadedByAnotherUser,
                LastDownloadedByUsername = lastDownloadedByUsername,
                LastDownloadedAtUtc = GetDateTimeOffset(reader, "last_downloaded_at_utc"),
                AlreadyImported = !reader.IsDBNull(reader.GetOrdinal("imported_to_store_at_utc")),
                AlreadyInGestiona = reader.GetInt32(reader.GetOrdinal("already_in_gestiona")) == 1,
                LockedByAnotherUser = lockedByAnotherUser,
            };
        }

        return metadata;
    }

    private async Task<MySqlConnection> OpenConnectionAsync(CancellationToken cancellationToken)
    {
        var connectionString = await _connectionStringProvider.GetConnectionStringAsync(cancellationToken);
        var connection = new MySqlConnection(connectionString);
        await connection.OpenAsync(cancellationToken);
        await using var timeZoneCommand = new MySqlCommand("SET time_zone = '+00:00';", connection);
        await timeZoneCommand.ExecuteNonQueryAsync(cancellationToken);
        return connection;
    }

    private static async Task EnsureConnectionOpenAsync(
        MySqlConnection connection,
        CancellationToken cancellationToken)
    {
        if (connection.State == ConnectionState.Open)
        {
            return;
        }

        await connection.OpenAsync(cancellationToken);
        await using var timeZoneCommand = new MySqlCommand("SET time_zone = '+00:00';", connection);
        await timeZoneCommand.ExecuteNonQueryAsync(cancellationToken);
    }

    private async Task SafeRollbackAsync(
        MySqlTransaction transaction,
        CancellationToken cancellationToken)
    {
        try
        {
            await transaction.RollbackAsync(cancellationToken);
        }
        catch
        {
            // No dejamos que un rollback sobre una conexion ya cerrada oculte el error real.
        }
    }

    private static object ToDbString(string? value)
    {
        return string.IsNullOrWhiteSpace(value) ? DBNull.Value : value;
    }

    private static object ToDbDate(DateTimeOffset? value)
    {
        return value is null ? DBNull.Value : value.Value.UtcDateTime;
    }

    private static DateTimeOffset? ParseDate(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return DateTimeOffset.TryParse(value.Replace("Z", "+00:00", StringComparison.Ordinal), out var parsed)
            ? parsed
            : null;
    }

    private static DateTimeOffset? ResolveReportMoment(ReportDto report)
    {
        return ParseDate(report.UpdateDate) ?? ParseDate(report.CreationDate);
    }

    private static DateTimeOffset? GetDateTimeOffset(MySqlDataReader reader, string columnName)
    {
        var ordinal = reader.GetOrdinal(columnName);
        return reader.IsDBNull(ordinal)
            ? null
            : new DateTimeOffset(DateTime.SpecifyKind(reader.GetDateTime(ordinal), DateTimeKind.Utc));
    }

    private static string GetStringValue(MySqlDataReader reader, string columnName)
    {
        var ordinal = reader.GetOrdinal(columnName);
        if (reader.IsDBNull(ordinal))
        {
            return string.Empty;
        }

        var value = reader.GetValue(ordinal);
        return value switch
        {
            Guid guid => guid.ToString("D"),
            string text => text,
            _ => Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty
        };
    }

    private static string? BuildTrackingNote(ReportMetadata? metadata)
    {
        if (metadata is null)
        {
            return null;
        }

        if (metadata.LockedByAnotherUser)
        {
            return string.IsNullOrWhiteSpace(metadata.LastDownloadedByUsername)
                ? "Importada por otro usuario"
                : $"Importada por {metadata.LastDownloadedByUsername}";
        }

        if (metadata.AlreadyInGestiona)
        {
            return "Ya existe expediente en Gestiona";
        }

        if (metadata.DownloadedByAnotherUser)
        {
            if (!string.IsNullOrWhiteSpace(metadata.LastDownloadedByUsername) && metadata.LastDownloadedAtUtc is not null)
            {
                return $"La descargó {metadata.LastDownloadedByUsername} el {metadata.LastDownloadedAtUtc.Value.ToLocalTime():dd/MM/yyyy HH:mm}";
            }

            return "Ya la descargó otro usuario";
        }

        if (metadata.DownloadedByCurrentUser && metadata.LastDownloadedAtUtc is not null)
        {
            return $"Ya la descargaste el {metadata.LastDownloadedAtUtc.Value.ToLocalTime():dd/MM/yyyy HH:mm}";
        }

        if (metadata.AlreadyImported)
        {
            return "Ya está incorporada a la base de trabajo";
        }

        return null;
    }

    private sealed record ReportMetadata
    {
        public bool DownloadedByCurrentUser { get; init; }
        public bool DownloadedByAnotherUser { get; init; }
        public bool LockedByAnotherUser { get; init; }
        public string? LastDownloadedByUsername { get; init; }
        public DateTimeOffset? LastDownloadedAtUtc { get; init; }
        public bool AlreadyImported { get; init; }
        public bool AlreadyInGestiona { get; init; }
    }

    private static bool IsLockedByAnotherUser(ReportDto report)
        => report.AlreadyImported &&
           report.DownloadedByAnotherUser &&
           !report.DownloadedByCurrentUser;
}
