/**
 * Blog System Module
 * Handles blog storage, retrieval, and management
 * Supports dual-source: repo-based (JSON files) and database-based
 */

const fs = require('fs/promises');
const fsSync = require('fs');
const path = require('path');

// Blog configuration
const BLOGS_REPO_DIR = path.join(process.cwd(), 'blogs');
const BLOGS_DB_FILE = path.join(process.cwd(), '.data', '.opencode-chat', 'blogs.db.json');
const BLOGS_UPLOAD_DIR = path.join(process.cwd(), 'public', 'blogs', 'images');

// In-memory cache
let blogsCache = null;
let categoriesCache = null;
let lastCacheTime = 0;
const CACHE_TTL = 60000; // 1 minute

/**
 * Initialize blog storage
 */
async function initBlogStorage() {
  try {
    // Ensure upload directory exists
    await fs.mkdir(BLOGS_UPLOAD_DIR, { recursive: true });
    
    // Ensure database file exists
    try {
      await fs.access(BLOGS_DB_FILE);
    } catch {
      await fs.mkdir(path.dirname(BLOGS_DB_FILE), { recursive: true });
      await fs.writeFile(BLOGS_DB_FILE, JSON.stringify({
        posts: [],
        categories: [
          { id: 'general', name: 'General', slug: 'general' },
          { id: 'updates', name: 'Updates', slug: 'updates' },
          { id: 'tutorials', name: 'Tutorials', slug: 'tutorials' },
          { id: 'wordpress', name: 'WordPress', slug: 'wordpress' }
        ],
        lastId: 0
      }, null, 2));
    }
  } catch (error) {
    console.error('Failed to initialize blog storage:', error);
  }
}

/**
 * Load database blogs
 */
async function loadDbBlogs() {
  try {
    const data = await fs.readFile(BLOGS_DB_FILE, 'utf8');
    return JSON.parse(data);
  } catch (error) {
    return { posts: [], categories: [], lastId: 0 };
  }
}

/**
 * Save database blogs
 */
async function saveDbBlogs(data) {
  await fs.writeFile(BLOGS_DB_FILE, JSON.stringify(data, null, 2));
}

/**
 * Load repo-based blogs
 */
async function loadRepoBlogs() {
  const posts = [];
  
  try {
    // Load news blogs
    const newsDir = path.join(BLOGS_REPO_DIR, 'news');
    if (fsSync.existsSync(newsDir)) {
      const newsFiles = await fs.readdir(newsDir);
      for (const file of newsFiles) {
        if (file.endsWith('.json')) {
          try {
            const content = await fs.readFile(path.join(newsDir, file), 'utf8');
            const post = JSON.parse(content);
            post.source = 'repo';
            post.type = 'news';
            posts.push(post);
          } catch (e) {
            console.error(`Failed to load news blog ${file}:`, e.message);
          }
        }
      }
    }
    
    // Load SEO blogs
    const seoDir = path.join(BLOGS_REPO_DIR, 'seo');
    if (fsSync.existsSync(seoDir)) {
      const seoFiles = await fs.readdir(seoDir);
      for (const file of seoFiles) {
        if (file.endsWith('.json')) {
          try {
            const content = await fs.readFile(path.join(seoDir, file), 'utf8');
            const post = JSON.parse(content);
            post.source = 'repo';
            post.type = 'seo';
            posts.push(post);
          } catch (e) {
            console.error(`Failed to load SEO blog ${file}:`, e.message);
          }
        }
      }
    }
  } catch (error) {
    console.error('Failed to load repo blogs:', error);
  }
  
  return posts;
}

/**
 * Get all blogs (merged from repo and database)
 */
async function getAllBlogs(options = {}) {
  const { type, status, category, limit, offset, source } = options;
  
  // Load both sources
  const [dbData, repoPosts] = await Promise.all([
    loadDbBlogs(),
    loadRepoBlogs()
  ]);
  
  // Merge posts (database takes precedence for same slug)
  const slugMap = new Map();
  
  // Add repo posts first
  for (const post of repoPosts) {
    if (post.slug) slugMap.set(post.slug, post);
  }
  
  // Add database posts (overwrites repo posts with same slug)
  for (const post of dbData.posts) {
    post.source = 'database';
    if (post.slug) slugMap.set(post.slug, post);
  }
  
  let blogs = Array.from(slugMap.values());
  
  // Apply filters
  if (type) blogs = blogs.filter(b => b.type === type);
  if (status) blogs = blogs.filter(b => b.status === status);
  if (category) blogs = blogs.filter(b => b.category === category || (b.categories && b.categories.includes(category)));
  if (source) blogs = blogs.filter(b => b.source === source);
  
  // Sort by published date (newest first)
  blogs.sort((a, b) => {
    const dateA = new Date(a.published_at || a.created_at || 0);
    const dateB = new Date(b.published_at || b.created_at || 0);
    return dateB - dateA;
  });
  
  // Apply pagination
  if (offset) blogs = blogs.slice(offset);
  if (limit) blogs = blogs.slice(0, limit);
  
  return blogs;
}

/**
 * Get single blog by slug
 */
async function getBlogBySlug(slug) {
  const blogs = await getAllBlogs();
  return blogs.find(b => b.slug === slug);
}

/**
 * Get blog by ID
 */
async function getBlogById(id) {
  const dbData = await loadDbBlogs();
  const post = dbData.posts.find(p => p.id === id);
  if (post) {
    post.source = 'database';
    return post;
  }
  
  const allBlogs = await getAllBlogs();
  return allBlogs.find(b => b.id === id);
}

/**
 * Create new blog post (database only)
 */
async function createBlog(postData) {
  const dbData = await loadDbBlogs();
  
  const newPost = {
    id: `db-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    ...postData,
    source: 'database',
    created_at: new Date().toISOString(),
    updated_at: new Date().toISOString()
  };
  
  // Check for duplicate slug
  const exists = dbData.posts.find(p => p.slug === newPost.slug);
  if (exists) {
    throw new Error('A post with this slug already exists');
  }
  
  dbData.posts.push(newPost);
  await saveDbBlogs(dbData);
  
  return newPost;
}

/**
 * Update blog post (database only)
 */
async function updateBlog(id, updates) {
  const dbData = await loadDbBlogs();
  const index = dbData.posts.findIndex(p => p.id === id);
  
  if (index === -1) {
    throw new Error('Post not found');
  }
  
  // Check if trying to update a repo-based blog
  if (dbData.posts[index].source === 'repo') {
    throw new Error('Cannot edit repository-based blogs from admin panel');
  }
  
  // Check for slug conflict
  if (updates.slug && updates.slug !== dbData.posts[index].slug) {
    const exists = dbData.posts.find(p => p.slug === updates.slug && p.id !== id);
    if (exists) {
      throw new Error('A post with this slug already exists');
    }
  }
  
  dbData.posts[index] = {
    ...dbData.posts[index],
    ...updates,
    updated_at: new Date().toISOString()
  };
  
  await saveDbBlogs(dbData);
  return dbData.posts[index];
}

/**
 * Delete blog post (database only)
 */
async function deleteBlog(id) {
  const dbData = await loadDbBlogs();
  const index = dbData.posts.findIndex(p => p.id === id);
  
  if (index === -1) {
    throw new Error('Post not found');
  }
  
  if (dbData.posts[index].source === 'repo') {
    throw new Error('Cannot delete repository-based blogs from admin panel');
  }
  
  const deleted = dbData.posts.splice(index, 1)[0];
  await saveDbBlogs(dbData);
  
  return deleted;
}

/**
 * Get categories
 */
async function getCategories() {
  const dbData = await loadDbBlogs();
  return dbData.categories || [];
}

/**
 * Create category
 */
async function createCategory(categoryData) {
  const dbData = await loadDbBlogs();
  
  const newCategory = {
    id: `cat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    ...categoryData,
    created_at: new Date().toISOString()
  };
  
  // Check for duplicate slug
  const exists = dbData.categories.find(c => c.slug === newCategory.slug);
  if (exists) {
    throw new Error('A category with this slug already exists');
  }
  
  dbData.categories.push(newCategory);
  await saveDbBlogs(dbData);
  
  return newCategory;
}

/**
 * Update category
 */
async function updateCategory(id, updates) {
  const dbData = await loadDbBlogs();
  const index = dbData.categories.findIndex(c => c.id === id);
  
  if (index === -1) {
    throw new Error('Category not found');
  }
  
  // Check for slug conflict
  if (updates.slug && updates.slug !== dbData.categories[index].slug) {
    const exists = dbData.categories.find(c => c.slug === updates.slug && c.id !== id);
    if (exists) {
      throw new Error('A category with this slug already exists');
    }
  }
  
  dbData.categories[index] = {
    ...dbData.categories[index],
    ...updates,
    updated_at: new Date().toISOString()
  };
  
  await saveDbBlogs(dbData);
  return dbData.categories[index];
}

/**
 * Delete category
 */
async function deleteCategory(id) {
  const dbData = await loadDbBlogs();
  const index = dbData.categories.findIndex(c => c.id === id);
  
  if (index === -1) {
    throw new Error('Category not found');
  }
  
  dbData.categories.splice(index, 1);
  await saveDbBlogs(dbData);
  
  return true;
}

/**
 * Generate slug from title
 */
function generateSlug(title) {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .substring(0, 100);
}

/**
 * Convert Editor.js blocks to HTML
 */
function blocksToHtml(blocks) {
  if (!blocks || !Array.isArray(blocks)) return '';
  
  return blocks.map(block => {
    switch (block.type) {
      case 'header':
        const level = Math.min(Math.max(block.data.level || 2, 1), 6);
        return `<h${level}>${escapeHtml(block.data.text)}</h${level}>`;
      
      case 'paragraph':
        return `<p>${escapeHtml(block.data.text)}</p>`;
      
      case 'list':
        const tag = block.data.style === 'ordered' ? 'ol' : 'ul';
        const items = (block.data.items || []).map(item => `<li>${escapeHtml(item)}</li>`).join('');
        return `<${tag}>${items}</${tag}>`;
      
      case 'quote':
        const caption = block.data.caption ? `<cite>${escapeHtml(block.data.caption)}</cite>` : '';
        return `<blockquote><p>${escapeHtml(block.data.text)}</p>${caption}</blockquote>`;
      
      case 'code':
        return `<pre><code>${escapeHtml(block.data.code)}</code></pre>`;
      
      case 'image':
        const caption_text = block.data.caption ? `<figcaption>${escapeHtml(block.data.caption)}</figcaption>` : '';
        return `<figure><img src="${escapeHtml(block.data.file?.url || block.data.url)}" alt="${escapeHtml(block.data.caption || '')}" />${caption_text}</figure>`;
      
      case 'embed':
        return `<div class="embed" data-service="${escapeHtml(block.data.service)}">${block.data.embed}</div>`;
      
      case 'table':
        const rows = (block.data.content || []).map(row => 
          `<tr>${row.map(cell => `<td>${escapeHtml(cell)}</td>`).join('')}</tr>`
        ).join('');
        return `<table>${rows}</table>`;
      
      default:
        return '';
    }
  }).join('\n');
}

/**
 * Escape HTML entities
 */
function escapeHtml(text) {
  if (!text) return '';
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

module.exports = {
  initBlogStorage,
  getAllBlogs,
  getBlogBySlug,
  getBlogById,
  createBlog,
  updateBlog,
  deleteBlog,
  getCategories,
  createCategory,
  updateCategory,
  deleteCategory,
  generateSlug,
  blocksToHtml,
  BLOGS_UPLOAD_DIR
};