"use client";

import { useState, useEffect, useMemo, useRef, useCallback, Suspense, Component, type ReactNode } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useSession } from "next-auth/react";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { Badge } from "@/components/ui/badge";
import { AccountSelector, type SocialAccountInfo } from "@/components/composer/AccountSelector";
import { MediaUpload } from "@/components/composer/MediaUpload";
import { TagInput } from "@/components/composer/TagInput";
import { PostPreview } from "@/components/composer/PostPreview";
import { NetworkPreview } from "@/components/composer/NetworkPreview";
import { LinkedInTargetingPanel, type LinkedInTargeting } from "@/components/targeting/LinkedInTargetingPanel";
import { FacebookGeoPanel } from "@/components/targeting/FacebookGeoPanel";
import { AIToolbar } from "@/components/composer/AIToolbar";
import { ThreadEditor } from "@/components/composer/ThreadEditor";
import { TrackSelector } from "@/components/composer/TrackSelector";
import { TemplateLibraryModal } from "@/components/composer/TemplateLibraryModal";
import { UrlImportPanel } from "@/components/composer/UrlImportPanel";
import { EmojiPicker } from "@/components/composer/EmojiPicker";
import { LayoutGrid, Link2, Info, AlertTriangle, Wand2, ChevronDown, ChevronUp } from "lucide-react";
import { CampaignPicker } from "@/components/composer/CampaignPicker";
import type { Network, ContentTrack, Region, UserRole } from "@/lib/types";
import { TimeSlotPicker } from "@/components/composer/TimeSlotPicker";
import { RegionSelector } from "@/components/composer/RegionSelector";
import { platformSupportsGeoTargeting } from "@/lib/region-targeting-config";
import { countTwitterChars, truncateAtWordBoundary } from "@/lib/twitter-text";
import { formatDateInput, formatTimeInput } from "@/lib/time-slots";

const CHAR_LIMITS: Partial<Record<Network, number>> = {
  TWITTER: 280,
  LINKEDIN: 3000,
  FACEBOOK: 63206,
  INSTAGRAM: 2200,
};

const emptyLinkedInTargeting: LinkedInTargeting = {
  industries: [],
  seniority: [],
  jobFunctions: [],
  locations: [],
  languages: [],
  companySizes: [],
  estimatedReach: "",
};

function ComposePageInner() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const { data: session } = useSession();
  const role = (session?.user as { role?: UserRole })?.role;
  const isAdmin = role === "ADMIN";
  const isViewer = role === "VIEWER";
  const prefillDate = searchParams.get("date") ?? "";
  const editId = searchParams.get("edit") ?? "";

  // Viewers cannot access compose — redirect to dashboard
  useEffect(() => {
    if (isViewer) {
      router.replace("/dashboard");
    }
  }, [isViewer, router]);

  const [isEditMode, setIsEditMode] = useState(false);
  const [editLoading, setEditLoading] = useState(!!editId);
  const [existingMediaUrls, setExistingMediaUrls] = useState<string[]>([]);
  const [editPostStatus, setEditPostStatus] = useState<string>("");
  const [editReviewComment, setEditReviewComment] = useState<string>("");

  const [content, setContent] = useState("");
  const [networks, setNetworks] = useState<Network[]>([]);
  const [socialAccountIds, setSocialAccountIds] = useState<string[]>([]);
  const [selectedAccounts, setSelectedAccounts] = useState<SocialAccountInfo[]>([]);
  const [files, setFiles] = useState<File[]>([]);
  const [tags, setTags] = useState<string[]>([]);
  const [campaign, setCampaign] = useState("");
  const [scheduleDate, setScheduleDate] = useState(prefillDate);
  const [scheduleTime, setScheduleTime] = useState("");
  const [postNow, setPostNow] = useState(false);
  const [firstComment, setFirstComment] = useState("");
  const [showFirstComment, setShowFirstComment] = useState(false);
  const [linkedInTargeting, setLinkedInTargeting] = useState<LinkedInTargeting>(emptyLinkedInTargeting);
  const [fbCountries, setFbCountries] = useState<string[]>([]);
  const [track, setTrack] = useState<ContentTrack | null>(null);
  const [region, setRegion] = useState<Region | "">("");
  const [linkedInArticle, setLinkedInArticle] = useState(false);
  const [publishPriority, setPublishPriority] = useState<"NORMAL" | "URGENT" | "EMERGENCY">("NORMAL");
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");
  const [rightTab, setRightTab] = useState<"settings" | "preview">("preview");
  const [showTemplates, setShowTemplates] = useState(false);
  const [showUrlImport, setShowUrlImport] = useState(false);
  const [showAdvanced, setShowAdvanced] = useState(false);
  const [generatingArt, setGeneratingArt] = useState(false);
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  const handleEmojiInsert = useCallback((emoji: string) => {
    const ta = textareaRef.current;
    if (ta) {
      const start = ta.selectionStart;
      const end = ta.selectionEnd;
      const next = content.slice(0, start) + emoji + content.slice(end);
      setContent(next);
      // Restore cursor after emoji
      requestAnimationFrame(() => {
        ta.focus();
        const pos = start + emoji.length;
        ta.setSelectionRange(pos, pos);
      });
    } else {
      setContent((prev) => prev + emoji);
    }
  }, [content]);

  // Edit mode: fetch existing post data
  useEffect(() => {
    if (!editId) return;
    setEditLoading(true);
    fetch(`/api/v1/posts/${editId}`)
      .then((res) => {
        if (!res.ok) throw new Error("Post not found");
        return res.json();
      })
      .then((post) => {
        if (post.status === "PUBLISHED") {
          router.replace("/dashboard/calendar");
          return;
        }
        setEditPostStatus(post.status ?? "");
        setEditReviewComment(post.reviewComment ?? "");
        setContent(post.content ?? "");
        setTags(post.tags ?? []);
        setCampaign(post.campaignId ?? "");
        setTrack(post.track ?? null);
        setRegion(post.region ?? "");
        setLinkedInArticle(post.isArticle ?? false);
        setFirstComment(post.firstComment ?? "");
        if (post.firstComment) setShowFirstComment(true);
        if (post.publishPriority && post.publishPriority !== "NORMAL") {
          setPublishPriority(post.publishPriority);
        }
        if (post.scheduledAt) {
          const d = new Date(post.scheduledAt);
          if (!isNaN(d.getTime())) {
            setScheduleDate(formatDateInput(d));
            setScheduleTime(formatTimeInput(d.getHours(), d.getMinutes()));
          }
        }
        if (post.postAccounts?.length) {
          const validAccounts = post.postAccounts.filter(
            (pa: { socialAccount?: { id: string; network: string } | null }) => pa.socialAccount != null
          );
          const ids = validAccounts.map((pa: { socialAccount: { id: string; network: string } }) => pa.socialAccount.id);
          setSocialAccountIds(ids);
          const nets = [...new Set(validAccounts.map((pa: { socialAccount: { network: string } }) =>
            pa.socialAccount.network))] as Network[];
          setNetworks(nets);
        } else if (post.networks?.length) {
          setNetworks(post.networks as Network[]);
        }
        if (post.mediaUrls?.length) {
          setExistingMediaUrls(post.mediaUrls);
        }
        setIsEditMode(true);
      })
      .catch((err) => {
        setError(err instanceof Error ? err.message : "Failed to load post");
      })
      .finally(() => setEditLoading(false));
  }, [editId, router]);

  // Character count with URL normalization
  const charCounts = useMemo(() => {
    const counts: Partial<Record<Network, { count: number; limit: number; over: boolean; warn: boolean }>> = {};
    for (const n of networks) {
      const limit = CHAR_LIMITS[n];
      if (!limit) continue;
      const count = n === "TWITTER" ? countTwitterChars(content) : content.length;
      counts[n] = { count, limit, over: count > limit, warn: count >= limit * 0.8 };
    }
    return counts;
  }, [content, networks]);

  const isOverAnyLimit = useMemo(
    () => Object.values(charCounts).some((c) => c?.over),
    [charCounts]
  );

  const twitterOver = charCounts.TWITTER?.over ?? false;

  const handleFitToTwitter = () => {
    const fitted = truncateAtWordBoundary(content, 280);
    setContent(fitted);
  };

  /** Upload media files and return array of URLs */
  const uploadFiles = async (mediaFiles: File[]): Promise<string[]> => {
    if (mediaFiles.length === 0) return [];
    const formData = new FormData();
    for (const f of mediaFiles) formData.append("files", f);
    const res = await fetch("/api/v1/media/upload", { method: "POST", body: formData });
    if (!res.ok) throw new Error("Failed to upload media");
    const data = await res.json();
    return data.urls as string[];
  };

  const buildAudienceTargeting = () => {
    const targeting: Record<string, unknown> = {};
    if (networks.includes("LINKEDIN")) targeting.linkedin = linkedInTargeting;
    if (networks.includes("FACEBOOK")) targeting.facebook = { countries: fbCountries };
    return Object.keys(targeting).length > 0 ? targeting : undefined;
  };


  const handleSaveDraft = async () => {
    setSaving(true);
    setError("");
    setSuccess("");
    try {
      const newMediaUrls = await uploadFiles(files);
      const allMediaUrls = [...existingMediaUrls, ...newMediaUrls];

      const method = isEditMode ? "PATCH" : "POST";
      const url = isEditMode ? `/api/v1/posts/${editId}` : "/api/v1/posts";

      const payload: Record<string, unknown> = {
        content,
        networks,
        tags,
        campaignId: campaign || undefined,
        audienceTargeting: buildAudienceTargeting(),
        firstComment: firstComment || undefined,
        track: track || undefined,
        region: region || undefined,
        notes: undefined,
        isArticle: linkedInArticle || undefined,
        mediaUrls: allMediaUrls.length > 0 ? allMediaUrls : undefined,
        publishPriority: publishPriority !== "NORMAL" ? publishPriority : undefined,
      };
      if (!isEditMode) {
        payload.socialAccountIds = socialAccountIds.length > 0 ? socialAccountIds : undefined;
        payload.source = "HUMAN";
        payload.status = "DRAFT"; // BUG-1: explicitly mark as draft to skip cap enforcement
      }

      const res = await fetch(url, {
        method,
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });
      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || "Failed to save draft");
      }
      setSuccess(isEditMode ? "Post updated successfully!" : "Draft saved successfully!");
      setTimeout(() => router.push("/dashboard/drafts"), 1200);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to save draft");
    } finally {
      setSaving(false);
    }
  };

  const handleSchedule = async () => {
    setSaving(true);
    setError("");
    setSuccess("");
    try {
      // Build scheduledAt from date + time inputs (explicit local timezone)
      let scheduledAt: string | undefined;
      if (postNow && !isEditMode && isAdmin) {
        // Publish-now still goes through the manual publish endpoint, which only
        // accepts SCHEDULED posts. Use a near-future timestamp so create returns
        // SCHEDULED and the follow-up publish call can proceed immediately.
        scheduledAt = new Date(Date.now() + 60_000).toISOString();
      } else if (!postNow && scheduleDate) {
        const time = scheduleTime || "09:00";
        const [y, mo, d] = scheduleDate.split("-").map(Number);
        const [h, mi] = time.split(":").map(Number);
        scheduledAt = new Date(y, mo - 1, d, h, mi).toISOString();
      }

      const newMediaUrls = await uploadFiles(files);
      const allMediaUrls = [...existingMediaUrls, ...newMediaUrls];

      const method = isEditMode ? "PATCH" : "POST";
      const url = isEditMode ? `/api/v1/posts/${editId}` : "/api/v1/posts";

      const payload: Record<string, unknown> = {
        content,
        networks,
        tags,
        campaignId: campaign || undefined,
        audienceTargeting: buildAudienceTargeting(),
        scheduledAt,
        firstComment: firstComment || undefined,
        track: track || undefined,
        region: region || undefined,
        notes: undefined,
        isArticle: linkedInArticle || undefined,
        mediaUrls: allMediaUrls.length > 0 ? allMediaUrls : undefined,
        publishPriority: publishPriority !== "NORMAL" ? publishPriority : undefined,
      };
      if (!isEditMode) {
        payload.socialAccountIds = socialAccountIds.length > 0 ? socialAccountIds : undefined;
        payload.source = "HUMAN";
      }

      const res = await fetch(url, {
        method,
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });

      if (!res.ok) {
        const data = await res.json();
        throw new Error(data.error || (isEditMode ? "Failed to update post" : "Failed to create post"));
      }

      const post = await res.json();

      // If "Post now" is checked and user is ADMIN, immediately publish (create mode only)
      if (!isEditMode && postNow && isAdmin) {
        const pubRes = await fetch(`/api/v1/posts/${post.id}/publish`, {
          method: "POST",
        });
        if (!pubRes.ok) {
          const pubData = await pubRes.json();
          throw new Error(pubData.details || pubData.error || "Post created but publishing failed");
        }
        setSuccess("Post published successfully!");
      } else if (isEditMode) {
        setSuccess("Post updated successfully!");
      } else if (post.status === "PENDING_APPROVAL") {
        setSuccess("Post submitted for approval!");
      } else if (scheduledAt) {
        const d = new Date(post.scheduledAt || scheduledAt);
        setSuccess(`Post scheduled for ${d.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })}`);
      } else {
        setSuccess("Post saved!");
      }

      // Contextual redirect based on action taken
      const redirectTo = (postNow && isAdmin)
        ? "/dashboard/calendar"
        : (post.status === "PENDING_APPROVAL")
          ? "/dashboard/drafts"
          : "/dashboard/queue";
      setTimeout(() => router.push(redirectTo), 1200);
    } catch (err) {
      const msg = err instanceof Error ? err.message : "Failed to schedule post";
      if (msg.toLowerCase().includes("weekly cap")) {
        setError("This track has reached its weekly posting limit. Try a different track or schedule for next week.");
      } else {
        setError(msg);
      }
    } finally {
      setSaving(false);
    }
  };

  if (editLoading) {
    return <div className="p-8 text-gray-500 text-sm">Loading post...</div>;
  }

  // Guard: show loading while session resolves, block viewers once known
  if (!session) {
    return <div className="p-8 text-gray-400 text-sm">Loading...</div>;
  }
  if (isViewer) {
    return <div className="p-8 text-gray-500 text-sm" data-testid="compose-blocked">You do not have permission to compose posts.</div>;
  }

  return (
    <div className="space-y-6">
      <div className="border-b border-gray-100 -mx-4 md:-mx-6 -mt-4 md:-mt-6 px-4 sm:px-8 py-4 sm:py-5 mb-4 sm:mb-6">
        <h1 className="text-xl sm:text-2xl font-semibold text-gray-900">{isEditMode ? "Edit Post" : "Compose Post"}</h1>
        <p className="text-xs sm:text-sm text-gray-500">{isEditMode ? "Modify and reschedule your post" : "Create and schedule social media content"}</p>
      </div>

      {/* Helpful tip for new users with no accounts selected */}
      {selectedAccounts.length === 0 && !content && (
        <div className="flex items-start gap-3 rounded-lg border border-blue-200 bg-blue-50/50 px-4 py-3">
          <Info className="h-4 w-4 text-blue-500 mt-0.5 shrink-0" />
          <div>
            <p className="text-sm font-medium text-gray-700">New here? Start by selecting an account above.</p>
            <p className="text-xs text-gray-500 mt-0.5">
              Pick a connected social account, write your content, then schedule or publish. Need to connect an account first?{" "}
              <a href="/dashboard/settings" className="text-blue-600 underline">Go to Settings</a>.
            </p>
          </div>
        </div>
      )}

      {/* UXG-6: Rejection reason banner in edit mode */}
      {isEditMode && editPostStatus === "REJECTED" && editReviewComment && (
        <div className="rounded-lg bg-red-50 border border-red-200 px-4 py-3 text-sm text-red-700">
          <span className="font-medium">This post was rejected.</span> Reason: {editReviewComment}
        </div>
      )}

      {/* Status messages */}
      {error && (
        <div className="rounded-lg bg-red-50 border border-red-200 px-4 py-3 text-sm text-red-700">
          {error}
        </div>
      )}
      {success && (
        <div className="rounded-lg bg-green-50 border border-green-200 px-4 py-3 text-sm text-green-700">
          {success}
        </div>
      )}

      <div className="grid gap-4 sm:gap-6 lg:grid-cols-3">
        {/* Left panel — Content */}
        <div className="lg:col-span-2 space-y-6">
          <Card className="bg-white border border-gray-200 rounded-xl shadow-none overflow-visible">
            <CardContent className="space-y-5 overflow-visible pt-6">
              {/* Account selector */}
              <div>
                <Label className="text-sm font-medium text-gray-900 mb-2">Publish to</Label>
                <AccountSelector
                  selectedIds={socialAccountIds}
                  onChange={(ids, nets, accts) => {
                    setSocialAccountIds(ids);
                    setNetworks(nets);
                    setSelectedAccounts(accts);
                  }}
                />
              </div>

              {/* Stagger info note — shown when multiple networks selected */}
              {networks.length > 1 && (
                <div className="flex items-start gap-2 rounded-lg border border-blue-200 bg-blue-50/50 px-3 py-2" data-testid="stagger-info">
                  <Info className="h-4 w-4 text-blue-500 mt-0.5 shrink-0" />
                  <p className="text-xs text-blue-700">
                    Posts will be published 15 minutes apart across networks to optimize engagement.
                    {publishPriority === "EMERGENCY" && (
                      <span className="font-semibold text-red-600 ml-1">Emergency priority: stagger bypassed, publishing to all networks simultaneously.</span>
                    )}
                  </p>
                </div>
              )}

              {/* Priority selector — ADMIN only */}
              {isAdmin && (
                <div>
                  <Label className="text-xs mb-1.5">Publish Priority</Label>
                  <select
                    value={publishPriority}
                    onChange={(e) => setPublishPriority(e.target.value as "NORMAL" | "URGENT" | "EMERGENCY")}
                    className="w-full rounded-lg border border-gray-200 px-3 py-1.5 text-sm text-gray-700"
                    data-testid="priority-selector"
                  >
                    <option value="NORMAL">Normal</option>
                    <option value="URGENT">Urgent</option>
                    <option value="EMERGENCY">Emergency — bypasses stagger, publishes simultaneously</option>
                  </select>
                  {publishPriority === "EMERGENCY" && (
                    <div className="flex items-center gap-1.5 mt-1.5">
                      <AlertTriangle className="h-3.5 w-3.5 text-red-500" />
                      <p className="text-xs text-red-600 font-medium">Emergency posts bypass stagger and publish to all networks immediately.</p>
                    </div>
                  )}
                </div>
              )}

              {/* Content textarea — hero element */}
              <div>
                <div className="flex items-center justify-between mb-2">
                  <Label className="text-sm font-medium text-gray-900">Your post</Label>
                  <div className="flex items-center gap-3">
                    <button
                      type="button"
                      onClick={() => setShowUrlImport((v) => !v)}
                      className="inline-flex items-center justify-center gap-1.5 text-xs font-medium text-blue-600 hover:text-blue-700 min-h-[44px] min-w-[44px] sm:min-w-0 sm:min-h-0"
                    >
                      <Link2 className="h-4 w-4 sm:h-3.5 sm:w-3.5" />
                      <span className="hidden sm:inline">Import from URL</span>
                    </button>
                    <button
                      type="button"
                      onClick={() => setShowTemplates(true)}
                      className="inline-flex items-center justify-center gap-1.5 text-xs font-medium text-blue-600 hover:text-blue-700 min-h-[44px] min-w-[44px] sm:min-w-0 sm:min-h-0"
                    >
                      <LayoutGrid className="h-4 w-4 sm:h-3.5 sm:w-3.5" />
                      <span className="hidden sm:inline">Templates</span>
                    </button>
                  </div>
                </div>
                <textarea
                  ref={textareaRef}
                  value={content}
                  onChange={(e) => setContent(e.target.value)}
                  placeholder="What do you want to share? Write something great..."
                  className="w-full min-h-[240px] rounded-xl border border-gray-200 px-4 py-3 text-base leading-relaxed focus:outline-none focus:ring-2 focus:ring-gray-900 resize-y placeholder:text-gray-400"
                />
                {networks.length > 0 ? (
                  <div className="flex flex-wrap items-center gap-2 mt-1">
                    {networks.map((n) => {
                      const cc = charCounts[n];
                      if (!cc) return null;
                      const remaining = cc.limit - cc.count;
                      return (
                        <span
                          key={n}
                          className={`text-xs tabular-nums ${
                            cc.over ? "text-red-600 font-semibold" : cc.warn ? "text-amber-600 font-medium" : "text-gray-500"
                          }`}
                        >
                          {n}: {cc.over ? remaining : cc.count}/{cc.limit}
                        </span>
                      );
                    })}
                    {twitterOver && networks.includes("TWITTER") && (
                      <button
                        type="button"
                        onClick={handleFitToTwitter}
                        className="text-xs font-medium text-blue-600 hover:text-blue-700 hover:underline ml-1"
                      >
                        Fit to Twitter
                      </button>
                    )}
                  </div>
                ) : content.length > 0 ? (
                  <div className="flex flex-wrap items-center gap-2 mt-1">
                    <span className={`text-xs tabular-nums ${countTwitterChars(content) > 280 ? "text-red-600 font-semibold" : countTwitterChars(content) >= 224 ? "text-amber-600 font-medium" : "text-gray-500"}`}>
                      {countTwitterChars(content)}/280
                    </span>
                  </div>
                ) : null}
              </div>

              {/* URL import panel */}
              <UrlImportPanel
                open={showUrlImport}
                onClose={() => setShowUrlImport(false)}
                onInsert={(text) => setContent(text)}
                onTrackSuggest={(t) => setTrack(t)}
                onImageFound={(imageUrl) => setExistingMediaUrls((prev) => prev.includes(imageUrl) ? prev : [...prev, imageUrl])}
              />

              {/* AI toolbar + Emoji */}
              <AIToolbar content={content} networks={networks} track={track} onAccept={setContent}>
                <EmojiPicker onSelect={handleEmojiInsert} />
              </AIToolbar>

              {/* Thread editor for Twitter when over 280 chars */}
              {networks.includes("TWITTER") && content.length > 280 && (
                <ThreadEditor content={content} onChange={(chunks) => setContent(chunks.join("\n\n"))} />
              )}

              {/* LinkedIn article toggle for long content */}
              {networks.includes("LINKEDIN") && content.length > 1500 && (
                <div className="flex items-center gap-3 rounded-lg border border-blue-200 bg-blue-50/50 px-3 py-2.5">
                  <label className="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
                    <input
                      type="checkbox"
                      checked={linkedInArticle}
                      onChange={(e) => setLinkedInArticle(e.target.checked)}
                      className="rounded"
                    />
                    Post as LinkedIn article
                  </label>
                  <span className="text-xs text-gray-500">Long-form content performs better as an article on LinkedIn</span>
                </div>
              )}

              {/* Media upload */}
              <div>
                <Label className="text-sm font-medium text-gray-900 mb-2">Media</Label>
                {existingMediaUrls.length > 0 && (
                  <div className="flex flex-wrap gap-2 mb-2">
                    {existingMediaUrls.map((url, i) => (
                      <div key={url} className="relative group">
                        <img src={url} alt={`Media ${i + 1}`} className="h-16 w-16 object-cover rounded-lg border border-gray-200" />
                        <button
                          type="button"
                          onClick={() => setExistingMediaUrls((prev) => prev.filter((_, idx) => idx !== i))}
                          className="absolute -top-1.5 -right-1.5 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-opacity"
                        >
                          &times;
                        </button>
                      </div>
                    ))}
                  </div>
                )}
                <MediaUpload files={files} onChange={setFiles} />
                {content.trim().length > 10 && (
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    disabled={generatingArt}
                    className="mt-2 border-purple-200 text-purple-700 hover:bg-purple-50"
                    onClick={async () => {
                      setGeneratingArt(true);
                      try {
                        const res = await fetch("/api/v1/ai/image", {
                          method: "POST",
                          headers: { "Content-Type": "application/json" },
                          body: JSON.stringify({ content }),
                        });
                        if (res.ok) {
                          const data = await res.json();
                          if (data.url) {
                            setExistingMediaUrls((prev) => [...prev, data.url]);
                          }
                        }
                      } catch { /* ignore */ } finally {
                        setGeneratingArt(false);
                      }
                    }}
                  >
                    <Wand2 className={`h-3.5 w-3.5 mr-1.5 ${generatingArt ? "animate-spin" : ""}`} />
                    {generatingArt ? "Generating..." : "AI Art"}
                  </Button>
                )}
              </div>

              {/* Advanced options — collapsible secondary fields */}
              <div className="border border-gray-100 rounded-xl overflow-hidden">
                <button
                  type="button"
                  onClick={() => setShowAdvanced((v) => !v)}
                  className="w-full flex items-center justify-between px-4 py-3 text-sm font-medium text-gray-600 hover:bg-gray-50 transition-colors"
                >
                  <span>Advanced options</span>
                  {showAdvanced ? <ChevronUp className="h-4 w-4 text-gray-400" /> : <ChevronDown className="h-4 w-4 text-gray-400" />}
                </button>
                {showAdvanced && (
                  <div className="border-t border-gray-100 px-4 py-4 space-y-4 bg-gray-50/50">
                    {/* Tags */}
                    <div className="relative z-20">
                      <Label className="text-xs mb-1.5">Tags</Label>
                      <TagInput tags={tags} onChange={setTags} />
                    </div>

                    {/* Campaign */}
                    <div>
                      <Label className="text-xs mb-1.5">Campaign</Label>
                      <CampaignPicker value={campaign} onChange={setCampaign} />
                    </div>

                    {/* Track selector */}
                    <div>
                      <Label className="text-xs mb-1.5">Content Track</Label>
                      <TrackSelector value={track} onChange={setTrack} content={content} />
                    </div>

                    {/* Region selector */}
                    <div>
                      <Label className="text-xs mb-1.5">Region</Label>
                      <RegionSelector value={region} onChange={setRegion} />
                      {region && region !== "GLOBAL" && networks.some((n) => platformSupportsGeoTargeting(n)) && (
                        <p className="text-xs text-blue-600 mt-1" data-testid="geo-targeted-hint">
                          Posts to {networks.filter((n) => platformSupportsGeoTargeting(n)).join(", ")} will be geo-targeted to {region}
                        </p>
                      )}
                    </div>

                    {/* R6: Content guidance tips */}
                    <div className="rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5 space-y-1.5">
                      <p className="text-xs font-semibold text-gray-700">Content Tips</p>
                      <div className="grid grid-cols-1 sm:grid-cols-3 gap-2 text-[11px] text-gray-500">
                        <div><span className="font-medium text-gray-600">80/20 rule:</span> 80% value content, 20% promotional</div>
                        <div><span className="font-medium text-gray-600">Threads:</span> 5-8 tweets optimal, hook first, CTA last</div>
                        <div><span className="font-medium text-gray-600">Hashtags:</span> 1-3 max, at end of post, brand + topic</div>
                      </div>
                      {files.length === 0 && content.length > 20 && (
                        <p className="text-[11px] text-amber-600">Add an image — visual posts get 2x engagement</p>
                      )}
                    </div>
                  </div>
                )}
              </div>

              {/* First comment */}
              {!showFirstComment ? (
                <button
                  type="button"
                  onClick={() => setShowFirstComment(true)}
                  className="text-sm text-gray-900 hover:underline font-medium"
                >
                  + Add first comment (posts 1 min after)
                </button>
              ) : (
                <div>
                  <Label className="text-xs mb-1.5">
                    {networks.includes("LINKEDIN") && !networks.includes("TWITTER")
                      ? "First comment on LinkedIn post"
                      : "First Comment"}
                  </Label>
                  <textarea
                    value={firstComment}
                    onChange={(e) => setFirstComment(e.target.value)}
                    placeholder="This comment will be posted 1 minute after the main post..."
                    className="w-full min-h-[80px] rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-gray-900 resize-y"
                  />
                </div>
              )}
            </CardContent>
          </Card>

          {/* Targeting panels */}
          {networks.includes("LINKEDIN") && (
            <LinkedInTargetingPanel value={linkedInTargeting} onChange={setLinkedInTargeting} />
          )}
          {networks.includes("FACEBOOK") && (
            <FacebookGeoPanel value={fbCountries} onChange={setFbCountries} />
          )}
        </div>

        {/* Right panel — Settings / Preview */}
        <div className="space-y-6">
          {/* Tab toggle */}
          <div className="flex rounded-lg border border-gray-200">
            <button
              onClick={() => setRightTab("settings")}
              className={`flex-1 px-3 py-2.5 sm:py-1.5 min-h-[44px] sm:min-h-0 text-sm font-medium rounded-l-lg transition-colors ${
                rightTab === "settings" ? "bg-gray-900 text-white" : "text-gray-700 hover:bg-gray-50"
              }`}
            >
              Settings
            </button>
            <button
              onClick={() => setRightTab("preview")}
              className={`flex-1 px-3 py-2.5 sm:py-1.5 min-h-[44px] sm:min-h-0 text-sm font-medium rounded-r-lg transition-colors ${
                rightTab === "preview" ? "bg-gray-900 text-white" : "text-gray-700 hover:bg-gray-50"
              }`}
            >
              Preview
            </button>
          </div>

          {rightTab === "preview" ? (
            <Card className="bg-white border border-gray-200 rounded-xl shadow-none">
              <CardHeader>
                <CardTitle className="text-base font-semibold text-gray-900 mb-4">Post Preview</CardTitle>
              </CardHeader>
              <CardContent>
                {networks.length > 1 ? (
                  <NetworkPreview content={content} networks={networks} files={files} />
                ) : (
                  <PostPreview content={content} networks={networks} files={files} />
                )}
              </CardContent>
            </Card>
          ) : (
            <>
              <Card className="bg-white border border-gray-200 rounded-xl shadow-none">
                <CardHeader>
                  <CardTitle className="text-base font-semibold text-gray-900 mb-4">Schedule</CardTitle>
                </CardHeader>
                <CardContent className="space-y-4">
                  {isAdmin ? (
                    <label className="flex items-center gap-2 text-sm text-gray-700">
                      <input
                        type="checkbox"
                        checked={postNow}
                        onChange={(e) => setPostNow(e.target.checked)}
                        className="rounded"
                      />
                      Post now
                    </label>
                  ) : (
                    <p className="text-xs text-amber-600 bg-amber-50 border border-amber-200 rounded-lg px-3 py-2">
                      Your post will be queued for review.
                    </p>
                  )}

                  {!postNow && (
                    <TimeSlotPicker
                      selectedTrack={track}
                      region={region}
                      scheduleDate={scheduleDate}
                      scheduleTime={scheduleTime}
                      onDateChange={setScheduleDate}
                      onTimeChange={setScheduleTime}
                    />
                  )}
                </CardContent>
              </Card>

              <Card className="bg-white border border-gray-200 rounded-xl shadow-none">
                <CardHeader>
                  <CardTitle className="text-base font-semibold text-gray-900 mb-4">Status</CardTitle>
                </CardHeader>
                <CardContent>
                  <div className="flex items-center gap-2">
                    <span className="h-2 w-2 rounded-full bg-gray-400" />
                    <span className="text-sm text-gray-500">Draft</span>
                  </div>
                  {selectedAccounts.length > 0 && (
                    <div className="mt-3 space-y-1">
                      <p className="text-xs font-medium text-gray-500">Publishing To</p>
                      <div className="flex flex-wrap gap-1">
                        {selectedAccounts.map((a) => (
                          <Badge key={a.id} variant="outline" className="text-xs border-gray-200">
                            {a.network} &middot; @{a.accountName}
                            {region && region !== "GLOBAL" && platformSupportsGeoTargeting(a.network) && (
                              <span className="ml-1 text-blue-600" data-testid="geo-targeted-badge">(geo-targeted)</span>
                            )}
                          </Badge>
                        ))}
                      </div>
                    </div>
                  )}
                </CardContent>
              </Card>
            </>
          )}

          <Separator className="bg-gray-200" />

          <div className="flex flex-col gap-2">
            <Button
              onClick={handleSchedule}
              disabled={!content || (networks.length === 0 && socialAccountIds.length === 0) || saving || isOverAnyLimit}
              className="bg-gray-900 hover:bg-gray-800 text-white rounded-lg px-4 py-3 sm:py-2 min-h-[44px] sm:min-h-0 text-sm font-medium w-full"
            >
              {saving ? "Saving..." : isEditMode ? "Update Post" : postNow && isAdmin ? "Publish Now" : role === "EDITOR" ? "Submit for Approval" : "Add to Queue"}
            </Button>
            <Button
              variant="outline"
              onClick={handleSaveDraft}
              disabled={!content || saving}
              className="border border-gray-200 rounded-lg px-4 py-3 sm:py-2 min-h-[44px] sm:min-h-0 text-sm text-gray-700 hover:bg-gray-50 w-full"
            >
              {saving ? "Saving..." : isEditMode ? "Save as Draft" : "Save Draft"}
            </Button>
          </div>
        </div>
      </div>

      {/* Template library modal */}
      <TemplateLibraryModal
        open={showTemplates}
        onClose={() => setShowTemplates(false)}
        currentContent={content}
        onInsert={(templateContent, hashtags, mode) => {
          if (mode === "replace") {
            setContent(templateContent);
          } else {
            setContent((prev) =>
              prev ? prev + "\n\n" + templateContent : templateContent
            );
          }
          if (hashtags.length > 0) {
            setTags((prev) => [...new Set([...prev, ...hashtags])]);
          }
        }}
      />
    </div>
  );
}

class ComposeErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> {
  state = { error: null as Error | null };
  static getDerivedStateFromError(error: Error) { return { error }; }
  componentDidCatch(error: Error) { console.error("[ComposeErrorBoundary]", error); }
  render() {
    if (this.state.error) {
      return (
        <div className="p-8 space-y-4">
          <div className="rounded-lg bg-red-50 border border-red-200 px-4 py-3 text-sm text-red-700">
            Something went wrong in the composer. Please refresh the page.
          </div>
          <button
            onClick={() => this.setState({ error: null })}
            className="text-sm font-medium text-blue-600 hover:underline"
          >
            Try again
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

export default function ComposePage() {
  return (
    <ComposeErrorBoundary>
      <Suspense fallback={<div className="p-8 text-gray-500 text-sm">Loading...</div>}>
        <ComposePageInner />
      </Suspense>
    </ComposeErrorBoundary>
  );
}
