const { describe, test, expect, beforeEach, afterEach } = require('bun:test')
const crypto = require('crypto')
const path = require('path')

describe('Blog System', () => {
  describe('Post Management', () => {
    const blogPosts = []

    function createPost(data) {
      const now = new Date().toISOString()
      return {
        id: crypto.randomUUID(),
        title: data.title || 'Untitled',
        slug: data.slug || data.title?.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 100) || crypto.randomBytes(4).toString('hex'),
        content: data.content || '',
        excerpt: data.excerpt || data.content?.slice(0, 200) || '',
        author: data.author || 'Admin',
        category: data.category || 'general',
        tags: data.tags || [],
        status: data.status || 'draft',
        publishedAt: data.status === 'published' ? now : null,
        createdAt: now,
        updatedAt: now,
        featuredImage: data.featuredImage || null,
        metaTitle: data.metaTitle || data.title?.slice(0, 60) || '',
        metaDescription: data.metaDescription || data.excerpt?.slice(0, 160) || '',
      }
    }

    function validatePost(post) {
      const errors = []
      if (!post.title || post.title.trim().length === 0) errors.push('Title is required')
      if (!post.content || post.content.trim().length === 0) errors.push('Content is required')
      if (post.title && post.title.length > 200) errors.push('Title must be 200 characters or less')
      if (post.metaTitle && post.metaTitle.length > 60) errors.push('Meta title must be 60 characters or less')
      if (post.metaDescription && post.metaDescription.length > 160) errors.push('Meta description must be 160 characters or less')
      return { valid: errors.length === 0, errors }
    }

    beforeEach(() => {
      blogPosts.length = 0
    })

    test('creates a blog post with defaults', () => {
      const post = createPost({ title: 'Test Post' })

      expect(post.id).toBeDefined()
      expect(post.title).toBe('Test Post')
      expect(post.status).toBe('draft')
      expect(post.category).toBe('general')
    })

    test('creates a published post', () => {
      const post = createPost({ title: 'Published Post', status: 'published' })

      expect(post.status).toBe('published')
      expect(post.publishedAt).toBeDefined()
    })

    test('generates slug from title', () => {
      const post = createPost({ title: 'Hello World Test!' })

      expect(post.slug).toBe('hello-world-test-')
    })

    test('validates required fields', () => {
      const post = createPost({})
      const result = validatePost(post)

      expect(result.valid).toBe(false)
      expect(result.errors).toContain('Content is required')
    })

    test('validates title length', () => {
      const post = createPost({ title: 'a'.repeat(201), content: 'test' })
      const result = validatePost(post)

      expect(result.errors).toContain('Title must be 200 characters or less')
    })

    test('validates meta title length', () => {
      const post = createPost({
        title: 'Test',
        content: 'test',
        metaTitle: 'a'.repeat(61),
      })
      const result = validatePost(post)

      expect(result.errors).toContain('Meta title must be 60 characters or less')
    })

    test('generates excerpt from content', () => {
      const post = createPost({
        title: 'Test',
        content: 'This is a long content that should be truncated for the excerpt.',
      })

      expect(post.excerpt.length).toBeLessThanOrEqual(200)
    })
  })

  describe('Category Management', () => {
    const defaultCategories = [
      { id: 'general', name: 'General', slug: 'general' },
      { id: 'updates', name: 'Updates', slug: 'updates' },
      { id: 'tutorials', name: 'Tutorials', slug: 'tutorials' },
      { id: 'wordpress', name: 'WordPress', slug: 'wordpress' },
    ]

    function createCategory(data) {
      return {
        id: data.id || crypto.randomUUID(),
        name: data.name || 'New Category',
        slug: data.slug || data.name?.toLowerCase().replace(/[^a-z0-9]+/g, '-') || 'new-category',
        description: data.description || '',
        createdAt: new Date().toISOString(),
      }
    }

    function validateCategory(category) {
      const errors = []
      if (!category.name || category.name.trim().length === 0) errors.push('Name is required')
      if (category.name && category.name.length > 50) errors.push('Name must be 50 characters or less')
      return { valid: errors.length === 0, errors }
    }

    test('has default categories', () => {
      expect(defaultCategories.length).toBe(4)
      expect(defaultCategories.find(c => c.id === 'general')).toBeDefined()
      expect(defaultCategories.find(c => c.id === 'tutorials')).toBeDefined()
    })

    test('creates new category', () => {
      const category = createCategory({ name: 'New Topic' })

      expect(category.name).toBe('New Topic')
      expect(category.slug).toBe('new-topic')
    })

    test('validates category name', () => {
      const category = createCategory({ name: '' })
      const result = validateCategory(category)

      expect(result.valid).toBe(false)
      expect(result.errors).toContain('Name is required')
    })

    test('validates category name length', () => {
      const category = createCategory({ name: 'a'.repeat(51) })
      const result = validateCategory(category)

      expect(result.errors).toContain('Name must be 50 characters or less')
    })
  })

  describe('Blog Caching', () => {
    let cache = {
      posts: null,
      categories: null,
      lastUpdate: 0,
      ttl: 60000,
    }

    function getCacheAge() {
      return Date.now() - cache.lastUpdate
    }

    function isCacheValid() {
      return cache.posts !== null && getCacheAge() < cache.ttl
    }

    function invalidateCache() {
      cache.posts = null
      cache.categories = null
      cache.lastUpdate = 0
    }

    function updateCache(posts, categories) {
      cache.posts = posts
      cache.categories = categories
      cache.lastUpdate = Date.now()
    }

    beforeEach(() => {
      invalidateCache()
    })

    test('starts with invalid cache', () => {
      expect(isCacheValid()).toBe(false)
    })

    test('cache becomes valid after update', () => {
      updateCache([{ id: '1' }], [{ id: 'general' }])

      expect(isCacheValid()).toBe(true)
      expect(cache.posts.length).toBe(1)
    })

    test('cache expires after TTL', () => {
      cache.posts = []
      cache.lastUpdate = Date.now() - 70000
      cache.categories = []

      expect(isCacheValid()).toBe(false)
    })

    test('invalidation clears cache', () => {
      updateCache([{ id: '1' }], [])
      invalidateCache()

      expect(cache.posts).toBe(null)
      expect(isCacheValid()).toBe(false)
    })

    test('tracks cache age', () => {
      cache.lastUpdate = Date.now() - 30000

      expect(getCacheAge()).toBeGreaterThanOrEqual(30000)
    })
  })

  describe('Blog Search and Filtering', () => {
    const posts = [
      { id: '1', title: 'WordPress Tutorial', category: 'tutorials', status: 'published', tags: ['wordpress', 'beginner'] },
      { id: '2', title: 'Platform Updates', category: 'updates', status: 'published', tags: ['news'] },
      { id: '3', title: 'Advanced WordPress Tips', category: 'tutorials', status: 'published', tags: ['wordpress', 'advanced'] },
      { id: '4', title: 'Draft Post', category: 'general', status: 'draft', tags: [] },
    ]

    function filterPosts(filters) {
      let result = [...posts]

      if (filters.status) {
        result = result.filter(p => p.status === filters.status)
      }
      if (filters.category) {
        result = result.filter(p => p.category === filters.category)
      }
      if (filters.tag) {
        result = result.filter(p => p.tags.includes(filters.tag))
      }
      if (filters.search) {
        const search = filters.search.toLowerCase()
        result = result.filter(p => p.title.toLowerCase().includes(search))
      }

      return result
    }

    test('filters by status', () => {
      const published = filterPosts({ status: 'published' })
      const drafts = filterPosts({ status: 'draft' })

      expect(published.length).toBe(3)
      expect(drafts.length).toBe(1)
    })

    test('filters by category', () => {
      const tutorials = filterPosts({ category: 'tutorials' })

      expect(tutorials.length).toBe(2)
      expect(tutorials.every(p => p.category === 'tutorials')).toBe(true)
    })

    test('filters by tag', () => {
      const wordpress = filterPosts({ tag: 'wordpress' })

      expect(wordpress.length).toBe(2)
    })

    test('searches by title', () => {
      const results = filterPosts({ search: 'wordpress' })

      expect(results.length).toBe(2)
    })

    test('combines filters', () => {
      const results = filterPosts({ status: 'published', category: 'tutorials' })

      expect(results.length).toBe(2)
    })
  })

  describe('Slug Generation', () => {
    function generateSlug(title) {
      return String(title || '')
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-+|-+$/g, '')
        .slice(0, 100)
    }

    function ensureUniqueSlug(slug, existingSlugs) {
      if (!existingSlugs.includes(slug)) return slug

      let counter = 1
      let newSlug = `${slug}-${counter}`
      while (existingSlugs.includes(newSlug)) {
        counter++
        newSlug = `${slug}-${counter}`
      }
      return newSlug
    }

    test('generates slug from title', () => {
      expect(generateSlug('Hello World')).toBe('hello-world')
      expect(generateSlug('Test Post!')).toBe('test-post')
      expect(generateSlug('Multiple   Spaces')).toBe('multiple-spaces')
    })

    test('handles special characters', () => {
      expect(generateSlug('Test @#$%^& Post')).toBe('test-post')
      expect(generateSlug('--Test--')).toBe('test')
    })

    test('truncates long slugs', () => {
      const longTitle = 'a'.repeat(200)
      const slug = generateSlug(longTitle)

      expect(slug.length).toBe(100)
    })

    test('ensures unique slug', () => {
      const existing = ['test-post', 'test-post-1']

      expect(ensureUniqueSlug('test-post', existing)).toBe('test-post-2')
      expect(ensureUniqueSlug('unique-slug', existing)).toBe('unique-slug')
    })
  })

  describe('RSS Feed Generation', () => {
    function generateRssItem(post, baseUrl) {
      return {
        title: post.title,
        link: `${baseUrl}/blog/${post.slug}`,
        description: post.excerpt || post.content?.slice(0, 200) || '',
        pubDate: new Date(post.publishedAt).toUTCString(),
        guid: `${baseUrl}/blog/${post.slug}`,
      }
    }

    function generateRssFeed(posts, options) {
      const { title, description, baseUrl } = options

      return {
        version: '2.0',
        channel: {
          title,
          description,
          link: `${baseUrl}/blog`,
          lastBuildDate: new Date().toUTCString(),
          items: posts.map(p => generateRssItem(p, baseUrl)),
        },
      }
    }

    test('generates RSS item from post', () => {
      const post = {
        title: 'Test Post',
        slug: 'test-post',
        excerpt: 'Test excerpt',
        publishedAt: '2024-01-01T00:00:00Z',
      }
      const item = generateRssItem(post, 'https://example.com')

      expect(item.title).toBe('Test Post')
      expect(item.link).toBe('https://example.com/blog/test-post')
      expect(item.description).toBe('Test excerpt')
    })

    test('generates RSS feed', () => {
      const posts = [
        { title: 'Post 1', slug: 'post-1', excerpt: 'Excerpt 1', publishedAt: new Date().toISOString() },
        { title: 'Post 2', slug: 'post-2', excerpt: 'Excerpt 2', publishedAt: new Date().toISOString() },
      ]
      const feed = generateRssFeed(posts, {
        title: 'My Blog',
        description: 'Blog description',
        baseUrl: 'https://example.com',
      })

      expect(feed.version).toBe('2.0')
      expect(feed.channel.title).toBe('My Blog')
      expect(feed.channel.items.length).toBe(2)
    })
  })

  describe('Image Handling', () => {
    function validateBlogImage(image) {
      const errors = []
      const maxSize = 5 * 1024 * 1024
      const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']

      if (!image) {
        errors.push('Image is required')
      } else {
        if (image.size > maxSize) errors.push('Image must be 5MB or less')
        if (!allowedTypes.includes(image.type)) errors.push('Invalid image type')
      }

      return { valid: errors.length === 0, errors }
    }

    function generateImageFilename(originalName, postId) {
      const ext = path.extname(originalName || 'image.jpg').toLowerCase()
      const timestamp = Date.now()
      return `blog-${postId}-${timestamp}${ext}`
    }

    test('validates image size', () => {
      const image = { size: 6 * 1024 * 1024, type: 'image/jpeg' }
      const result = validateBlogImage(image)

      expect(result.valid).toBe(false)
      expect(result.errors).toContain('Image must be 5MB or less')
    })

    test('validates image type', () => {
      const image = { size: 1000, type: 'image/bmp' }
      const result = validateBlogImage(image)

      expect(result.errors).toContain('Invalid image type')
    })

    test('accepts valid image', () => {
      const image = { size: 1000, type: 'image/jpeg' }
      const result = validateBlogImage(image)

      expect(result.valid).toBe(true)
    })

    test('generates image filename', () => {
      const filename = generateImageFilename('photo.jpg', 'post-123')

      expect(filename).toMatch(/^blog-post-123-\d+\.jpg$/)
    })

    test('extracts extension from original name', () => {
      const filename = generateImageFilename('image.PNG', 'post-1')

      expect(filename.endsWith('.png')).toBe(true)
    })
  })
})
