namespace Sonex.Services.ImageSync
{
using Sonex.DBWrapper;

internal static class Program
{
    public static async Task<int> Main(string[] args)
    {
        try
        {
            ImageSyncMode mode = ImageSyncModeExtensions.Parse(args);
            ImageSyncOptions options = ImageSyncOptions.Load();

            EnsurePasswords(options);
            await InitializeDatabaseAsync(options);

            using NetworkShareConnection? networkShare = NetworkShareConnection.ConnectIfNeeded(
                options.SourceRoot,
                options.SourceUserName,
                options.SourcePassword);

            ValidateOptions(options);

            Directory.CreateDirectory(options.DestinationRoot);

            using var cts = new CancellationTokenSource();

            Console.CancelKeyPress += (_, eventArgs) =>
            {
                eventArgs.Cancel = true;
                cts.Cancel();
                ConsoleHelpers.WriteWarning("Cancellation requested. Finishing current operation...");
            };

            using var engine = new ImageSyncEngine(options);

            ConsoleHelpers.WriteInfo($"Mode            : {mode}");
            ConsoleHelpers.WriteInfo($"Source root     : {options.SourceRoot}");
            ConsoleHelpers.WriteInfo($"Destination root: {options.DestinationRoot}");
            ConsoleHelpers.WriteInfo($"Sizes           : {string.Join(", ", options.Sizes.Select(x => x.FolderName))}");
            Console.WriteLine();

            if (mode == ImageSyncMode.ScanOnce)
            {
                await engine.RunFullScanAsync(cts.Token);
                return 0;
            }

            await engine.RunAsync(cts.Token);
            return 0;
        }
        catch (OperationCanceledException)
        {
            ConsoleHelpers.WriteWarning("Image sync has been stopped.");
            return 0;
        }
        catch (Exception ex)
        {
            ConsoleHelpers.WriteError("Critical error:");
            ConsoleHelpers.WriteError(ex.ToString());
            return 1;
        }
    }

    private static void EnsurePasswords(ImageSyncOptions options)
    {
        if (options.SourceRequiresCredentials &&
            string.IsNullOrWhiteSpace(options.SourcePassword))
        {
            Console.Write($"Enter password for network share user '{options.SourceUserName}': ");
            options.SourcePassword = ConsoleHelpers.ReadPassword();
            Console.WriteLine();
        }

        if (!string.IsNullOrWhiteSpace(options.DbUserName) &&
            string.IsNullOrWhiteSpace(options.DbPassword))
        {
            Console.Write($"Enter database password for '{options.DbUserName}': ");
            options.DbPassword = ConsoleHelpers.ReadPassword();
            Console.WriteLine();
        }
    }

    private static void ValidateOptions(ImageSyncOptions options)
    {
        if (string.IsNullOrWhiteSpace(options.SourceRoot))
            throw new InvalidOperationException("Config 'SourceRoot' cannot be empty.");

        if (string.IsNullOrWhiteSpace(options.DestinationRoot))
            throw new InvalidOperationException("Config 'DestinationRoot' cannot be empty.");

        if (string.IsNullOrWhiteSpace(options.DbHost))
            throw new InvalidOperationException("Config 'DB_HOST' cannot be empty.");

        if (string.IsNullOrWhiteSpace(options.DbDatabase))
            throw new InvalidOperationException("Config 'DB_DATA_BASE' cannot be empty.");

        if (string.IsNullOrWhiteSpace(options.DbUserName))
            throw new InvalidOperationException("Config 'DB_USER_NAME' cannot be empty.");

        if (!Directory.Exists(options.SourceRoot))
            throw new DirectoryNotFoundException($"Source directory was not found or is not accessible: {options.SourceRoot}");
    }

    private static async Task InitializeDatabaseAsync(ImageSyncOptions options)
    {
        DB.InitPostgreSQL(
            host: options.DbHost,
            port: options.DbPort,
            database: options.DbDatabase,
            username: options.DbUserName,
            password: options.DbPassword,
            applicationName: "Sonex.Services.ImageSync",
            timeout: options.DbTimeout,
            commandTimeout: options.DbCommandTimeout,
            pooling: options.DbPooling,
            minPoolSize: options.DbMinPoolSize,
            maxPoolSize: options.DbMaxPoolSize,
            includeErrorDetail: options.DbIncludeErrorDetail);

        await ExecuteSqlScriptAsync(Path.Combine(AppContext.BaseDirectory, "CreateProductsImages.sql"));

        foreach (string migrationPath in Directory
                     .EnumerateFiles(AppContext.BaseDirectory, "Migrate*.sql")
                     .OrderBy(Path.GetFileName, StringComparer.OrdinalIgnoreCase))
        {
            await ExecuteSqlScriptAsync(migrationPath);
        }
    }

    private static async Task ExecuteSqlScriptAsync(string path)
    {
        if (!File.Exists(path))
            return;

        string sql = await File.ReadAllTextAsync(path);
        if (string.IsNullOrWhiteSpace(sql))
            return;

        var result = await DB.ExecuteAsync(sql);
        if (result.Success)
            return;

        throw new InvalidOperationException(
            $"Cannot execute database script '{Path.GetFileName(path)}': {result.ErrorMessage}");
    }
}
}
