internal static partial class Program
{
    static Task TestVaultCryptoRoundtripAsync()
    {
        var crypto = new VaultCryptoService();
        var plaintext = "sample vault payload";
        var payload = crypto.EncryptData(System.Text.Encoding.UTF8.GetBytes(plaintext), "vault-pass-123");

        Assert(payload.Length == VaultCryptoService.SaltSize + VaultCryptoService.NonceSize + VaultCryptoService.TagSize + plaintext.Length, "Vault payload length should match salt+nonce+tag+ciphertext layout.");
        var decrypted = crypto.DecryptData(payload, "vault-pass-123");
        Assert(System.Text.Encoding.UTF8.GetString(decrypted) == plaintext, "Vault roundtrip decrypt should return original plaintext.");

        return Task.CompletedTask;
    }

    static Task TestVaultCryptoDecryptsPythonFixtureAsync()
    {
        var crypto = new VaultCryptoService();

        var payload = Convert.FromBase64String("AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKC6AErhDEMERBl7OFkG4L4oZ2JZckS0VzhxaZoVLckF7VXE+NIYXILsJ8f1I=");
        var expectedPlaintext = Convert.FromBase64String("dmF1bHQgcGF5bG9hZCBleGFtcGxlCmxpbmUy");
        var decrypted = crypto.DecryptData(payload, "vault-pass-123");

        Assert(decrypted.SequenceEqual(expectedPlaintext), "C# decrypt should match Python-generated payload plaintext.");

        return Task.CompletedTask;
    }

    static Task TestVaultKeyDerivationMatchesPythonAsync()
    {
        var crypto = new VaultCryptoService();
        var salt = Enumerable.Range(0, VaultCryptoService.SaltSize).Select(i => (byte)i).ToArray();
        var key = crypto.DeriveKey("vault-pass-123", salt);
        var expectedKeyHex = "b29f523f28bf178f6815c6ca9ee2a588d79b3bd9a822c92a2f0dde5bc853bb52";
        var actualKeyHex = Convert.ToHexString(key).ToLowerInvariant();

        Assert(actualKeyHex == expectedKeyHex, "Derived key should match Python PBKDF2 fixture key.");

        return Task.CompletedTask;
    }

    static Task TestVaultMonthlyFilenameParityAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            var dbPath = dbService.GetDatabasePath();
            Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
            File.WriteAllText(dbPath, "db-bytes");

            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);

            var expectedName = $"_db_{Path.GetFileName(dbPath)}.vault";
            Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, expectedName)), "Expected DB vault snapshot filename with _db_ prefix and .vault suffix.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultLoadClearsAndExtractsAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            var dbPath = dbService.GetDatabasePath();
            Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
            var originalBytes = Guid.NewGuid().ToByteArray();
            File.WriteAllBytes(dbPath, originalBytes);

            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);

            File.WriteAllBytes(dbPath, [1, 2, 3, 4]);
            var ok = storage.LoadAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);

            Assert(ok, "Expected vault load success with correct password.");
            var loaded = File.ReadAllBytes(dbPath);
            Assert(loaded.SequenceEqual(originalBytes), "Expected DB file bytes restored from vault snapshot.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultLoadWrongPasswordPreservesVaultAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            var dbPath = dbService.GetDatabasePath();
            Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
            File.WriteAllText(dbPath, "db payload");

            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);
            var vaultPath = Path.Combine(config.Current.VaultDirectory, $"_db_{Path.GetFileName(dbPath)}.vault");
            var before = File.ReadAllBytes(vaultPath);

            var ok = storage.LoadAllVaults("wrong-password", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);
            var after = File.ReadAllBytes(vaultPath);

            Assert(!ok, "Expected vault load failure with wrong password.");
            Assert(before.SequenceEqual(after), "Vault file bytes should remain unchanged on wrong password.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultLoadLegacyInitVaultHandlingAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            var legacyPath = Path.Combine(config.Current.VaultDirectory, "_init_vault.vault");
            File.WriteAllBytes(legacyPath, [1, 2, 3, 4]);

            var ok = storage.LoadAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbService.GetDatabasePath())!);

            Assert(ok, "Legacy-only vault directory should still be treated as successful load state.");
            Assert(File.Exists(legacyPath), "Legacy _init_vault.vault should be ignored in SQLCipher snapshot mode.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultCurrentMonthSaveOptimizedAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            var dbPath = dbService.GetDatabasePath();
            Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
            File.WriteAllText(dbPath, "initial db");

            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);
            var vaultPath = Path.Combine(config.Current.VaultDirectory, $"_db_{Path.GetFileName(dbPath)}.vault");
            Assert(File.Exists(vaultPath), "Expected DB vault snapshot file to be created.");

            var firstLength = new FileInfo(vaultPath).Length;
            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, Path.GetDirectoryName(dbPath)!);
            var secondLength = new FileInfo(vaultPath).Length;

            Assert(firstLength > 0 && secondLength > 0, "Vault snapshot should remain non-empty across repeated rebuilds.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultRebuildAllVaultsAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            var dbDir = Path.GetDirectoryName(dbService.GetDatabasePath())!;
            Directory.CreateDirectory(dbDir);
            File.WriteAllText(Path.Combine(dbDir, "journal_cache.db"), "primary");
            File.WriteAllText(Path.Combine(dbDir, "analytics.db"), "secondary");

            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir);

            Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, "_db_journal_cache.db.vault")), "Expected primary DB snapshot vault.");
            Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, "_db_analytics.db.vault")), "Expected secondary DB snapshot vault.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultClearDataDirectoryAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        var scratchDir = Path.Combine(root, "scratch-data");
        Directory.CreateDirectory(scratchDir);
        Directory.CreateDirectory(Path.Combine(scratchDir, "nested"));

        try
        {
            File.WriteAllText(Path.Combine(scratchDir, "tmp.md"), "decrypted content");
            File.WriteAllText(Path.Combine(scratchDir, "nested", "tmp.txt"), "temp");

            storage.ClearDataDirectory(scratchDir);

            Assert(!Directory.Exists(scratchDir), "Non-db scratch directory should be deleted by clear_data_directory.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static async Task TestEntryVaultLoadAllEmptyAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-smoke", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(root);

        try
        {
            var entry = NewLockedEntry();
            var request = JsonSerializer.Serialize(new
            {
                action = "vault.load_all",
                payload = new
                {
                    password = "vault-pass-123",
                    vaultDirectory = Path.Combine(root, "vault")
                }
            });

            Directory.CreateDirectory(Path.Combine(root, "vault"));

            var response = await entry.HandleCommandAsync(request);
            using var doc = JsonDocument.Parse(response);

            Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for empty vault directory load.");
            Assert(doc.RootElement.GetProperty("data").GetBoolean(), "Expected vault.load_all data=true for empty vault directory.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }

    static async Task TestEntryVaultClearDataDirectoryAsync()
    {
        var entry = NewEntry();
        var request = JsonSerializer.Serialize(new
        {
            action = "vault.clear_data_directory",
            payload = new { }
        });

        var response = await entry.HandleCommandAsync(request);
        using var doc = JsonDocument.Parse(response);

        Assert(doc.RootElement.GetProperty("ok").GetBoolean(), "Expected ok=true for clear_data_directory.");
        Assert(doc.RootElement.GetProperty("data").GetBoolean(), "Expected clear_data_directory result=true.");
    }

    static Task TestSidecarVaultCliLoadAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);

        using var session = new DatabaseSessionService(dbService);
        session.SetPassword("vault-pass-123");

        var repo = new SqliteEntryFileRepository(session);
        var entryFiles = new EntryFileService(repo);
        var searchService = new EntrySearchService(repo);
        var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService(), dbService), searchService, config, entryFiles);

        var vaultDir = Path.Combine(root, "vault-cli");
        Directory.CreateDirectory(vaultDir);

        try
        {
            var exitCode = cli.RunVaultCommand(["load", "--password", "vault-pass-123", "--vault-dir", vaultDir]);
            Assert(exitCode == 0, "Expected vault load CLI command to succeed on empty vault directory.");

            var dbDir = Path.Combine(config.Current.VaultDirectory, "db");
            Assert(Directory.Exists(dbDir), "Expected db directory to be created by vault load CLI command.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestSidecarVaultCliSaveAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-sidecar-cli-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);

        using var session = new DatabaseSessionService(dbService);
        session.SetPassword("vault-pass-123");

        var repo = new SqliteEntryFileRepository(session);
        var entryFiles = new EntryFileService(repo);
        var searchService = new EntrySearchService(repo);
        var cli = new SidecarCli(new VaultStorageService(new VaultCryptoService(), dbService), searchService, config, entryFiles);

        var vaultDir = Path.Combine(root, "vault-cli");
        Directory.CreateDirectory(vaultDir);

        try
        {
            entryFiles.SaveEntry(new EntrySavePayload("entry body", Mode: "Overwrite", FileName: "2026-02-22"));
            session.CloseConnection();

            var exitCode = cli.RunVaultCommand(["save", "--password", "vault-pass-123", "--vault-dir", vaultDir]);

            Assert(exitCode == 0, "Expected vault save CLI command to succeed.");
            Assert(File.Exists(Path.Combine(vaultDir, "_db_journal_cache.db.vault")), "Expected DB vault snapshot file to be written by save CLI command.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestVaultCustomEntryRoundtripAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var storage = new VaultStorageService(new VaultCryptoService(), dbService);

        try
        {
            using (var seedSession = new DatabaseSessionService(dbService))
            {
                seedSession.SetPassword("vault-pass-123");
                var seedRepo = new SqliteEntryFileRepository(seedSession);
                var seedEntryFiles = new EntryFileService(seedRepo);

                seedEntryFiles.SaveEntry(new EntrySavePayload("date entry", Mode: "Overwrite", FileName: "2026-02-01"));
                seedEntryFiles.SaveEntry(new EntrySavePayload("custom entry body", Mode: "Overwrite", FileName: "My Custom Entry"));
                seedEntryFiles.SaveEntry(new EntrySavePayload("work notes body", Mode: "Overwrite", FileName: "Work Notes"));
                seedSession.CloseConnection();
            }

            var dbPath = dbService.GetDatabasePath();
            var dbDir = Path.GetDirectoryName(dbPath)!;

            storage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir);
            Assert(File.Exists(Path.Combine(config.Current.VaultDirectory, "_db_journal_cache.db.vault")), "Expected DB vault snapshot to be created.");

            File.Delete(dbPath);
            Assert(!File.Exists(dbPath), "DB file should be deleted before restore.");

            var ok = storage.LoadAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir);
            Assert(ok, "Expected vault load to succeed.");

            using (var verifySession = new DatabaseSessionService(dbService))
            {
                verifySession.SetPassword("vault-pass-123");
                var verifyRepo = new SqliteEntryFileRepository(verifySession);
                var verifyEntryFiles = new EntryFileService(verifyRepo);
                var allEntries = verifyEntryFiles.ListEntries();
                Assert(allEntries.Any(e => e.FileName == "2026-02-01.md"), "Date entry should be restored from vault DB snapshot.");
                Assert(allEntries.Any(e => e.FileName == "My Custom Entry.md"), "Custom entry should be restored from vault DB snapshot.");
                Assert(allEntries.Any(e => e.FileName == "Work Notes.md"), "Second custom entry should be restored from vault DB snapshot.");
            }
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }

        return Task.CompletedTask;
    }

    static async Task TestEntryVaultLoadWrongPasswordKeepsSessionLockedAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-vault-smoke", Guid.NewGuid().ToString("N"));
        var config = NewConfigService(root);
        var dbService = new JournalDatabaseService(config);
        var vaultStorage = new VaultStorageService(new VaultCryptoService(), dbService);
        var session = new DatabaseSessionService(dbService);
        var repo = new SqliteEntryFileRepository(session);
        var entryFiles = new EntryFileService(repo);
        var entrySearch = new EntrySearchService(repo);

        var entry = new Entry(
            NewService(),
            entrySearch,
            vaultStorage,
            dbService,
            session,
            config,
            new DisabledAiService("none"),
            new DisabledSpeechBridgeService("none"),
            new DisabledS2TService(),
            entryFiles,
            new ListService(new SqliteListRepository(session)),
            new TodoService(new SqliteTodoRepository(session)),
            new DisabledCoachService(),
            new ConversationService(new SqliteConversationRepository(session)),
            new CommandLogger());

        try
        {
            var dbDir = Path.GetDirectoryName(dbService.GetDatabasePath())!;
            Directory.CreateDirectory(dbDir);
            File.WriteAllBytes(dbService.GetDatabasePath(), Guid.NewGuid().ToByteArray());
            vaultStorage.RebuildAllVaults("vault-pass-123", config.Current.VaultDirectory, dbDir);

            var loadRequest = JsonSerializer.Serialize(new
            {
                action = "vault.load_all",
                payload = new
                {
                    password = "wrong-password",
                    vaultDirectory = config.Current.VaultDirectory
                }
            });
            var loadResponse = await entry.HandleCommandAsync(loadRequest);
            using var loadDoc = JsonDocument.Parse(loadResponse);
            Assert(loadDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected vault.load_all response envelope to be ok=true.");
            Assert(!loadDoc.RootElement.GetProperty("data").GetBoolean(), "Expected vault.load_all data=false with wrong password.");

            var listRequest = JsonSerializer.Serialize(new
            {
                action = "lists.list"
            });
            var listResponse = await entry.HandleCommandAsync(listRequest);
            using var listDoc = JsonDocument.Parse(listResponse);
            Assert(!listDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected lists.list to fail while locked.");
            var error = listDoc.RootElement.GetProperty("error").GetString() ?? "";
            Assert(error.Contains("database is locked", StringComparison.OrdinalIgnoreCase), "Expected locked-session error after failed vault.load_all.");
        }
        finally
        {
            if (Directory.Exists(root))
                Directory.Delete(root, recursive: true);
        }
    }

    static async Task TestEntryTemplateSaveAutoSyncsVaultAsync()
    {
        var root = Path.Combine(Path.GetTempPath(), "journal-entry-vault-sync-smoke", Guid.NewGuid().ToString("N"));
        var entry = NewEntry(root: root);

        var configResponse = await entry.HandleCommandAsync("""{"action":"config.get"}""");
        using var configDoc = JsonDocument.Parse(configResponse);
        var configData = configDoc.RootElement.GetProperty("data");
        var vaultDir = configData.GetProperty("VaultDirectory").GetString() ?? "";
        var dbFilename = configData.GetProperty("DatabaseFilename").GetString() ?? "journal_cache.db";

        var saveTemplateRequest = JsonSerializer.Serialize(new
        {
            action = "templates.save",
            payload = new
            {
                name = "Weekly Review",
                content = "## Wins\n- shipped feature"
            }
        });
        var saveTemplateResponse = await entry.HandleCommandAsync(saveTemplateRequest);
        using var saveTemplateDoc = JsonDocument.Parse(saveTemplateResponse);
        Assert(saveTemplateDoc.RootElement.GetProperty("ok").GetBoolean(), "Expected templates.save to succeed.");

        var dbVaultPath = Path.Combine(vaultDir, $"_db_{dbFilename}.vault");
        Assert(File.Exists(dbVaultPath), "Expected template save auto-sync to write DB vault snapshot.");

        if (Directory.Exists(root))
            Directory.Delete(root, recursive: true);
    }
}
