"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { toast } from "sonner";
import { Pencil, Plus } from "lucide-react";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import {
  AuthorLine,
  ContentEditor,
  DeleteButton,
  NotesSheet,
  type NewCommentInput,
  type NewNoteInput,
  type NotePatch,
} from "@/components/notes-sheet";
import { Markdown } from "@/components/markdown";
import {
  NotesCell,
  PillSelect,
  StatCard,
} from "@/components/review-table";
import { ALL, EnumFilter, ExpandableSearch } from "@/components/column-filters";
import { createClient } from "@/lib/supabase/client";
import { NOTE_BUCKET } from "@/lib/constants";
import { formatTimestamp } from "@/lib/format";
import {
  CATEGORY_DOT,
  SIGNOFF_DOT,
  SIGNOFF_STYLES,
  STATUS_DOT,
  STATUS_STYLES,
} from "@/lib/status-styles";
import {
  DEFAULT_NOTE_CATEGORY,
  NOTE_CATEGORIES,
  PAGE_STATUSES,
  SIGNOFF_OPTIONS,
  type Comment,
  type Feature,
  type Note,
  type NoteCategory,
  type PageStatus,
  type Signoff,
} from "@/lib/types";

const INCOMPLETE = "incomplete";
const IN_PROGRESS = "inprogress";
const LAST_CATEGORY_KEY = "review:lastNoteCategory";
const IN_PROGRESS_STATUSES: PageStatus[] = [
  "creating",
  "finalizing",
  "revision pending",
  "revising",
];

// The description cell: markdown body + author/date, with author-only edit/delete.
function FeatureDescription({
  feature,
  canEdit,
  onSave,
  onDelete,
}: {
  feature: Feature;
  canEdit: boolean;
  onSave: (description: string) => void;
  onDelete: () => void;
}) {
  const [editing, setEditing] = useState(false);

  if (editing) {
    return (
      <ContentEditor
        initial={feature.description}
        onSave={(description) => {
          onSave(description);
          setEditing(false);
        }}
        onCancel={() => setEditing(false)}
      />
    );
  }

  return (
    <div className="space-y-1">
      <div className="min-w-0 break-words">
        {feature.description ? (
          <Markdown content={feature.description} />
        ) : (
          <span className="text-sm italic text-muted-foreground">
            No description
          </span>
        )}
      </div>
      <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 pt-0.5">
        <AuthorLine email={feature.author_email} />
        <span className="font-mono text-xs text-muted-foreground">
          {formatTimestamp(feature.created_at)}
        </span>
        {canEdit && (
          <span className="ml-auto flex items-center gap-0.5">
            <button
              type="button"
              aria-label="Edit feature"
              title="Edit feature"
              onClick={() => setEditing(true)}
              className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
            >
              <Pencil className="size-3.5" />
            </button>
            <DeleteButton label="Delete feature" onDelete={onDelete} />
          </span>
        )}
      </div>
    </div>
  );
}

export function FeaturesTable({
  projectId,
  initialFeatures,
  currentUserEmail,
  users = [],
}: {
  projectId: string;
  initialFeatures: Feature[];
  currentUserEmail: string | null;
  users?: string[];
}) {
  const [features, setFeatures] = useState<Feature[]>(initialFeatures);
  const [openFeatureId, setOpenFeatureId] = useState<string | null>(null);
  const [openNoteId, setOpenNoteId] = useState<string | null>(null);
  const [addOpen, setAddOpen] = useState(false);
  const [addDraft, setAddDraft] = useState("");
  const router = useRouter();
  const supabase = useMemo(() => createClient(), []);

  const [query, setQuery] = useState("");
  const [statusFilter, setStatusFilter] = useState<string>(INCOMPLETE);
  const [signoffFilter, setSignoffFilter] = useState<string>(ALL);
  const [categoryFilter, setCategoryFilter] = useState<string>(ALL);
  const [showResolved, setShowResolved] = useState(false);

  const [defaultCategory, setDefaultCategory] = useState<NoteCategory>(
    DEFAULT_NOTE_CATEGORY,
  );
  useEffect(() => {
    const saved = localStorage.getItem(LAST_CATEGORY_KEY);
    if (saved && (NOTE_CATEGORIES as readonly string[]).includes(saved)) {
      setDefaultCategory(saved as NoteCategory);
    }
  }, []);

  useEffect(() => {
    setFeatures(initialFeatures);
  }, [initialFeatures]);

  useEffect(() => {
    const channel = supabase
      .channel(`features-${projectId}`)
      .on(
        "postgres_changes",
        {
          event: "*",
          schema: "public",
          table: "features",
          filter: `project_id=eq.${projectId}`,
        },
        () => router.refresh(),
      )
      .on(
        "postgres_changes",
        { event: "*", schema: "public", table: "notes" },
        () => router.refresh(),
      )
      .on(
        "postgres_changes",
        { event: "*", schema: "public", table: "comments" },
        () => router.refresh(),
      )
      .subscribe();
    return () => {
      supabase.removeChannel(channel);
    };
  }, [supabase, projectId, router]);

  // Deep-link from a notification: /<slug>?focus=feature:<id>&note=<id>. Opens
  // the feature's notes thread as a modal (works regardless of filters). The _t
  // param makes re-clicks re-open it.
  const searchParams = useSearchParams();
  const handledFocus = useRef<string | null>(null);
  useEffect(() => {
    const focus = searchParams.get("focus");
    if (!focus || !focus.startsWith("feature:")) return;
    const key = focus + ":" + (searchParams.get("_t") ?? "");
    if (handledFocus.current === key) return;
    const featureId = focus.slice("feature:".length);
    if (!features.some((f) => f.id === featureId)) return;
    handledFocus.current = key;
    setOpenFeatureId(featureId);
    setOpenNoteId(searchParams.get("note"));
  }, [searchParams, features]);

  async function setStatus(featureId: string, status: PageStatus) {
    // Same status → review automation as pages.
    const patch: { status: PageStatus; signoff?: Signoff } = { status };
    if (status === "open" || status === "creating" || status === "finalizing") {
      patch.signoff = "not ready";
    } else if (status === "drafted") {
      patch.signoff = "ready for review";
    } else if (status === "revising") {
      patch.signoff = "revision not ready";
    }

    const snapshot = features;
    setFeatures((prev) =>
      prev.map((f) => (f.id === featureId ? { ...f, ...patch } : f)),
    );
    const { error } = await supabase
      .from("features")
      .update(patch)
      .eq("id", featureId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't update status. Please try again.");
      return;
    }
    router.refresh();
  }

  async function setSignoff(featureId: string, signoff: Signoff) {
    const patch: { signoff: Signoff; status?: PageStatus } = { signoff };
    if (signoff === "revision needed") patch.status = "revision pending";
    else if (signoff === "approved") patch.status = "completed";

    const snapshot = features;
    setFeatures((prev) =>
      prev.map((f) => (f.id === featureId ? { ...f, ...patch } : f)),
    );
    const { error } = await supabase
      .from("features")
      .update(patch)
      .eq("id", featureId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't update review. Please try again.");
      return;
    }
    router.refresh();
  }

  async function addFeature(description: string) {
    const text = description.trim();
    if (!text) return;
    const { data, error } = await supabase
      .from("features")
      .insert({ project_id: projectId, description: text })
      .select("id, created_at")
      .single();
    if (error) {
      toast.error("Couldn't add the feature. Please try again.");
      return;
    }
    const feature: Feature = {
      id: data.id,
      project_id: projectId,
      description: text,
      status: "open",
      signoff: "not ready",
      author_email: currentUserEmail,
      created_at: data.created_at,
      notes: [],
    };
    setFeatures((prev) => [...prev, feature]);
    router.refresh();
  }

  async function updateDescription(featureId: string, description: string) {
    const snapshot = features;
    setFeatures((prev) =>
      prev.map((f) => (f.id === featureId ? { ...f, description } : f)),
    );
    const { error } = await supabase
      .from("features")
      .update({ description })
      .eq("id", featureId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't update the feature. Please try again.");
      return;
    }
    router.refresh();
  }

  async function deleteFeature(featureId: string) {
    const snapshot = features;
    setFeatures((prev) => prev.filter((f) => f.id !== featureId));
    const { error } = await supabase
      .from("features")
      .delete()
      .eq("id", featureId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't delete the feature. Please try again.");
      return;
    }
    router.refresh();
  }

  async function addNote(featureId: string, input: NewNoteInput) {
    try {
      let imagePath: string | null = null;
      if (input.imageFile) {
        const ext =
          input.imageFile.name.split(".").pop()?.toLowerCase() || "png";
        imagePath = `features/${featureId}/${crypto.randomUUID()}.${ext}`;
        const { error: uploadError } = await supabase.storage
          .from(NOTE_BUCKET)
          .upload(imagePath, input.imageFile, {
            contentType: input.imageFile.type || "image/png",
          });
        if (uploadError) throw uploadError;
      }

      const { data, error } = await supabase
        .from("notes")
        .insert({
          feature_id: featureId,
          content: input.content,
          image_path: imagePath,
          category: input.category,
        })
        .select("id, created_at")
        .single();
      if (error) throw error;

      const note: Note = {
        id: data.id,
        page_id: null,
        feature_id: featureId,
        content: input.content,
        image_url: input.imageDataUrl,
        category: input.category,
        resolved: false,
        author_email: currentUserEmail,
        created_at: data.created_at,
        comments: [],
      };
      setFeatures((prev) =>
        prev.map((f) =>
          f.id === featureId ? { ...f, notes: [...f.notes, note] } : f,
        ),
      );
      setDefaultCategory(input.category);
      localStorage.setItem(LAST_CATEGORY_KEY, input.category);
      router.refresh();
    } catch {
      toast.error("Couldn't add the note. Please try again.");
    }
  }

  async function updateNote(noteId: string, patch: NotePatch) {
    const snapshot = features;
    setFeatures((prev) =>
      prev.map((f) => ({
        ...f,
        notes: f.notes.map((n) => (n.id === noteId ? { ...n, ...patch } : n)),
      })),
    );
    const dbPatch: NotePatch & { resolved_at?: string | null } = { ...patch };
    if (patch.resolved !== undefined) {
      dbPatch.resolved_at = patch.resolved ? new Date().toISOString() : null;
    }
    const { error } = await supabase
      .from("notes")
      .update(dbPatch)
      .eq("id", noteId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't update the note. Please try again.");
      return;
    }
    router.refresh();
  }

  async function deleteNote(noteId: string) {
    const snapshot = features;
    setFeatures((prev) =>
      prev.map((f) => ({
        ...f,
        notes: f.notes.filter((n) => n.id !== noteId),
      })),
    );
    const { error } = await supabase.from("notes").delete().eq("id", noteId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't delete the note. Please try again.");
      return;
    }
    router.refresh();
  }

  async function addComment(noteId: string, input: NewCommentInput) {
    try {
      let imagePath: string | null = null;
      if (input.imageFile) {
        const ext =
          input.imageFile.name.split(".").pop()?.toLowerCase() || "png";
        imagePath = `features/${noteId}/${crypto.randomUUID()}.${ext}`;
        const { error: uploadError } = await supabase.storage
          .from(NOTE_BUCKET)
          .upload(imagePath, input.imageFile, {
            contentType: input.imageFile.type || "image/png",
          });
        if (uploadError) throw uploadError;
      }
      const { data, error } = await supabase
        .from("comments")
        .insert({ note_id: noteId, content: input.content, image_path: imagePath })
        .select("id, created_at")
        .single();
      if (error) throw error;

      const comment: Comment = {
        id: data.id,
        note_id: noteId,
        content: input.content,
        image_url: input.imageDataUrl,
        author_email: currentUserEmail,
        created_at: data.created_at,
      };
      setFeatures((prev) =>
        prev.map((f) => ({
          ...f,
          notes: f.notes.map((n) =>
            n.id === noteId ? { ...n, comments: [...n.comments, comment] } : n,
          ),
        })),
      );
      router.refresh();
    } catch {
      toast.error("Couldn't add the comment. Please try again.");
    }
  }

  async function updateComment(commentId: string, content: string) {
    const snapshot = features;
    setFeatures((prev) =>
      prev.map((f) => ({
        ...f,
        notes: f.notes.map((n) => ({
          ...n,
          comments: n.comments.map((c) =>
            c.id === commentId ? { ...c, content } : c,
          ),
        })),
      })),
    );
    const { error } = await supabase
      .from("comments")
      .update({ content })
      .eq("id", commentId);
    if (error) {
      setFeatures(snapshot);
      toast.error("Couldn't update the comment. Please try again.");
      return;
    }
    router.refresh();
  }

  const openFeature = features.find((f) => f.id === openFeatureId) ?? null;
  const activeNote =
    openNoteId !== null
      ? (openFeature?.notes.find((n) => n.id === openNoteId) ?? null)
      : null;

  const q = query.trim().toLowerCase();
  const visibleFeatures = features.filter((f) => {
    if (statusFilter === INCOMPLETE && f.status === "completed") return false;
    if (statusFilter === IN_PROGRESS && !IN_PROGRESS_STATUSES.includes(f.status))
      return false;
    if (
      statusFilter !== ALL &&
      statusFilter !== INCOMPLETE &&
      statusFilter !== IN_PROGRESS &&
      f.status !== statusFilter
    )
      return false;
    if (signoffFilter !== ALL && f.signoff !== signoffFilter) return false;
    if (
      categoryFilter !== ALL &&
      !f.notes.some(
        (n) => n.category === categoryFilter && (showResolved || !n.resolved),
      )
    )
      return false;
    if (q && !f.description.toLowerCase().includes(q)) return false;
    return true;
  });

  const allCleared =
    statusFilter === ALL &&
    signoffFilter === ALL &&
    categoryFilter === ALL &&
    query.trim() === "";

  function resetFilters() {
    setStatusFilter(ALL);
    setSignoffFilter(ALL);
    setCategoryFilter(ALL);
    setQuery("");
  }
  function focusStatus(value: string) {
    resetFilters();
    setStatusFilter(value);
  }
  function focusCategory(category: NoteCategory) {
    resetFilters();
    setCategoryFilter(category);
  }

  const countStatus = (fn: (f: Feature) => boolean) =>
    features.filter(fn).length;
  const countNotes = (category: NoteCategory) =>
    features.reduce(
      (n, f) =>
        n + f.notes.filter((x) => !x.resolved && x.category === category).length,
      0,
    );

  const canEdit = (email: string | null) =>
    email !== null && email === currentUserEmail;

  return (
    <div className="mb-8">
      <div className="mb-3 flex items-center justify-between">
        <h2 className="text-lg font-semibold tracking-tight">Features</h2>
        <Button size="sm" className="gap-1" onClick={() => setAddOpen(true)}>
          <Plus className="size-4" />
          Add Feature
        </Button>
      </div>

      <div className="mb-4 flex flex-wrap gap-2">
        <StatCard
          label="Total features"
          value={features.length}
          dot={null}
          active={allCleared}
          onClick={resetFilters}
        />
        <StatCard
          label="Open"
          value={countStatus((f) => f.status === "open")}
          dot={STATUS_DOT.open}
          active={statusFilter === "open"}
          onClick={() => focusStatus("open")}
        />
        <StatCard
          label="In progress"
          value={countStatus((f) => IN_PROGRESS_STATUSES.includes(f.status))}
          dot={STATUS_DOT.creating}
          active={statusFilter === IN_PROGRESS}
          onClick={() => focusStatus(IN_PROGRESS)}
        />
        <StatCard
          label="Drafted"
          value={countStatus((f) => f.status === "drafted")}
          dot={STATUS_DOT.drafted}
          active={statusFilter === "drafted"}
          onClick={() => focusStatus("drafted")}
        />
        <StatCard
          label="Completed"
          value={countStatus((f) => f.status === "completed")}
          dot={STATUS_DOT.completed}
          active={statusFilter === "completed"}
          onClick={() => focusStatus("completed")}
        />
        {NOTE_CATEGORIES.map((category) => (
          <StatCard
            key={category}
            label={category}
            value={countNotes(category)}
            dot={CATEGORY_DOT[category]}
            active={categoryFilter === category}
            onClick={() => focusCategory(category)}
          />
        ))}
      </div>

      <div className="overflow-hidden rounded-lg border bg-background">
        <Table>
          <TableHeader>
            <TableRow className="bg-muted/40 hover:bg-muted/40">
              <TableHead className="w-[38%] px-4 py-3 align-top">
                <div className="flex flex-col gap-2.5">
                  <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                    Feature
                  </span>
                  <ExpandableSearch
                    value={query}
                    onChange={setQuery}
                    placeholder="Search features…"
                    label="Search features"
                  />
                </div>
              </TableHead>
              <TableHead className="w-[15%] px-4 py-3 align-top">
                <div className="flex flex-col gap-2.5">
                  <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                    Status
                  </span>
                  <EnumFilter
                    ariaLabel="Filter by status"
                    value={statusFilter}
                    options={PAGE_STATUSES}
                    dotClass={STATUS_DOT}
                    onChange={setStatusFilter}
                    extraOptions={[
                      { value: INCOMPLETE, label: "Incomplete" },
                      { value: IN_PROGRESS, label: "In progress" },
                    ]}
                  />
                </div>
              </TableHead>
              <TableHead className="w-[16%] px-4 py-3 align-top">
                <div className="flex flex-col gap-2.5">
                  <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                    Review
                  </span>
                  <EnumFilter
                    ariaLabel="Filter by review state"
                    value={signoffFilter}
                    options={SIGNOFF_OPTIONS}
                    dotClass={SIGNOFF_DOT}
                    onChange={setSignoffFilter}
                  />
                </div>
              </TableHead>
              <TableHead className="px-4 py-3 align-top">
                <div className="flex flex-col gap-2.5">
                  <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
                    Review Notes
                  </span>
                  <div className="flex flex-wrap items-center gap-x-4 gap-y-2">
                    <EnumFilter
                      ariaLabel="Filter notes by type"
                      value={categoryFilter}
                      options={NOTE_CATEGORIES}
                      dotClass={CATEGORY_DOT}
                      onChange={setCategoryFilter}
                      allLabel="All types"
                    />
                    <div className="flex items-center gap-2">
                      <Switch
                        id="feature-show-resolved"
                        checked={showResolved}
                        onCheckedChange={setShowResolved}
                      />
                      <label
                        htmlFor="feature-show-resolved"
                        className="cursor-pointer text-xs font-normal text-muted-foreground"
                      >
                        Show resolved
                      </label>
                    </div>
                  </div>
                </div>
              </TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {visibleFeatures.length === 0 ? (
              <TableRow>
                <TableCell
                  colSpan={4}
                  className="py-10 text-center text-sm text-muted-foreground"
                >
                  {features.length === 0
                    ? "No features yet. Add the first one."
                    : "No features match the current filters."}
                </TableCell>
              </TableRow>
            ) : (
              visibleFeatures.map((feature) => (
                <TableRow key={feature.id} className="align-top">
                  <TableCell className="py-4 whitespace-normal">
                    <FeatureDescription
                      feature={feature}
                      canEdit={canEdit(feature.author_email)}
                      onSave={(description) =>
                        updateDescription(feature.id, description)
                      }
                      onDelete={() => deleteFeature(feature.id)}
                    />
                  </TableCell>
                  <TableCell className="py-4">
                    <PillSelect
                      label="Status"
                      value={feature.status}
                      options={PAGE_STATUSES}
                      pillClass={STATUS_STYLES}
                      dotClass={STATUS_DOT}
                      onSelect={(next) => setStatus(feature.id, next)}
                    />
                  </TableCell>
                  <TableCell className="py-4">
                    <PillSelect
                      label="Review"
                      value={feature.signoff}
                      options={SIGNOFF_OPTIONS}
                      pillClass={SIGNOFF_STYLES}
                      dotClass={SIGNOFF_DOT}
                      onSelect={(next) => setSignoff(feature.id, next)}
                    />
                  </TableCell>
                  <TableCell className="py-4">
                    <NotesCell
                      notes={feature.notes}
                      onOpenNotes={() => {
                        setOpenFeatureId(feature.id);
                        setOpenNoteId(null);
                      }}
                      onOpenThread={(noteId) => {
                        setOpenFeatureId(feature.id);
                        setOpenNoteId(noteId);
                      }}
                      categoryFilter={categoryFilter}
                      showResolved={showResolved}
                    />
                  </TableCell>
                </TableRow>
              ))
            )}
          </TableBody>
        </Table>
      </div>

      <Sheet open={addOpen} onOpenChange={setAddOpen}>
        <SheetContent className="flex w-full flex-col sm:max-w-md">
          <SheetHeader className="border-b">
            <SheetTitle>New feature</SheetTitle>
            <SheetDescription>
              Track a feature through the same statuses as pages.
            </SheetDescription>
          </SheetHeader>
          <div className="flex-1" />
          <SheetFooter className="border-t">
            <label htmlFor="feature-desc" className="text-xs font-medium">
              Description
            </label>
            <Textarea
              id="feature-desc"
              value={addDraft}
              onChange={(e) => setAddDraft(e.target.value)}
              placeholder="Describe the feature… (markdown supported)"
              className="min-h-24 resize-none"
            />
            <div className="flex justify-end">
              <Button
                size="sm"
                disabled={!addDraft.trim()}
                onClick={() => {
                  addFeature(addDraft);
                  setAddDraft("");
                  setAddOpen(false);
                }}
              >
                Add Feature
              </Button>
            </div>
          </SheetFooter>
        </SheetContent>
      </Sheet>

      <NotesSheet
        parent={
          openFeature
            ? {
                id: openFeature.id,
                title: "Feature notes",
                notes: openFeature.notes,
              }
            : null
        }
        activeNote={activeNote}
        open={openFeatureId !== null}
        onOpenChange={(next) => {
          if (!next) {
            setOpenFeatureId(null);
            setOpenNoteId(null);
          }
        }}
        onAddNote={addNote}
        onUpdateNote={updateNote}
        onDeleteNote={deleteNote}
        onAddComment={addComment}
        onUpdateComment={updateComment}
        onOpenThread={(noteId) => setOpenNoteId(noteId)}
        onBack={() => setOpenNoteId(null)}
        currentUserEmail={currentUserEmail}
        defaultCategory={defaultCategory}
        mentionUsers={users}
      />
    </div>
  );
}
