using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using Marten.Internal;
using Marten.Internal.Storage;
using Marten.Schema.Identity.Sequences;
using Marten.Services;
using Npgsql;
using Weasel.Core;
using Weasel.Postgresql;
using Weasel.Postgresql.Functions;
using Weasel.Postgresql.Tables;

#nullable enable
namespace Marten.Storage
{
    public interface ITenantStorage
    {
        /// <summary>
        ///     Directs Marten to disregard any previous schema checks. Useful
        ///     if you change the underlying schema without shutting down the document store
        /// </summary>
        void ResetSchemaExistenceChecks();

        void MarkAllFeaturesAsChecked();

        /// <summary>
        ///     Ensures that the IDocumentStorage object for a document type is ready
        ///     and also attempts to update the database schema for any detected changes
        /// </summary>
        /// <param name="documentType"></param>
        void EnsureStorageExists(Type documentType);

        /// <summary>
        ///     Ensures that the IDocumentStorage object for a document type is ready
        ///     and also attempts to update the database schema for any detected changes
        /// </summary>
        /// <param name="featureType"></param>
        /// <param name="???"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        Task EnsureStorageExistsAsync(Type featureType, CancellationToken token = default);

        /// <summary>
        /// Find the feature schema object for the designated storageType
        /// </summary>
        /// <param name="storageType"></param>
        /// <returns></returns>
        IFeatureSchema FindFeature(Type storageType);
    }

    public interface ITenant: ITenantStorage
    {
        string TenantId { get; }

        /// <summary>
        ///     Used to create new Hilo sequences
        /// </summary>
        ISequences Sequences { get; }


        IProviderGraph Providers { get; }

        /// <summary>
        ///     Retrieves or generates the active IDocumentStorage object
        ///     for the given document type
        /// </summary>
        /// <param name="documentType"></param>
        /// <returns></returns>
        IDocumentStorage<T> StorageFor<T>() where T : notnull;

        /// <summary>
        ///     Directly open a managed connection to the underlying Postgresql database
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="isolationLevel"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        IManagedConnection OpenConnection(CommandRunnerMode mode = CommandRunnerMode.AutoCommit,
            IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, int? timeout = null);

        /// <summary>
        ///     Set the minimum sequence number for a Hilo sequence for a specific document type
        ///     to the specified floor. Useful for migrating data between databases
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="floor"></param>
        Task ResetHiloSequenceFloor<T>(long floor);

        /// <summary>
        ///     Fetch a connection to the tenant database
        /// </summary>
        /// <returns></returns>
        NpgsqlConnection CreateConnection();


        /// <summary>
        ///     Fetches a list of all of the Marten generated tables
        ///     in the database
        /// </summary>
        /// <returns></returns>
        Task<IReadOnlyList<DbObjectName>> SchemaTables();

        /// <summary>
        ///     Fetches a list of the Marten document tables
        ///     in the database
        /// </summary>
        /// <returns></returns>
        Task<IReadOnlyList<DbObjectName>> DocumentTables();

        /// <summary>
        ///     Fetches a list of functions generated by Marten
        ///     in the database
        /// </summary>
        /// <returns></returns>
        Task<IReadOnlyList<DbObjectName>> Functions();

        /// <summary>
        ///     Query for the designated FunctionBody
        /// </summary>
        /// <param name="function"></param>
        /// <returns></returns>
        Task<Function> DefinitionForFunction(DbObjectName function);

        /// <summary>
        ///     Retrieve the existing Table for the document type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        Task<Table> ExistingTableFor(Type type);


    }
}
