import { prisma } from "@/lib/prisma";
import { requireUser, withGuard, HttpError } from "@/lib/guard";
import { parseBody, ok } from "@/lib/http";
import { updatePostSchema } from "@/lib/validators";
import { can, isManagerTier } from "@/lib/rbac";
import type { Prisma, PostStatus } from "@prisma/client";

export const runtime = "nodejs";

type Ctx = { params: Promise<{ id: string }> };

const INCLUDE = {
  author: { select: { id: true, name: true, position: true } },
  reviewer: { select: { id: true, name: true } },
  platforms: { select: { id: true, name: true } },
} satisfies Prisma.PostInclude;

// PATCH /api/posts/[id] — edit content and/or drive a workflow transition.
//
// Workflow rules (FR-3):
//   • Author edits content only while DRAFT or REJECTED.
//   • action:"submit"  — author sends DRAFT/REJECTED → PENDING_APPROVAL.
//   • action:"approve" — a manager moves PENDING_APPROVAL → APPROVED.
//   • action:"reject"  — a manager moves PENDING_APPROVAL → REJECTED (+ note).
//   • action:"post"    — ONLY the author publishes APPROVED → POSTED (+ date).
//   • action:"reopen"  — author moves REJECTED → DRAFT to edit again.
export const PATCH = withGuard(async (req: Request, ctx: Ctx) => {
  const me = await requireUser();
  const { id } = await ctx.params;
  const data = await parseBody(req, updatePostSchema);

  const post = await prisma.post.findUnique({ where: { id } });
  if (!post) throw new HttpError(404, "Post not found");

  const isAuthor = post.authorId === me.id;
  const isManager = isManagerTier(me.role);
  const update: Prisma.PostUpdateInput = {};

  // ── Workflow action ─────────────────────────────────────────────────────────
  if (data.action) {
    const next = transition(post.status, data.action);
    switch (data.action) {
      case "submit":
      case "reopen": {
        if (!isAuthor && me.role !== "SUPER_ADMIN")
          throw new HttpError(403, "Only the author can do this");
        update.status = next;
        break;
      }
      case "approve":
      case "reject": {
        if (!can(me.role, "posts:approve"))
          throw new HttpError(403, "Only a manager can review posts");
        if (data.action === "reject" && !data.reviewNote?.trim())
          throw new HttpError(400, "A note is required when rejecting");
        update.status = next;
        update.reviewer = { connect: { id: me.id } };
        update.reviewNote = data.reviewNote?.trim() || null;
        break;
      }
      case "post": {
        // Only the marketing staff who authored it may publish (FR-3).
        if (!isAuthor && me.role !== "SUPER_ADMIN")
          throw new HttpError(403, "Only the author publishes the post");
        update.status = next;
        update.postedDate = data.postedDate ? new Date(data.postedDate) : new Date();
        break;
      }
    }
  }

  // ── Content edits ──────────────────────────────────────────────────────────
  const editsContent =
    data.title !== undefined ||
    data.content !== undefined ||
    data.mediaUrl !== undefined ||
    data.mediaNotes !== undefined ||
    data.scheduledDate !== undefined ||
    data.platformIds !== undefined;

  if (editsContent) {
    if (!isAuthor && !isManager)
      throw new HttpError(403, "You cannot edit this post");
    // Content is locked once it is awaiting review, approved, or live —
    // unless a manager is intervening.
    const editable = post.status === "DRAFT" || post.status === "REJECTED";
    if (!editable && !isManager)
      throw new HttpError(400, "This post can no longer be edited");

    if (data.title !== undefined) update.title = data.title;
    if (data.content !== undefined) update.content = data.content;
    if (data.mediaUrl !== undefined) update.mediaUrl = data.mediaUrl || null;
    if (data.mediaNotes !== undefined) update.mediaNotes = data.mediaNotes || null;
    if (data.scheduledDate !== undefined)
      update.scheduledDate = data.scheduledDate ? new Date(data.scheduledDate) : null;
    if (data.platformIds !== undefined) {
      const platforms = await prisma.platform.findMany({
        where: { id: { in: data.platformIds }, active: true },
        select: { id: true },
      });
      update.platforms = { set: platforms.map((p) => ({ id: p.id })) };
    }
  }

  if (Object.keys(update).length === 0)
    throw new HttpError(400, "Nothing to update");

  const updated = await prisma.post.update({
    where: { id },
    data: update,
    include: INCLUDE,
  });
  return ok(updated);
});

// DELETE /api/posts/[id] — author removes their own draft; managers can remove any.
export const DELETE = withGuard(async (_req: Request, ctx: Ctx) => {
  const me = await requireUser();
  const { id } = await ctx.params;
  const post = await prisma.post.findUnique({ where: { id } });
  if (!post) throw new HttpError(404, "Post not found");
  if (post.authorId !== me.id && !isManagerTier(me.role))
    throw new HttpError(403, "You cannot delete this post");
  await prisma.post.delete({ where: { id } });
  return ok({ ok: true });
});

// Validate a status transition and return the resulting status, or throw 400.
function transition(current: PostStatus, action: string): PostStatus {
  const allowed: Record<string, { from: PostStatus[]; to: PostStatus }> = {
    submit: { from: ["DRAFT", "REJECTED"], to: "PENDING_APPROVAL" },
    approve: { from: ["PENDING_APPROVAL"], to: "APPROVED" },
    reject: { from: ["PENDING_APPROVAL"], to: "REJECTED" },
    post: { from: ["APPROVED"], to: "POSTED" },
    reopen: { from: ["REJECTED"], to: "DRAFT" },
  };
  const rule = allowed[action];
  if (!rule || !rule.from.includes(current))
    throw new HttpError(400, `Cannot ${action} a post that is ${current}`);
  return rule.to;
}
