"use client";

import { useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Check, Plus, Tag as TagIcon } from "lucide-react";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/app/shadcn_components/ui/popover";
import { PropertyWithRelations, PropertyTag } from "@/app/db/schema/property";
import { tagDotColour } from "@/lib/tags/colour";
import { TagChip } from "./TagChip";
import {
  Tag,
  usePortfolioTags,
  portfolioTagsKey,
} from "./useTags";
import type { PropertiesResponse } from "./useProperties";

/** How many chips to show inline before collapsing the rest into "+N". */
const MAX_VISIBLE_CHIPS = 2;

type AssignVars = { tag: Tag; action: "add" | "remove" };

/**
 * The property table's Tags cell (ADR-0013): shows the property's Tags as chips
 * with a "+N" overflow, and an assign popover that toggles membership against
 * POST …/assignments {mode:"properties"}. Writes are optimistic across every
 * cached property-table query for this portfolio, reconciled on settle.
 */
export function TagsCell({ property }: { property: PropertyWithRelations }) {
  const portfolioId = String(property.portfolioId);
  const propertyId = String(property.id);
  const tags = property.tags ?? [];

  const [open, setOpen] = useState(false);
  const queryClient = useQueryClient();
  const { data: allTags = [], isLoading, isError } = usePortfolioTags(portfolioId);

  const assignedIds = new Set(tags.map((t) => t.id));

  const assign = useMutation<unknown, Error, AssignVars, { snapshot: [readonly unknown[], PropertiesResponse | undefined][] }>({
    mutationFn: async ({ tag, action }) => {
      const res = await fetch(
        `/api/portfolio/${portfolioId}/tags/${tag.id}/assignments`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            mode: "properties",
            action,
            propertyIds: [propertyId],
          }),
        },
      );
      if (!res.ok) {
        const body = await res.json().catch(() => ({}));
        throw new Error(body.error ?? "Couldn't update tags");
      }
      return res.json();
    },
    // Optimistically add/remove the chip on every cached property-table page.
    onMutate: async ({ tag, action }) => {
      await queryClient.cancelQueries({ queryKey: ["properties", portfolioId] });
      const snapshot = queryClient.getQueriesData<PropertiesResponse>({
        queryKey: ["properties", portfolioId],
      });
      const chip: PropertyTag = { id: tag.id, name: tag.name, colour: tag.colour };
      queryClient.setQueriesData<PropertiesResponse>(
        { queryKey: ["properties", portfolioId] },
        (prev) =>
          prev
            ? {
                ...prev,
                data: prev.data.map((row) =>
                  String(row.id) === propertyId
                    ? { ...row, tags: nextTags(row.tags ?? [], chip, action) }
                    : row,
                ),
              }
            : prev,
      );
      return { snapshot };
    },
    onError: (_e, _vars, context) => {
      context?.snapshot.forEach(([key, data]) =>
        queryClient.setQueryData(key, data),
      );
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ["properties", portfolioId] });
      queryClient.invalidateQueries({ queryKey: portfolioTagsKey(portfolioId) });
    },
  });

  const visible = tags.slice(0, MAX_VISIBLE_CHIPS);
  const overflow = tags.length - visible.length;

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <button
          className="group flex min-h-[1.75rem] max-w-[13rem] flex-wrap items-center gap-1 rounded-lg px-1 py-0.5 text-left transition hover:bg-slate-50"
          title="Edit tags"
        >
          {tags.length === 0 ? (
            <span className="inline-flex items-center gap-1 rounded-full border border-dashed border-slate-300 px-2 py-0.5 text-[10px] font-semibold text-slate-400 group-hover:border-slate-400 group-hover:text-slate-600">
              <Plus className="h-3 w-3" />
              Tag
            </span>
          ) : (
            <>
              {visible.map((t) => (
                <TagChip key={t.id} name={t.name} colour={t.colour} size="xs" />
              ))}
              {overflow > 0 && (
                <span className="inline-flex items-center rounded-full bg-slate-100 px-1.5 py-0.5 text-[10px] font-bold text-slate-500">
                  +{overflow}
                </span>
              )}
            </>
          )}
        </button>
      </PopoverTrigger>
      <PopoverContent align="start" className="w-64 p-0">
        <div className="border-b border-slate-100 px-3 py-2">
          <p className="text-xs font-bold text-slate-700">Assign tags</p>
          <p className="truncate text-[11px] text-slate-400">
            {property.address ?? "This property"}
          </p>
        </div>
        <div className="max-h-64 overflow-y-auto p-1">
          {isLoading ? (
            <p className="px-2 py-3 text-xs text-slate-400">Loading tags…</p>
          ) : isError ? (
            <p className="px-2 py-3 text-xs text-red-500">Couldn&apos;t load tags.</p>
          ) : allTags.length === 0 ? (
            <div className="px-2 py-3 text-xs text-slate-400">
              No tags yet.{" "}
              <a
                href={`/portfolio/${portfolioId}/settings/tags`}
                className="font-semibold text-brandmidblue hover:underline"
              >
                Create one
              </a>
              .
            </div>
          ) : (
            allTags.map((t) => {
              const on = assignedIds.has(t.id);
              return (
                <button
                  key={t.id}
                  type="button"
                  onClick={() =>
                    assign.mutate({ tag: t, action: on ? "remove" : "add" })
                  }
                  className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition hover:bg-slate-50"
                >
                  <span
                    className="h-3 w-3 shrink-0 rounded-full border"
                    style={{ backgroundColor: tagDotColour(t.colour) }}
                  />
                  <span className="flex-1 truncate text-xs text-slate-700">
                    {t.name}
                  </span>
                  <span
                    className={`flex h-4 w-4 shrink-0 items-center justify-center rounded border transition ${
                      on
                        ? "border-brandmidblue bg-brandmidblue text-white"
                        : "border-slate-300"
                    }`}
                  >
                    {on && <Check className="h-2.5 w-2.5" />}
                  </span>
                </button>
              );
            })
          )}
        </div>
        <div className="flex items-center justify-between border-t border-slate-100 px-3 py-2">
          <a
            href={`/portfolio/${portfolioId}/settings/tags`}
            className="inline-flex items-center gap-1 text-[11px] font-semibold text-slate-500 hover:text-brandmidblue"
          >
            <TagIcon className="h-3 w-3" />
            Manage tags
          </a>
          {assign.isError && (
            <span className="text-[11px] text-red-500">{assign.error.message}</span>
          )}
        </div>
      </PopoverContent>
    </Popover>
  );
}

/** Add or remove a chip from a property's tag list, keeping it name-ordered. */
function nextTags(
  current: PropertyTag[],
  chip: PropertyTag,
  action: "add" | "remove",
): PropertyTag[] {
  if (action === "remove") return current.filter((t) => t.id !== chip.id);
  if (current.some((t) => t.id === chip.id)) return current;
  return [...current, chip].sort((a, b) => a.name.localeCompare(b.name));
}
