// Copyright (C) 2021-present sam (starshines.gay)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using Npgsql;

namespace Catalogger.Backend.Database;

public class DatabaseConnection(Guid id, ILogger logger, NpgsqlConnection inner)
    : DbConnection,
        IDisposable
{
    public Guid ConnectionId => id;
    public NpgsqlConnection Inner => inner;
    private readonly ILogger _logger = logger.ForContext<DatabaseConnection>();
    private readonly DateTimeOffset _openTime = DateTimeOffset.UtcNow;

    private bool _hasClosed;

    public override async Task OpenAsync(CancellationToken cancellationToken) =>
        await inner.OpenAsync(cancellationToken);

    public override async Task CloseAsync()
    {
        if (_hasClosed)
        {
            await inner.CloseAsync();
            return;
        }

        DatabasePool.DecrementConnections();
        var openFor = DateTimeOffset.UtcNow - _openTime;
        _logger.Verbose("Closing connection {ConnId}, open for {OpenFor}", ConnectionId, openFor);
        _hasClosed = true;
        await inner.CloseAsync();
    }

    protected override async ValueTask<DbTransaction> BeginDbTransactionAsync(
        IsolationLevel isolationLevel,
        CancellationToken cancellationToken
    )
    {
        _logger.Verbose("Beginning transaction on connection {ConnId}", ConnectionId);
        return await inner.BeginTransactionAsync(isolationLevel, cancellationToken);
    }

    public new void Dispose()
    {
        Close();
        inner.Dispose();
        GC.SuppressFinalize(this);
    }

    public override async ValueTask DisposeAsync()
    {
        await CloseAsync();
        await inner.DisposeAsync();
        GC.SuppressFinalize(this);
    }

    protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) =>
        inner.BeginTransaction(isolationLevel);

    public override void ChangeDatabase(string databaseName) => inner.ChangeDatabase(databaseName);

    public override void Close() => inner.Close();

    public override void Open() => inner.Open();

    [AllowNull]
    public override string ConnectionString
    {
        get => inner.ConnectionString;
        set => inner.ConnectionString = value;
    }

    public override string Database => inner.Database;
    public override ConnectionState State => inner.State;
    public override string DataSource => inner.DataSource;
    public override string ServerVersion => inner.ServerVersion;

    protected override DbCommand CreateDbCommand() => inner.CreateCommand();
}
