/**
 * Blog Management Service
 * Business logic for blog post CRUD operations with AI image generation.
 *
 * Storage: Convex Storage
 * Database: Convex content queries/mutations
 */

import { generateBlogImage } from './ai-image-generator.service';
import { v4 as uuidv4 } from 'uuid';
import type {
  BlogPostDB,
  BlogPostListItem,
  CreateBlogPostInput,
  UpdateBlogPostInput,
  BlogPostFilters,
  PaginatedBlogPosts,
  BlogStats,
  ImageUploadResult,
} from '@/lib/types/blog-management.types';
import { calculateReadingTime } from '@/lib/validators/blog-management.validator';
import {
  uploadToConvexStorage,
  getConvexFileUrl,
  deleteFromConvexStorage,
} from '@/lib/convex-storage';
import { BLOG_IMAGES_BUCKET } from '@/lib/constants/storage.constants';
import { convexQuery, convexMutation } from '@/lib/convex-client';

/**
 * Upload image to Convex Storage
 */
async function uploadImageToStorage(
  base64Data: string,
  filename: string
): Promise<ImageUploadResult> {
  // Convert base64 to buffer
  const buffer = Buffer.from(base64Data, 'base64');

  const result = await uploadToConvexStorage({
    buffer,
    fileName: filename,
    fileType: 'image/png',
    fileSize: buffer.length,
    bucket: BLOG_IMAGES_BUCKET,
    entityType: 'blog_post',
  });

  // Get public URL for the uploaded file
  const publicUrl = await getConvexFileUrl(result.storageId);

  return {
    publicUrl: publicUrl || '',
    path: result.storageId, // storageId serves as the path reference
    bucket: BLOG_IMAGES_BUCKET,
  };
}

/**
 * Delete image from Convex Storage
 */
async function deleteImageFromStorage(imagePath: string): Promise<void> {
  try {
    // imagePath may be a Convex storageId or a URL containing one
    const storageId = imagePath.includes('storageId=')
      ? new URL(imagePath).searchParams.get('storageId') || imagePath
      : imagePath;

    await deleteFromConvexStorage(storageId);
  } catch (error) {
    console.error('Error deleting image from storage:', error);
  }
}

/**
 * Get all blog posts with optional filters and pagination
 */
export async function getBlogPosts(
  filters: BlogPostFilters = {},
  page: number = 1,
  pageSize: number = 20
): Promise<PaginatedBlogPosts> {
  // Use Convex getAllPosts then filter/paginate client-side
  const lang = filters.lang || 'en';
  const allPosts = await convexQuery("content/queries:getAllPosts", {
    lang,
    published: filters.status === 'PUBLISHED' ? true : undefined,
  }) as BlogPostListItem[] | null;

  let posts = allPosts || [];

  // Apply filters client-side (Convex queries handle basic lang/published)
  if (filters.status && filters.status !== 'PUBLISHED') {
    posts = posts.filter((p) => p.status === filters.status);
  }
  if (filters.featured !== undefined) {
    posts = posts.filter((p) => p.featured === filters.featured);
  }
  if (filters.author) {
    posts = posts.filter((p) => p.author === filters.author);
  }
  if (filters.tags && filters.tags.length > 0) {
    posts = posts.filter((p) =>
      filters.tags!.every((t) => (p.tags || []).includes(t))
    );
  }
  if (filters.search) {
    const search = filters.search.toLowerCase();
    posts = posts.filter(
      (p) =>
        p.title.toLowerCase().includes(search) ||
        (p.excerpt && p.excerpt.toLowerCase().includes(search))
    );
  }
  if (filters.dateFrom) {
    posts = posts.filter((p) => p.date >= filters.dateFrom!);
  }
  if (filters.dateTo) {
    posts = posts.filter((p) => p.date <= filters.dateTo!);
  }

  // Paginate
  const total = posts.length;
  const totalPages = Math.ceil(total / pageSize);
  const start = (page - 1) * pageSize;
  const paged = posts.slice(start, start + pageSize);

  return {
    posts: paged,
    total,
    page,
    pageSize,
    totalPages,
  };
}

/**
 * Get a single blog post by ID
 */
export async function getBlogPostById(id: string): Promise<BlogPostDB | null> {
  // Use getAllPosts and find by ID (no direct getById in content queries)
  const enPosts = await convexQuery("content/queries:getAllPosts", {
    lang: 'en',
    published: undefined,
  }) as BlogPostDB[] | null;

  const arPosts = await convexQuery("content/queries:getAllPosts", {
    lang: 'ar',
    published: undefined,
  }) as BlogPostDB[] | null;

  const allPosts = [...(enPosts || []), ...(arPosts || [])];
  return allPosts.find((p) => p.id === id || (p as Record<string, unknown>)._id === id) || null;
}

/**
 * Check if slug exists for a given language
 */
export async function checkSlugExists(
  slug: string,
  lang: 'ar' | 'en',
  excludeId?: string
): Promise<boolean> {
  const post = await convexQuery("content/queries:getPostBySlug", {
    slug,
    lang,
  }) as BlogPostDB | null;

  if (!post) return false;
  if (excludeId && (post.id === excludeId || (post as Record<string, unknown>)._id === excludeId)) {
    return false;
  }
  return true;
}

/**
 * Create a new blog post with optional AI-generated image
 */
export async function createBlogPost(input: CreateBlogPostInput): Promise<BlogPostDB> {
  // Check if slug already exists
  const slugExists = await checkSlugExists(input.slug, input.lang);
  if (slugExists) {
    throw new Error(`A post with slug '${input.slug}' already exists for language '${input.lang}'`);
  }

  let coverImageUrl: string | null = null;

  // Generate AI image if requested
  if (input.generateAiImage) {
    try {
      const { posts: existingPosts } = await getBlogPosts({ lang: input.lang }, 1, 10);

      const generatedImage = await generateBlogImage({
        title: input.title,
        excerpt: input.excerpt,
        tags: input.tags,
        language: input.lang,
        existingPosts: existingPosts.map((p) => ({
          title: p.title,
          coverImage: p.cover_image || undefined,
        })),
      });

      const filename = `${input.slug}-${Date.now()}.png`;
      const uploadResult = await uploadImageToStorage(generatedImage.base64, filename);
      coverImageUrl = uploadResult.publicUrl;
    } catch (error) {
      console.error('Failed to generate AI image:', error);
      // Continue without image
    }
  }

  const readingTimeMinutes = calculateReadingTime(input.content);

  const postData = {
    id: uuidv4(),
    slug: input.slug,
    lang: input.lang,
    title: input.title,
    excerpt: input.excerpt,
    content: input.content,
    cover_image: coverImageUrl,
    author: input.author,
    tags: input.tags || [],
    status: input.status || 'DRAFT',
    published: input.status === 'PUBLISHED',
    featured: input.featured || false,
    featured_order: input.featured_order || null,
    meta_title: input.meta_title || input.title,
    meta_description: input.meta_description || input.excerpt,
    meta_keywords: input.meta_keywords || input.tags || [],
    og_image: coverImageUrl,
    reading_time_minutes: readingTimeMinutes,
    date: input.date || new Date().toISOString().split('T')[0],
    published_at: input.status === 'PUBLISHED' ? new Date().toISOString() : null,
    created_at: new Date().toISOString(),
    updated_at: new Date().toISOString(),
  };

  const convexId = await convexMutation("content/mutations:createContent", {
    table: 'blog_posts',
    lang: input.lang,
    data: postData,
  });

  return { ...postData, _id: convexId } as unknown as BlogPostDB;
}

/**
 * Update an existing blog post
 */
export async function updateBlogPost(
  id: string,
  input: UpdateBlogPostInput
): Promise<BlogPostDB> {
  const existingPost = await getBlogPostById(id);
  if (!existingPost) {
    throw new Error('Blog post not found');
  }

  if (input.slug && input.slug !== existingPost.slug) {
    const slugExists = await checkSlugExists(input.slug, existingPost.lang, id);
    if (slugExists) {
      throw new Error(`A post with slug '${input.slug}' already exists for this language`);
    }
  }

  let coverImageUrl = existingPost.cover_image;

  if (input.regenerateAiImage) {
    try {
      if (existingPost.cover_image) {
        await deleteImageFromStorage(existingPost.cover_image);
      }

      const { posts: existingPosts } = await getBlogPosts({ lang: existingPost.lang }, 1, 10);

      const generatedImage = await generateBlogImage({
        title: input.title || existingPost.title,
        excerpt: input.excerpt || existingPost.excerpt,
        tags: input.tags || existingPost.tags,
        language: existingPost.lang,
        existingPosts: existingPosts.map((p) => ({
          title: p.title,
          coverImage: p.cover_image || undefined,
        })),
      });

      const filename = `${input.slug || existingPost.slug}-${Date.now()}.png`;
      const uploadResult = await uploadImageToStorage(generatedImage.base64, filename);
      coverImageUrl = uploadResult.publicUrl;
    } catch (error) {
      console.error('Failed to regenerate AI image:', error);
    }
  }

  const updateData: Record<string, unknown> = {
    updated_at: new Date().toISOString(),
  };

  if (input.slug !== undefined) updateData.slug = input.slug;
  if (input.title !== undefined) updateData.title = input.title;
  if (input.excerpt !== undefined) updateData.excerpt = input.excerpt;
  if (input.content !== undefined) {
    updateData.content = input.content;
    updateData.reading_time_minutes = calculateReadingTime(input.content);
  }
  if (input.author !== undefined) updateData.author = input.author;
  if (input.tags !== undefined) updateData.tags = input.tags;
  if (input.status !== undefined) {
    updateData.status = input.status;
    updateData.published = input.status === 'PUBLISHED';
    if (input.status === 'PUBLISHED' && existingPost.status !== 'PUBLISHED') {
      updateData.published_at = new Date().toISOString();
    }
  }
  if (input.featured !== undefined) updateData.featured = input.featured;
  if (input.featured_order !== undefined) updateData.featured_order = input.featured_order;
  if (input.meta_title !== undefined) updateData.meta_title = input.meta_title;
  if (input.meta_description !== undefined) updateData.meta_description = input.meta_description;
  if (input.meta_keywords !== undefined) updateData.meta_keywords = input.meta_keywords;
  if (input.date !== undefined) updateData.date = input.date;
  if (input.published_at !== undefined) updateData.published_at = input.published_at;
  if (input.cover_image !== undefined || coverImageUrl !== existingPost.cover_image) {
    updateData.cover_image = input.cover_image || coverImageUrl;
    updateData.og_image = input.cover_image || coverImageUrl;
  }

  // Use the Convex _id if available, otherwise the UUID id
  const convexDocId = (existingPost as Record<string, unknown>)._id as string || id;

  await convexMutation("content/mutations:updateContent", {
    table: 'blog_posts',
    id: convexDocId,
    data: updateData,
  });

  return { ...existingPost, ...updateData } as unknown as BlogPostDB;
}

/**
 * Delete a blog post and its associated image
 */
export async function deleteBlogPost(id: string): Promise<void> {
  const post = await getBlogPostById(id);
  if (!post) {
    throw new Error('Blog post not found');
  }

  // Delete associated image from storage
  if (post.cover_image) {
    await deleteImageFromStorage(post.cover_image);
  }

  // Delete post from database via generic content mutation
  const convexDocId = (post as Record<string, unknown>)._id as string || id;
  await convexMutation("content/mutations:updateContent", {
    table: 'blog_posts',
    id: convexDocId,
    data: { status: 'DELETED', updatedAt: Date.now() },
  });
}

/**
 * Get blog statistics for dashboard
 */
export async function getBlogStats(): Promise<BlogStats> {
  const enPosts = await convexQuery("content/queries:getAllPosts", {
    lang: 'en',
    published: undefined,
  }) as BlogPostListItem[] | null;

  const arPosts = await convexQuery("content/queries:getAllPosts", {
    lang: 'ar',
    published: undefined,
  }) as BlogPostListItem[] | null;

  const posts = [...(enPosts || []), ...(arPosts || [])];

  const sevenDaysAgo = new Date();
  sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

  return {
    total: posts.length,
    byStatus: {
      draft: posts.filter((p) => p.status === 'DRAFT').length,
      published: posts.filter((p) => p.status === 'PUBLISHED').length,
      archived: posts.filter((p) => p.status === 'ARCHIVED').length,
    },
    byLanguage: {
      ar: (arPosts || []).length,
      en: (enPosts || []).length,
    },
    featured: posts.filter((p) => p.featured === true).length,
    recentlyPublished: posts.filter(
      (p) =>
        p.status === 'PUBLISHED' &&
        p.published_at &&
        new Date(p.published_at) >= sevenDaysAgo
    ).length,
  };
}

/**
 * Get all unique authors
 */
export async function getAllAuthors(): Promise<string[]> {
  const enPosts = await convexQuery("content/queries:getAllPosts", {
    lang: 'en',
    published: undefined,
  }) as BlogPostListItem[] | null;

  const arPosts = await convexQuery("content/queries:getAllPosts", {
    lang: 'ar',
    published: undefined,
  }) as BlogPostListItem[] | null;

  const allPosts = [...(enPosts || []), ...(arPosts || [])];
  const authors = [...new Set(allPosts.map((p) => p.author))].sort();
  return authors;
}

/**
 * Get all unique tags across all posts
 */
export async function getAllBlogTags(): Promise<string[]> {
  const enTags = await convexQuery("content/queries:getAllTags", {
    lang: 'en',
  }) as string[] | null;

  const arTags = await convexQuery("content/queries:getAllTags", {
    lang: 'ar',
  }) as string[] | null;

  const allTags = [...new Set([...(enTags || []), ...(arTags || [])])].sort();
  return allTags;
}

/**
 * Bulk update blog post status
 */
export async function bulkUpdateStatus(
  ids: string[],
  status: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED'
): Promise<number> {
  const updateData: Record<string, unknown> = {
    status,
    published: status === 'PUBLISHED',
    updated_at: new Date().toISOString(),
  };

  if (status === 'PUBLISHED') {
    updateData.published_at = new Date().toISOString();
  }

  let updated = 0;
  for (const id of ids) {
    try {
      await convexMutation("content/mutations:updateContent", {
        table: 'blog_posts',
        id,
        data: updateData,
      });
      updated++;
    } catch (error) {
      console.error(`Error updating blog post ${id}:`, error);
    }
  }

  return updated;
}

/**
 * Toggle featured status
 */
export async function toggleFeatured(id: string, featured: boolean): Promise<void> {
  await convexMutation("content/mutations:updateContent", {
    table: 'blog_posts',
    id,
    data: {
      featured,
      updated_at: new Date().toISOString(),
    },
  });
}
