import { apiRequest } from '../lib/api' import { getActiveBusinessId } from '../lib/businessContext' import { extractFirstImageUrl } from '../utils/htmlImages' import type { Blog, BlogAuthor, BlogDetail, BlogPostType, BlogStatus } from '../types/blog' export const BLOGS_PER_PAGE = 12 export interface BlogApi { id: string businessId: string title: string slug: string type: BlogPostType abstract: string mainTextHtml: string status: BlogStatus categoryId: string | null categoryName: string tags: string[] seoMetaTitle?: string | null seoMetaDescription?: string | null titleImageUrl: string | null featuredMediaId: string | null authorId?: string | null author?: BlogAuthor | null commentCount: number publishedAt: string | null createdAt: string updatedAt: string } export interface BlogsListResponse { items: BlogApi[] total: number page: number pageSize: number } export interface ListBlogsParams { page?: number pageSize?: number status?: BlogStatus type?: BlogPostType categoryId?: string title?: string } function businessPath(suffix = '') { const businessId = getActiveBusinessId() if (!businessId) { throw new Error('No active business selected. Please sign in again.') } return `/businesses/${businessId}/blogs${suffix}` } export interface CreateBlogPayload { title: string type: BlogPostType abstract?: string mainTextHtml?: string categoryId?: string status?: BlogStatus featuredMediaId?: string tags?: string[] seoMetaTitle?: string | null seoMetaDescription?: string | null } export interface UpdateBlogPayload { title?: string type?: BlogPostType abstract?: string | null mainTextHtml?: string | null categoryId?: string | null status?: BlogStatus featuredMediaId?: string | null tags?: string[] seoMetaTitle?: string | null seoMetaDescription?: string | null } export function resolveBlogTitleImageUrl(blog: BlogApi): string | null { return blog.titleImageUrl?.trim() || extractFirstImageUrl(blog.mainTextHtml) || null } export function mapBlogApiToUi(blog: BlogApi): Blog { return { id: blog.id, title: blog.title, slug: blog.slug, type: blog.type, abstract: blog.abstract, status: blog.status, categoryId: blog.categoryId, categoryName: blog.categoryName, tags: blog.tags, titleImageUrl: resolveBlogTitleImageUrl(blog), commentCount: blog.commentCount, author: blog.author ?? null, publishedAt: blog.publishedAt, createdAt: blog.createdAt, updatedAt: blog.updatedAt, } } export function mapBlogDetailFromApi(blog: BlogApi): BlogDetail { return { ...mapBlogApiToUi(blog), mainTextHtml: blog.mainTextHtml, author: blog.author ?? null, } } export function formatBlogAuthor(author: BlogAuthor | null): string { if (!author) return 'Unknown author' const name = [author.firstName, author.lastName].filter(Boolean).join(' ').trim() return name || author.email || 'Unknown author' } export function formatBlogDate( value: string | null, locale: 'en' | 'fa' = 'en', ): string { if (!value) return '—' const date = new Date(value) if (Number.isNaN(date.getTime())) return value return date.toLocaleDateString(locale === 'fa' ? 'fa-IR' : 'en-US', { year: 'numeric', month: 'long', day: 'numeric', }) } export function formatBlogCardDate( value: string | null, locale: 'en' | 'fa' = 'en', ): string { if (!value) return locale === 'fa' ? 'منتشر نشده' : 'Not published' const date = new Date(value) if (Number.isNaN(date.getTime())) return value return date.toLocaleDateString(locale === 'fa' ? 'fa-IR' : 'en-US', { year: 'numeric', month: 'short', day: 'numeric', }) } export function isBlogVerified(blog: Pick): boolean { return blog.status === 'published' } export async function setBlogVerified(blogId: string, verified: boolean) { return updateBlog(blogId, { status: verified ? 'published' : 'draft' }) } export function mapBlogApiToFormState(blog: BlogApi) { return { title: blog.title, type: blog.type, abstract: blog.abstract, mainTextHtml: blog.mainTextHtml, categoryId: blog.categoryId ?? '', tags: blog.tags, seoMetaTitle: blog.seoMetaTitle ?? '', seoMetaDescription: blog.seoMetaDescription ?? '', titleImage: resolveBlogTitleImageUrl(blog), featuredMediaId: blog.featuredMediaId, } } export async function listBlogs( page = 1, pageSize = BLOGS_PER_PAGE, signal?: AbortSignal, filters?: Omit, ) { const q = new URLSearchParams() q.set('page', String(page)) q.set('pageSize', String(pageSize)) if (filters?.status) q.set('status', filters.status) if (filters?.type) q.set('type', filters.type) if (filters?.categoryId) q.set('categoryId', filters.categoryId) if (filters?.title) q.set('title', filters.title) const data = await apiRequest(`${businessPath()}?${q.toString()}`, { auth: true, signal, }) return { ...data, items: data.items.map(mapBlogApiToUi), } } export async function getBlog(blogId: string, signal?: AbortSignal) { const data = await apiRequest<{ blog: BlogApi }>(businessPath(`/${blogId}`), { auth: true, signal, }) return data.blog } export async function getBlogDetail(blogId: string, signal?: AbortSignal) { const blog = await getBlog(blogId, signal) return mapBlogDetailFromApi(blog) } export async function createBlog(payload: CreateBlogPayload) { return apiRequest<{ message: string; blog: BlogApi }>(businessPath(), { method: 'POST', auth: true, body: payload, }) } export async function updateBlog(blogId: string, payload: UpdateBlogPayload) { return apiRequest<{ message: string; blog: BlogApi }>(businessPath(`/${blogId}`), { method: 'PATCH', auth: true, body: payload, }) } export async function deleteBlog(blogId: string) { return apiRequest<{ message: string }>(businessPath(`/${blogId}`), { method: 'DELETE', auth: true, }) }