import { mutation } from "../_generated/server";
import { v } from "convex/values";

// Content tables that follow the standard CMS pattern
const CONTENT_TABLES = [
  "blog_posts",
  "hero_sections",
  "feature_blocks",
  "footer_blocks",
  "cta_sections",
  "faq_entries",
  "announcements",
  "page_layouts",
  "site_settings",
  "social_proof",
  "events",
] as const;

type ContentTable = (typeof CONTENT_TABLES)[number];

function isContentTable(table: string): table is ContentTable {
  return (CONTENT_TABLES as readonly string[]).includes(table);
}

/**
 * Create a new content block (hero, feature, announcement, etc.)
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const createContent = mutation({
  args: {
    table: v.string(),
    lang: v.optional(v.string()),
    data: v.any(),
    createdBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    if (!isContentTable(args.table)) {
      throw new Error(`Invalid content table: ${args.table}`);
    }
    const now = Date.now();
    const record = {
      lang: args.lang ?? "en",
      status: "DRAFT" as const,
      version: 1,
      ...args.data,
      createdBy: args.createdBy,
      createdAt: now,
      updatedAt: now,
    };
    const id = await ctx.db.insert(args.table, record);
    return id;
  },
});

/**
 * Update an existing content block
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const updateContent = mutation({
  args: {
    table: v.string(),
    id: v.string(),
    data: v.any(),
    updatedBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    if (!isContentTable(args.table)) {
      throw new Error(`Invalid content table: ${args.table}`);
    }
    const existing = await ctx.db.get(args.id as never);
    if (!existing) {
      throw new Error(`Content ${args.id} not found in ${args.table}`);
    }
    await ctx.db.patch(args.id as never, {
      ...args.data,
      updatedBy: args.updatedBy,
      updatedAt: Date.now(),
    });
    return args.id;
  },
});

/**
 * Publish a content block (DRAFT -> PUBLISHED)
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const publishContent = mutation({
  args: {
    table: v.string(),
    id: v.string(),
    publishedBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    if (!isContentTable(args.table)) {
      throw new Error(`Invalid content table: ${args.table}`);
    }
    const existing = await ctx.db.get(args.id as never);
    if (!existing) {
      throw new Error(`Content ${args.id} not found in ${args.table}`);
    }
    const now = Date.now();

    // Save version snapshot
    await ctx.db.insert("content_versions", {
      entityType: args.table,
      entityId: args.id,
      version: (existing as Record<string, unknown>).version as number,
      snapshot: existing,
      createdBy: args.publishedBy,
      createdAt: now,
    });

    await ctx.db.patch(args.id as never, {
      status: "PUBLISHED",
      publishedBy: args.publishedBy,
      publishedAt: now,
      updatedAt: now,
    });
    return args.id;
  },
});

// ── Blog Post Mutations ─────────────────────────────────────────────────

/**
 * Create a new blog post
 * Fields aligned with blog_posts schema: slug, lang, title, excerpt, content,
 * coverImage, author, tags, published, featured, featuredOrder, metaTitle,
 * metaDescription, metaKeywords, ogImage, readingTimeMinutes, date
 */
export const createBlogPost = mutation({
  args: {
    title: v.string(),
    slug: v.string(),
    lang: v.string(),
    content: v.string(),
    excerpt: v.string(),
    author: v.string(),
    date: v.string(),
    tags: v.array(v.string()),
    published: v.optional(v.boolean()),
    featured: v.optional(v.boolean()),
    featuredOrder: v.optional(v.number()),
    coverImage: v.optional(v.string()),
    metaTitle: v.optional(v.string()),
    metaDescription: v.optional(v.string()),
    metaKeywords: v.optional(v.array(v.string())),
    ogImage: v.optional(v.string()),
    readingTimeMinutes: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    // Ensure slug+lang is unique
    const existingBySlug = await ctx.db
      .query("blog_posts")
      .withIndex("by_slug_lang", (q) =>
        q.eq("slug", args.slug).eq("lang", args.lang),
      )
      .first();
    if (existingBySlug) {
      throw new Error(
        `Blog post with slug "${args.slug}" and lang "${args.lang}" already exists`,
      );
    }

    const now = Date.now();
    const isPublished = args.published ?? false;
    const id = await ctx.db.insert("blog_posts", {
      title: args.title,
      slug: args.slug,
      lang: args.lang,
      content: args.content,
      excerpt: args.excerpt,
      author: args.author,
      date: args.date,
      tags: args.tags,
      published: isPublished,
      featured: args.featured ?? false,
      featuredOrder: args.featuredOrder,
      coverImage: args.coverImage,
      metaTitle: args.metaTitle,
      metaDescription: args.metaDescription,
      metaKeywords: args.metaKeywords,
      ogImage: args.ogImage,
      readingTimeMinutes: args.readingTimeMinutes,
      publishedAt: isPublished ? now : undefined,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Patch-style update for any blog post fields
 * If published changes to true, sets publishedAt automatically
 */
export const updateBlogPost = mutation({
  args: {
    id: v.id("blog_posts"),
    title: v.optional(v.string()),
    slug: v.optional(v.string()),
    lang: v.optional(v.string()),
    content: v.optional(v.string()),
    excerpt: v.optional(v.string()),
    author: v.optional(v.string()),
    date: v.optional(v.string()),
    tags: v.optional(v.array(v.string())),
    published: v.optional(v.boolean()),
    featured: v.optional(v.boolean()),
    featuredOrder: v.optional(v.number()),
    coverImage: v.optional(v.string()),
    metaTitle: v.optional(v.string()),
    metaDescription: v.optional(v.string()),
    metaKeywords: v.optional(v.array(v.string())),
    ogImage: v.optional(v.string()),
    readingTimeMinutes: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const { id, ...updates } = args;
    const existing = await ctx.db.get(id);
    if (!existing) {
      throw new Error(`Blog post ${id} not found`);
    }

    // If slug or lang is changing, verify uniqueness
    const newSlug = updates.slug ?? existing.slug;
    const newLang = updates.lang ?? existing.lang;
    if (newSlug !== existing.slug || newLang !== existing.lang) {
      const existingBySlug = await ctx.db
        .query("blog_posts")
        .withIndex("by_slug_lang", (q) =>
          q.eq("slug", newSlug).eq("lang", newLang),
        )
        .first();
      if (existingBySlug) {
        throw new Error(
          `Blog post with slug "${newSlug}" and lang "${newLang}" already exists`,
        );
      }
    }

    // Build patch object — only include fields that were provided
    const patch: Record<string, unknown> = { updatedAt: Date.now() };
    if (updates.title !== undefined) patch.title = updates.title;
    if (updates.slug !== undefined) patch.slug = updates.slug;
    if (updates.lang !== undefined) patch.lang = updates.lang;
    if (updates.content !== undefined) patch.content = updates.content;
    if (updates.excerpt !== undefined) patch.excerpt = updates.excerpt;
    if (updates.author !== undefined) patch.author = updates.author;
    if (updates.date !== undefined) patch.date = updates.date;
    if (updates.tags !== undefined) patch.tags = updates.tags;
    if (updates.coverImage !== undefined) patch.coverImage = updates.coverImage;
    if (updates.featured !== undefined) patch.featured = updates.featured;
    if (updates.featuredOrder !== undefined)
      patch.featuredOrder = updates.featuredOrder;
    if (updates.metaTitle !== undefined) patch.metaTitle = updates.metaTitle;
    if (updates.metaDescription !== undefined)
      patch.metaDescription = updates.metaDescription;
    if (updates.metaKeywords !== undefined)
      patch.metaKeywords = updates.metaKeywords;
    if (updates.ogImage !== undefined) patch.ogImage = updates.ogImage;
    if (updates.readingTimeMinutes !== undefined)
      patch.readingTimeMinutes = updates.readingTimeMinutes;

    // Handle published change — set publishedAt when transitioning to true
    if (updates.published !== undefined) {
      patch.published = updates.published;
      if (updates.published === true && existing.published !== true) {
        patch.publishedAt = Date.now();
      }
    }

    await ctx.db.patch(id, patch);
    return id;
  },
});

/**
 * Delete a blog post by ID
 */
export const deleteBlogPost = mutation({
  args: {
    id: v.id("blog_posts"),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Blog post ${args.id} not found`);
    }
    await ctx.db.delete(args.id);
    return args.id;
  },
});

/**
 * Bulk update blog post published status
 * Sets publishedAt when transitioning to published=true
 */
export const bulkUpdateBlogStatus = mutation({
  args: {
    ids: v.array(v.id("blog_posts")),
    published: v.boolean(),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const results: { id: string; success: boolean; error?: string }[] = [];

    for (const id of args.ids) {
      const existing = await ctx.db.get(id);
      if (!existing) {
        results.push({ id, success: false, error: "Not found" });
        continue;
      }

      const patch: Record<string, unknown> = {
        published: args.published,
        updatedAt: now,
      };
      if (args.published === true && existing.published !== true) {
        patch.publishedAt = now;
      }

      await ctx.db.patch(id, patch);
      results.push({ id, success: true });
    }

    return results;
  },
});
