import { beforeEach, describe, expect, it, vi } from 'vitest';
// `cmsApi` is the only dependency the service uses; we mock it so the
// tests run hermetically without an HTTP server. Each mocked response
// follows the SDK's `{ data, error }` envelope.
const cmsApiMock = vi.fn();
vi.mock('../../lib/api.js', () => ({
    cmsApi: (...args) => cmsApiMock(...args),
}));
const { fetchPosts, fetchPostTypes } = await import('../../lib/posts-service.js');
const COLLECTIONS_RESPONSE = [
    {
        slug: 'blog',
        labels: { singular: 'Blog Post', plural: 'Blog' },
        type: 'post',
        urlPrefix: 'blog',
        description: 'Blog posts',
        icon: 'file-text',
        color: 'purple',
        group: 'Posts',
        hidden: false,
        fieldCount: 12,
    },
    {
        slug: 'pages',
        labels: { singular: 'Page', plural: 'Pages' },
        type: 'page',
        urlPrefix: '',
        description: null,
        icon: null,
        color: null,
        group: null,
        hidden: false,
        fieldCount: 5,
    },
];
// Canned response from the server-side `GET /posts` aggregate. The server now
// owns filtering/sorting/pagination, so the client tests assert the mapping
// from this envelope onto the Post / PostType / PostAuthor shapes and that the
// query params are forwarded.
const POSTS_RESPONSE = {
    posts: [
        {
            id: 'n1',
            collection: 'news',
            title: 'Q2 2026 Product Roadmap Update',
            slug: 'q2-roadmap',
            status: 'SCHEDULED',
            publishedAt: null,
            scheduledAt: '2026-06-01T00:00:00.000Z',
            createdAt: '2026-05-10T00:00:00.000Z',
            updatedAt: '2026-05-24T00:00:00.000Z',
            seoScore: 62,
            author: { id: 'u-sarah', name: 'Sarah Morgan' },
        },
        {
            id: 'b1',
            collection: 'blog',
            title: 'The Future of AI Content Marketing',
            slug: 'ai-content-marketing',
            status: 'PUBLISHED',
            publishedAt: '2026-05-10T00:00:00.000Z',
            scheduledAt: null,
            createdAt: '2026-05-10T00:00:00.000Z',
            updatedAt: '2026-05-18T00:00:00.000Z',
            seoScore: 88,
            author: { id: 'u-maria', name: 'Maria K' },
        },
        {
            id: 'b3',
            collection: 'blog',
            title: 'Mid-Year Review',
            slug: 'mid-year-cms-trends',
            status: 'DRAFT',
            publishedAt: null,
            scheduledAt: null,
            createdAt: '2026-05-10T00:00:00.000Z',
            updatedAt: '2026-05-22T00:00:00.000Z',
            seoScore: null,
            author: null,
        },
    ],
    total: 5,
    page: 1,
    pageSize: 10,
    types: [
        {
            slug: 'blog',
            singular: 'Blog Post',
            plural: 'Blog',
            description: 'Blog posts',
            urlPrefix: 'blog',
            color: 'purple',
            icon: 'file-text',
            fieldCount: 12,
            count: 3,
        },
        {
            slug: 'news',
            singular: 'News Item',
            plural: 'News',
            description: 'News',
            urlPrefix: 'news',
            color: 'orange',
            icon: 'newspaper',
            fieldCount: 11,
            count: 1,
        },
    ],
    authors: [
        { id: 'u-sarah', name: 'Sarah Morgan' },
        { id: 'u-maria', name: 'Maria K' },
    ],
};
/** Records the last `/posts` URL the service requested. */
let lastPostsUrl = '';
function installDefaultMocks() {
    lastPostsUrl = '';
    cmsApiMock.mockImplementation((url) => {
        if (url === '/collections')
            return { data: COLLECTIONS_RESPONSE };
        if (url.startsWith('/posts')) {
            lastPostsUrl = url;
            return { data: POSTS_RESPONSE };
        }
        return { error: `unexpected url: ${url}` };
    });
}
describe('posts-service.fetchPostTypes', () => {
    beforeEach(() => {
        cmsApiMock.mockReset();
        installDefaultMocks();
    });
    it('returns only post-typed collections (filters out type: page)', async () => {
        const types = await fetchPostTypes();
        expect(types.map((t) => t.slug).sort()).toEqual(['blog']);
    });
    it('maps collection metadata onto the PostType shape', async () => {
        const types = await fetchPostTypes();
        const blog = types.find((t) => t.slug === 'blog');
        expect(blog).toMatchObject({
            id: 'blog',
            slug: 'blog',
            name: 'Blog',
            singularName: 'Blog Post',
            description: 'Blog posts',
            urlBase: '/blog',
            accentColor: 'purple',
            icon: 'file-text',
            fieldCount: 12,
            active: true,
        });
        // postCount is 0 here because fetchPostTypes doesn't load docs.
        expect(blog?.postCount).toBe(0);
    });
});
describe('posts-service.fetchPosts', () => {
    beforeEach(() => {
        cmsApiMock.mockReset();
        installDefaultMocks();
    });
    it('maps the server posts onto the Post shape', async () => {
        const res = await fetchPosts({ pageSize: 10 });
        expect(res.total).toBe(5);
        expect(res.posts.map((p) => p.id)).toEqual(['n1', 'b1', 'b3']);
        const blogPost = res.posts.find((p) => p.id === 'b1');
        expect(blogPost).toMatchObject({
            title: 'The Future of AI Content Marketing',
            postTypeId: 'blog',
            postTypeName: 'Blog Post',
            postTypeAccentColor: 'purple',
            authorId: 'u-maria',
            authorName: 'Maria K',
            authorInitials: 'MK',
            status: 'PUBLISHED',
            seoScore: 88,
        });
    });
    it('surfaces the scheduled date for SCHEDULED posts as the display date', async () => {
        const res = await fetchPosts({});
        const news = res.posts.find((p) => p.id === 'n1');
        expect(news?.publishDate).toBe('2026-06-01T00:00:00.000Z');
        expect(news?.updatedDate).toBe('2026-06-01T00:00:00.000Z');
    });
    it('falls back gracefully when a post has no author', async () => {
        const res = await fetchPosts({});
        const orphan = res.posts.find((p) => p.id === 'b3');
        expect(orphan?.authorName).toBe('Unknown');
        expect(orphan?.authorInitials).toBe('??');
        expect(orphan?.authorId).toBe('');
    });
    it('maps the dataset-wide tab counts from the endpoint', async () => {
        const res = await fetchPosts({});
        expect(res.types.find((t) => t.slug === 'blog')?.postCount).toBe(3);
        expect(res.types.find((t) => t.slug === 'news')?.postCount).toBe(1);
    });
    it('returns the contributor list sorted by name', async () => {
        const res = await fetchPosts({});
        expect(res.authors.map((a) => a.name)).toEqual(['Maria K', 'Sarah Morgan']);
    });
    it('forwards filter / sort / pagination params to the endpoint', async () => {
        await fetchPosts({
            search: 'roadmap',
            postType: 'news',
            status: 'SCHEDULED',
            author: 'u-sarah',
            sortBy: 'title',
            sortDirection: 'asc',
            page: 2,
            pageSize: 25,
        });
        const params = new URLSearchParams(lastPostsUrl.split('?')[1] ?? '');
        expect(params.get('search')).toBe('roadmap');
        expect(params.get('postType')).toBe('news');
        expect(params.get('status')).toBe('SCHEDULED');
        expect(params.get('author')).toBe('u-sarah');
        expect(params.get('sortBy')).toBe('title');
        expect(params.get('sortDirection')).toBe('asc');
        expect(params.get('page')).toBe('2');
        expect(params.get('pageSize')).toBe('25');
    });
    it('omits "all" filters from the query string', async () => {
        await fetchPosts({ postType: 'all', status: 'all', author: 'all' });
        const params = new URLSearchParams(lastPostsUrl.split('?')[1] ?? '');
        expect(params.has('postType')).toBe(false);
        expect(params.has('status')).toBe(false);
        expect(params.has('author')).toBe(false);
    });
    it('throws when the endpoint returns an error', async () => {
        cmsApiMock.mockReset();
        cmsApiMock.mockImplementation(() => ({ error: 'boom' }));
        await expect(fetchPosts({})).rejects.toThrow('boom');
    });
});
//# sourceMappingURL=posts-service.test.js.map