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

namespace ApiDenuncias.Services;

public sealed class GestionaExpedienteExceptionStore
{
    private readonly MySqlConnectionStringProvider _connectionStringProvider;

    public GestionaExpedienteExceptionStore(MySqlConnectionStringProvider connectionStringProvider)
    {
        _connectionStringProvider = connectionStringProvider;
    }

    public async Task EnsureSchemaAsync(CancellationToken cancellationToken = default)
    {
        await using var connection = await OpenConnectionAsync(cancellationToken);
        await EnsureTableAsync(connection, cancellationToken);
    }

    public async Task<GestionaExpedienteException?> FindAsync(
        DenunciasGestiona denuncia,
        CancellationToken cancellationToken = default)
    {
        await using var connection = await OpenConnectionAsync(cancellationToken);
        await EnsureTableAsync(connection, cancellationToken);

        var references = BuildReferences(denuncia);
        await using var command = connection.CreateCommand();
        command.Parameters.AddWithValue("@externalReportId", denuncia.Id_Denuncia);

        var referenceFilter = string.Empty;
        if (references.Count > 0)
        {
            var parameterNames = new List<string>(references.Count);
            for (var i = 0; i < references.Count; i++)
            {
                var parameterName = $"@reference{i}";
                parameterNames.Add(parameterName);
                command.Parameters.AddWithValue(parameterName, references[i]);
            }

            referenceFilter = $" OR external_reference IN ({string.Join(", ", parameterNames)})";
        }

        command.CommandText = $"""
            SELECT external_report_id, external_reference, gestiona_file_code
            FROM gestiona_file_exceptions
            WHERE external_report_id = @externalReportId
                  {referenceFilter}
            ORDER BY
                CASE WHEN external_report_id = @externalReportId THEN 0 ELSE 1 END,
                source_row DESC,
                id DESC
            LIMIT 1;
            """;

        await using var reader = await command.ExecuteReaderAsync(cancellationToken);
        if (!await reader.ReadAsync(cancellationToken))
        {
            return null;
        }

        return new GestionaExpedienteException(
            reader.IsDBNull(reader.GetOrdinal("external_report_id"))
                ? null
                : reader.GetInt32(reader.GetOrdinal("external_report_id")),
            reader.IsDBNull(reader.GetOrdinal("external_reference"))
                ? null
                : reader.GetString(reader.GetOrdinal("external_reference")),
            reader.GetString(reader.GetOrdinal("gestiona_file_code")));
    }

    private static List<string> BuildReferences(DenunciasGestiona denuncia)
    {
        var references = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        if (denuncia.Id_RegistroDenuncia > 0)
        {
            references.Add(denuncia.Id_RegistroDenuncia.ToString(CultureInfo.InvariantCulture));
        }

        AddReference(references, denuncia.Etiqueta);
        AddReference(references, denuncia.Asunto);

        return references.ToList();
    }

    private static void AddReference(HashSet<string> references, string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return;
        }

        references.Add(value.Trim());
    }

    private async Task<MySqlConnection> OpenConnectionAsync(CancellationToken cancellationToken)
    {
        var connectionString = await _connectionStringProvider.GetConnectionStringAsync(cancellationToken);
        var connection = new MySqlConnection(connectionString);
        await connection.OpenAsync(cancellationToken);
        return connection;
    }

    private static async Task EnsureTableAsync(MySqlConnection connection, CancellationToken cancellationToken)
    {
        await using var command = new MySqlCommand(
            """
            CREATE TABLE IF NOT EXISTS gestiona_file_exceptions (
                id INT AUTO_INCREMENT PRIMARY KEY,
                external_report_id INT NULL,
                external_reference VARCHAR(100) NOT NULL,
                gestiona_file_code VARCHAR(50) NOT NULL,
                notes VARCHAR(255) NULL,
                source_row INT NULL,
                created_at_utc DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
                updated_at_utc DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
                UNIQUE KEY uq_gestiona_file_exceptions_ref_code (external_reference, gestiona_file_code),
                KEY ix_gestiona_file_exceptions_report (external_report_id),
                KEY ix_gestiona_file_exceptions_reference (external_reference),
                KEY ix_gestiona_file_exceptions_code (gestiona_file_code)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
            """,
            connection);
        await command.ExecuteNonQueryAsync(cancellationToken);
    }
}

public sealed record GestionaExpedienteException(
    int? ExternalReportId,
    string? ExternalReference,
    string GestionaFileCode);
