/**
 * Post Editor service — data layer for the flat, section-based Post editor
 * (`/posts/:type/:id/edit`) and the per-type Post Template editor
 * (`/posts/types/:slug/template`).
 *
 * A post is a document in its post-type collection (e.g. `blog`). The editor
 * reads/writes the post's fields (title, slug, excerpt, featured image,
 * body, SEO) plus a `data.sections` JSON list of `PageSection`s — the same
 * flat model the Page Editor uses. New posts inherit their section list from
 * the post type's template.
 *
 * A template is a single document per post type in the hidden
 * `post-templates` collection, matched by `slug` == the post-type slug. It
 * stores the design-driven header layout (`data.header`) and the default
 * section list (`data.sections`).
 *
 * Persistence reuses the existing collection document API, so there is no
 * new endpoint and no Prisma migration — `sections` is declared as `json`
 * on each post-type collection (and `header`/`sections` on `post-templates`)
 * so the field-access layer doesn't strip them on write.
 */
import { cmsApi } from './api.js'
import {
  getSectionType,
  createSection,
  addSection,
  removeSection,
  duplicateSection,
  toggleSectionVisibility,
  moveSection,
  reorderSections,
  updateSectionContent,
  updateSectionSettings,
  updateSectionMeta,
  coerceSections,
  coercePostHeader,
  createPostTemplate,
  validateSectionContent,
  type PageSection,
  type PostHeaderConfig,
} from '@actuate-media/cms-core/page-sections'

export type { PageSection, PostHeaderConfig } from '@actuate-media/cms-core/page-sections'

// Re-export the pure section helpers so the post editor imports them from
// one place (mirrors page-editor-service).
export {
  createSection,
  addSection,
  removeSection,
  duplicateSection,
  toggleSectionVisibility,
  moveSection,
  reorderSections,
  updateSectionContent,
  updateSectionSettings,
  updateSectionMeta,
}

export type PostStatus = 'DRAFT' | 'PUBLISHED' | 'SCHEDULED' | 'ARCHIVED'

export const TEMPLATES_COLLECTION = 'post-templates'

export interface EditorPost {
  id: string | null
  /** the post-type collection slug (e.g. "blog") */
  postType: string
  title: string
  slug: string
  excerpt: string
  featuredImage: string
  body: string
  category: string
  status: PostStatus
  publishDate: string | null
  sections: PageSection[]
  seoTitle: string
  seoDescription: string
  publishedAt: string | null
  updatedAt: string | null
}

export interface EditorPostTemplate {
  /** template document id (null when the type has no saved template yet) */
  docId: string | null
  postType: string
  header: PostHeaderConfig
  sections: PageSection[]
}

export interface ValidationResult {
  errors: string[]
  warnings: string[]
}

// ─── Validation ──────────────────────────────────────────────────────

function isBlank(v: unknown): boolean {
  return v === undefined || v === null || (typeof v === 'string' && v.trim() === '')
}

/**
 * Validate a post for save/publish. Section rules are delegated to the
 * shared `validateSectionContent` so the editor and the server/MCP enforce
 * identical content rules; post-level rules (title/slug, SEO) live here.
 */
export function validatePost(post: EditorPost, forPublish = false): ValidationResult {
  const errors: string[] = []
  const warnings: string[] = []

  if (isBlank(post.title)) errors.push('Post title is required.')
  if (isBlank(post.slug)) errors.push('Post slug is required.')

  if (forPublish) {
    const visible = post.sections.filter((s) => s.visible)
    for (const section of visible) {
      const def = getSectionType(section.sectionType)
      const label = section.name || def?.name || 'Section'
      const result = validateSectionContent(section.sectionType, section.content, section.settings)
      for (const issue of result.errors) errors.push(`${label}: ${issue.message}`)
      for (const issue of result.warnings) warnings.push(`${label}: ${issue.message}`)
    }
    if (isBlank(post.seoTitle)) warnings.push('Add an SEO title to improve search results.')
    if (isBlank(post.seoDescription)) {
      warnings.push('Add an SEO description to improve search results.')
    }
  }

  return { errors, warnings }
}

/** Validate a template for save (section content only — no post fields). */
export function validateTemplate(template: EditorPostTemplate): ValidationResult {
  const errors: string[] = []
  const warnings: string[] = []
  for (const section of template.sections.filter((s) => s.visible)) {
    const def = getSectionType(section.sectionType)
    const label = section.name || def?.name || 'Section'
    const result = validateSectionContent(section.sectionType, section.content, section.settings)
    for (const issue of result.errors) errors.push(`${label}: ${issue.message}`)
    for (const issue of result.warnings) warnings.push(`${label}: ${issue.message}`)
  }
  return { errors, warnings }
}

// ─── API row mapping ─────────────────────────────────────────────────

interface DocumentApiRow {
  id: string
  collection?: string
  title: string | null
  slug: string | null
  status: PostStatus
  publishedAt: string | null
  scheduledAt?: string | null
  updatedAt: string | null
  data: Record<string, unknown> | null
}

function dataStr(data: Record<string, unknown>, key: string): string {
  const v = data[key]
  return typeof v === 'string' ? v : ''
}

function rowToEditorPost(postType: string, doc: DocumentApiRow): EditorPost {
  const data = doc.data ?? {}
  return {
    id: doc.id,
    postType,
    title: doc.title ?? dataStr(data, 'title'),
    slug: doc.slug ?? dataStr(data, 'slug'),
    excerpt: dataStr(data, 'excerpt'),
    featuredImage: dataStr(data, 'featuredImage'),
    body: dataStr(data, 'body'),
    category: dataStr(data, 'category'),
    status: doc.status ?? 'DRAFT',
    publishDate: (data.publishDate as string) ?? doc.scheduledAt ?? null,
    sections: coerceSections(data.sections),
    seoTitle: dataStr(data, 'metaTitle'),
    seoDescription: dataStr(data, 'metaDescription'),
    publishedAt: doc.publishedAt ?? null,
    updatedAt: doc.updatedAt ?? null,
  }
}

// ─── Templates ───────────────────────────────────────────────────────

/**
 * Shape returned by the dedicated `/post-templates/:type` endpoint
 * (`{ data: { postType, docId, header, sections } }`, unwrapped by cmsApi).
 */
interface TemplateApiResponse {
  postType: string
  docId: string | null
  header: unknown
  sections: unknown
}

function templateEndpoint(postType: string): string {
  return `/post-templates/${encodeURIComponent(postType)}`
}

/**
 * Load a post type's template via the dedicated, audited `/post-templates/:type`
 * endpoint (scoped + logged), rather than the raw collection API. When the type
 * has no saved template yet the endpoint returns the default starting template;
 * if the request fails outright we still hand back that default so the editor
 * is never blank.
 */
export async function fetchTemplateForEditor(postType: string): Promise<EditorPostTemplate> {
  const res = await cmsApi<TemplateApiResponse>(templateEndpoint(postType))
  if (res.error || !res.data) {
    const seed = createPostTemplate(postType)
    return { docId: null, postType, header: seed.header, sections: seed.sections }
  }
  const r = res.data
  const docId = r.docId ?? null
  const header = coercePostHeader(r.header)
  let sections = coerceSections(r.sections)
  // Defend against an older server (or an empty stored template): an unsaved
  // type should still seed the default sections so authors aren't faced with a
  // blank canvas.
  if (!docId && sections.length === 0) {
    sections = createPostTemplate(postType).sections
  }
  return { docId, postType, header, sections }
}

/** Persist a post type's template via `PUT /post-templates/:type`. */
export async function saveTemplate(template: EditorPostTemplate): Promise<EditorPostTemplate> {
  const res = await cmsApi<TemplateApiResponse>(templateEndpoint(template.postType), {
    method: 'PUT',
    body: JSON.stringify({ header: template.header, sections: template.sections }),
  })
  if (res.error || !res.data) throw new Error(res.error ?? 'Failed to save template')
  const r = res.data
  return {
    docId: r.docId ?? null,
    postType: template.postType,
    header: coercePostHeader(r.header),
    sections: coerceSections(r.sections),
  }
}

// ─── Posts ───────────────────────────────────────────────────────────

export async function fetchPostForEditor(postType: string, id: string): Promise<EditorPost> {
  const res = await cmsApi<DocumentApiRow>(`/collections/${encodeURIComponent(postType)}/${id}`)
  if (res.error || !res.data) throw new Error(res.error ?? 'Failed to load post')
  return rowToEditorPost(postType, res.data)
}

interface PostWritePayload {
  title: string
  slug: string
  excerpt: string
  featuredImage: string
  body: string
  category: string
  publishDate: string | null
  sections: PageSection[]
  metaTitle: string
  metaDescription: string
}

function toWriteBody(post: EditorPost, extra?: Record<string, unknown>): string {
  const payload: PostWritePayload & Record<string, unknown> = {
    title: post.title,
    slug: post.slug,
    excerpt: post.excerpt,
    featuredImage: post.featuredImage,
    body: post.body,
    category: post.category,
    publishDate: post.publishDate,
    sections: post.sections,
    metaTitle: post.seoTitle,
    metaDescription: post.seoDescription,
    ...extra,
  }
  return JSON.stringify(payload)
}

export async function createPost(post: EditorPost): Promise<EditorPost> {
  const res = await cmsApi<DocumentApiRow>(`/collections/${encodeURIComponent(post.postType)}`, {
    method: 'POST',
    body: toWriteBody(post),
  })
  if (res.error || !res.data) throw new Error(res.error ?? 'Failed to create post')
  return rowToEditorPost(post.postType, res.data)
}

export async function savePostDraft(post: EditorPost): Promise<EditorPost> {
  if (!post.id) return createPost(post)
  const res = await cmsApi<DocumentApiRow>(
    `/collections/${encodeURIComponent(post.postType)}/${post.id}`,
    { method: 'PUT', body: toWriteBody(post) },
  )
  if (res.error || !res.data) throw new Error(res.error ?? 'Failed to save draft')
  return rowToEditorPost(post.postType, res.data)
}

export async function publishPost(post: EditorPost): Promise<EditorPost> {
  const endpoint = post.id
    ? `/collections/${encodeURIComponent(post.postType)}/${post.id}`
    : `/collections/${encodeURIComponent(post.postType)}`
  const res = await cmsApi<DocumentApiRow>(endpoint, {
    method: post.id ? 'PUT' : 'POST',
    body: toWriteBody(post, { status: 'PUBLISHED' }),
  })
  if (res.error || !res.data) throw new Error(res.error ?? 'Failed to publish post')
  return rowToEditorPost(post.postType, res.data)
}

/** A blank post scaffold seeded with the post type's template sections. */
export async function emptyPostFromTemplate(postType: string): Promise<EditorPost> {
  let sections: PageSection[] = []
  try {
    const template = await fetchTemplateForEditor(postType)
    // Clone with fresh ids so the post owns its own sections, decoupled from
    // the template (editing the post never mutates the type template).
    sections = template.sections.map((s) => ({ ...s, id: createSection(s.sectionType).id }))
  } catch {
    // No template / fetch failure → start from an empty section list.
    sections = []
  }
  return {
    id: null,
    postType,
    title: '',
    slug: '',
    excerpt: '',
    featuredImage: '',
    body: '',
    category: '',
    status: 'DRAFT',
    publishDate: null,
    sections,
    seoTitle: '',
    seoDescription: '',
    publishedAt: null,
    updatedAt: null,
  }
}
