using Microsoft.Data.SqlClient;
using Microsoft.SqlServer.Management.Smo;
using DBMigration.Models;
using System.Threading.Tasks;

namespace DBMigration.Services
{
    public class DatabaseService
    {
        public async IAsyncEnumerable<DatabaseObject> GetTables(ConnectionInfo connection)
        {
            await foreach (var obj in GetDatabaseObjectsAsync(connection, "TABLE"))
            {
                yield return obj;
            }
        }

        public async IAsyncEnumerable<DatabaseObject> GetViews(ConnectionInfo connection)
        {
            await foreach (var obj in GetDatabaseObjectsAsync(connection, "VIEW"))
            {
                yield return obj;
            }
        }

        public async IAsyncEnumerable<DatabaseObject> GetProcedures(ConnectionInfo connection)
        {
            await foreach (var obj in GetDatabaseObjectsAsync(connection, "PROCEDURE"))
            {
                yield return obj;
            }
        }

        private async IAsyncEnumerable<DatabaseObject> GetDatabaseObjectsAsync(ConnectionInfo connection, string objectType)
        {
            using (var conn = new SqlConnection(connection.GetConnectionString()))
            {
                await conn.OpenAsync();
                var server = new Server(new Microsoft.SqlServer.Management.Common.ServerConnection(conn));
                var database = server.Databases[connection.DatabaseName];

                switch (objectType)
                {
                    case "TABLE":
                        foreach (Table table in database.Tables)
                        {
                            if (table.IsSystemObject || table.Name.StartsWith("_")) continue;
                            yield return CreateDatabaseObject(table);
                            await Task.Yield();
                        }
                        break;

                    case "VIEW":
                        foreach (Microsoft.SqlServer.Management.Smo.View view in database.Views)
                        {
                            if (view.IsSystemObject) continue;
                            yield return CreateDatabaseObject(view);
                            await Task.Yield();
                        }
                        break;

                    case "PROCEDURE":
                        foreach (StoredProcedure sp in database.StoredProcedures)
                        {
                            if (sp.IsSystemObject) continue;
                            yield return CreateDatabaseObject(sp);
                            await Task.Yield();
                        }
                        break;
                }
            }
        }

        private DatabaseObject CreateDatabaseObject(Table table)
        {
            return new DatabaseObject
            {
                Name = table.Name,
                Schema = table.Schema,
                Type = "TABLE",
                Definition = GetTableDefinition(table)
            };
        }

        private DatabaseObject CreateDatabaseObject(Microsoft.SqlServer.Management.Smo.View view)
        {
            return new DatabaseObject
            {
                Name = view.Name,
                Schema = view.Schema,
                Type = "VIEW",
                Definition = view.TextBody
            };
        }

        private DatabaseObject CreateDatabaseObject(StoredProcedure sp)
        {
            return new DatabaseObject
            {
                Name = sp.Name,
                Schema = sp.Schema,
                Type = "PROCEDURE",
                Definition = sp.TextBody
            };
        }

        private string GetTableDefinition(Table table)
        {
            var options = new ScriptingOptions
            {
                IncludeIfNotExists = true,
                ScriptDrops = false,
                WithDependencies = true,
                Indexes = true,
                Triggers = true,
                ClusteredIndexes = true,
                NonClusteredIndexes = true
            };

            return table.Script(options).ToString();
        }
    }
}
