internal static partial class Program
{
    static Task TestCreateTrimsAsync()
    {
        var service = NewService();
        var created = service.Create(new CreateFragmentDto("  !TRIGGER  ", "  stomach drop  ", ["  stress  ", "", "  body  "]));

        Assert(created.Type == "!TRIGGER", "Type should be trimmed.");
        Assert(created.Description == "stomach drop", "Description should be trimmed.");
        Assert(created.Tags.Count == 2, "Expected two normalized tags.");
        Assert(created.Tags[0] == "stress" && created.Tags[1] == "body", "Tags should be trimmed and preserved.");
        return Task.CompletedTask;
    }

    static Task TestUpdateAcceptsTypeAsync()
    {
        var service = NewService();
        var created = service.Create(new CreateFragmentDto("!TRIGGER", "one"));
        var ok = service.Update(created.Id, new UpdateFragmentDto(Type: "  !FLASHBACK  ", Description: "  two  ", Tags: ["  memory  "]));

        Assert(ok, "Expected update to succeed.");
        var updated = service.GetById(created.Id);
        Assert(updated is not null, "Updated fragment should exist.");
        Assert(updated!.Type == "!FLASHBACK", "Updated type should be trimmed and stored.");
        Assert(updated.Description == "two", "Updated description should be trimmed and stored.");
        Assert(updated.Tags.Count == 1 && updated.Tags[0] == "memory", "Updated tags should be normalized.");
        return Task.CompletedTask;
    }

    static Task TestUpdateRejectsWhitespaceTypeAsync()
    {
        var service = NewService();
        var created = service.Create(new CreateFragmentDto("!TRIGGER", "desc"));

        try
        {
            _ = service.Update(created.Id, new UpdateFragmentDto(Type: "   "));
        }
        catch (ValidationException)
        {
            return Task.CompletedTask;
        }

        throw new InvalidOperationException("Expected ValidationException for whitespace type update.");
    }

    static Task TestFileRepositoryPersistsAsync()
    {
        var tempRoot = Path.Combine(Path.GetTempPath(), "journal-smoke", Guid.NewGuid().ToString("N"));
        var dataDir = Path.Combine(tempRoot, "data");
        Directory.CreateDirectory(dataDir);
        const string password = "smoke-test-password";

        try
        {
            var configService = NewConfigService(tempRoot);
            var dbService = new JournalDatabaseService(configService);

            // First session: create a fragment
            using var session1 = new DatabaseSessionService(dbService);
            session1.SetPassword(password);
            var repo1 = new SqliteFragmentRepository(session1);
            var service1 = new FragmentService(repo1);
            var created = service1.Create(new CreateFragmentDto("!TRIGGER", "persist me", ["tag1"]));

            // Second session: verify persistence
            using var session2 = new DatabaseSessionService(dbService);
            session2.SetPassword(password);
            var repo2 = new SqliteFragmentRepository(session2);
            var service2 = new FragmentService(repo2);
            var loaded = service2.GetById(created.Id);

            Assert(loaded is not null, "Expected fragment to persist across repository instances.");
            Assert(loaded!.Description == "persist me", "Persisted fragment description mismatch.");
            Assert(loaded.Tags.Count == 1 && loaded.Tags[0] == "tag1", "Persisted tags mismatch.");
        }
        finally
        {
            if (Directory.Exists(tempRoot))
                Directory.Delete(tempRoot, recursive: true);
        }

        return Task.CompletedTask;
    }

    static Task TestJournalEntryModelAsync()
    {
        var fragment = new Fragment("!TRIGGER", "test fragment");
        var section = new ParsedSection(
            "Summary",
            content: ["line one", "- [x] completed thing"],
            checkboxes: new Dictionary<string, bool> { ["completed thing"] = true });

        var entry = new JournalEntry(
            date: "2026-02-22",
            fragments: [fragment],
            rawContent: "raw markdown content",
            sections: new Dictionary<string, ParsedSection> { ["Summary"] = section });

        Assert(entry.Date == "2026-02-22", "JournalEntry date mismatch.");
        Assert(entry.RawContent == "raw markdown content", "JournalEntry raw content mismatch.");
        Assert(entry.Fragments.Count == 1, "JournalEntry fragment count mismatch.");
        Assert(entry.Sections.Count == 1, "JournalEntry section count mismatch.");
        Assert(entry.GetSection("Summary").Contains("line one"), "JournalEntry section content mismatch.");
        Assert(entry.GetCheckboxState("Summary", "completed thing") is true, "JournalEntry checkbox state mismatch.");
        Assert(entry.GetCheckboxState("Summary", "missing") is null, "JournalEntry checkbox should return null when missing.");

        return Task.CompletedTask;
    }

    static Task TestMergeOverwritesMeaningfulSectionAsync()
    {
        var current = new JournalEntry(
            date: "2026-02-22",
            sections: new Dictionary<string, ParsedSection>
            {
                ["Summary"] = new ParsedSection("Summary", ["old content"])
            });

        var incoming = new JournalEntry(
            date: "2026-02-22",
            sections: new Dictionary<string, ParsedSection>
            {
                ["Summary"] = new ParsedSection(
                    "Summary",
                    ["   ", "new content line"],
                    new Dictionary<string, bool> { ["new check"] = true }),
                ["Reflection"] = new ParsedSection("Reflection", ["reflective note"])
            });

        current.MergeWith(incoming);

        Assert(current.GetSection("Summary").Contains("new content line"), "Meaningful section update should overwrite existing section.");
        Assert(!current.GetSection("Summary").Contains("old content"), "Old section content should be replaced.");
        Assert(current.GetCheckboxState("Summary", "new check") is true, "Overwritten section checkbox state should come from incoming section.");
        Assert(current.GetSection("Reflection").Contains("reflective note"), "Meaningful new section should be added.");

        return Task.CompletedTask;
    }

    static Task TestMergeIgnoresWhitespaceOnlySectionAsync()
    {
        var current = new JournalEntry(
            date: "2026-02-22",
            sections: new Dictionary<string, ParsedSection>
            {
                ["Summary"] = new ParsedSection("Summary", ["keep existing"])
            });

        var incoming = new JournalEntry(
            date: "2026-02-22",
            sections: new Dictionary<string, ParsedSection>
            {
                ["Summary"] = new ParsedSection("Summary", ["   ", "\t", ""])
            });

        current.MergeWith(incoming);

        Assert(current.GetSection("Summary").Contains("keep existing"), "Whitespace-only section update should be ignored.");

        return Task.CompletedTask;
    }

    static Task TestMergeAppendsNonDuplicateFragmentsAsync()
    {
        var current = new JournalEntry(
            date: "2026-02-22",
            fragments:
            [
                new Fragment("!TRIGGER", "duplicate description")
            ]);

        var incoming = new JournalEntry(
            date: "2026-02-22",
            fragments:
            [
                new Fragment("!NOTE", "duplicate description"),
            new Fragment("!NOTE", "new description")
            ]);

        current.MergeWith(incoming);

        Assert(current.Fragments.Count == 2, "Expected only one new fragment to be appended.");
        Assert(current.Fragments.Count(fragment => fragment.Description == "duplicate description") == 1, "Duplicate description should not be appended.");
        Assert(current.Fragments.Any(fragment => fragment.Description == "new description"), "New fragment description should be appended.");

        return Task.CompletedTask;
    }

    static Task TestToMarkdownCanonicalSectionOrderAsync()
    {
        var entry = new JournalEntry(
            date: "2026-02-22",
            sections: new Dictionary<string, ParsedSection>
            {
                ["Reflection"] = new ParsedSection("Reflection", ["reflection body"]),
                ["Summary"] = new ParsedSection("Summary", ["summary body"])
            });

        var markdown = entry.ToMarkdown();
        var summaryIdx = markdown.IndexOf("## Summary", StringComparison.Ordinal);
        var reflectionIdx = markdown.IndexOf("## Reflection", StringComparison.Ordinal);

        Assert(summaryIdx >= 0, "Summary header should be emitted.");
        Assert(reflectionIdx >= 0, "Reflection header should be emitted.");
        Assert(summaryIdx < reflectionIdx, "Sections should be emitted in canonical order.");

        return Task.CompletedTask;
    }

    static Task TestToMarkdownFragmentFormattingAsync()
    {
        var fragment = new Fragment("!TRIGGER", "fragment body")
        {
            Time = default,
            Tags = ["stress", "body"]
        };
        var entry = new JournalEntry(
            date: "2026-02-22",
            fragments: [fragment]);

        var markdown = entry.ToMarkdown();

        Assert(markdown.Contains("# Fragments\n", StringComparison.Ordinal), "Fragments header should be present.");
        Assert(markdown.Contains("!TRIGGER  #stress #body\nfragment body\n", StringComparison.Ordinal), "Fragment block format should match parity shape.");
        Assert(markdown.Contains("**Date:** 2026-02-22", StringComparison.Ordinal), "Date frontmatter line should be present.");

        return Task.CompletedTask;
    }
}

