"use client";

import type { Brand, Domain, DomainPurpose, DomainStatus, DomainSummary, GscState, Region, ServerAccount, ServerGroup } from "@domainops/shared-types";
import { App, Badge, Button, Dropdown, Input, Select, Space, Switch, Table, Tag, Tooltip, Typography } from "antd";
import { ClockCircleOutlined, DeleteOutlined, DownOutlined, FlagFilled, FlagOutlined, RightOutlined, InboxOutlined, EditOutlined, CopyOutlined, EllipsisOutlined, EyeOutlined, FormatPainterOutlined, PlusOutlined, ReloadOutlined, SafetyCertificateOutlined, SyncOutlined } from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { deleteDomain, getDomainSummary, listBrands, listRegions, refreshDomainExpiry, unflagDomain, listDomains, syncAllDomains } from "@/lib/api/domains";
import { toggleRobots } from "@/lib/api/seo";
import { ApiError } from "@/lib/api/client";
import { confirmRobotsChange } from "@/lib/utils/robotsConfirm";
import { DEFAULT_PAGE_SIZE, serverPaginationConfig, useServerPagination } from "@/lib/hooks/useServerPagination";
import { useAuth } from "@/lib/auth/AuthProvider";
import { titleCase } from "@/lib/utils/text";
import { formatShortDate } from "@/lib/utils/date";
import { cloudflareName, providerName, purposeName } from "@/lib/utils/domainFields";
import { HeldAtTags } from "@/components/features/HeldAtTags";
import { expiryInfo } from "@/lib/utils/expiry";
import { listServerAccounts, listServerGroups } from "@/lib/api/servers";
import { buildServerFilterOptions, serverFilterParams } from "@/lib/utils/serverChoice";
import { SORT_DIRECTIONS } from "@/lib/utils/sort";
import { DOMAIN_STATUS_LABELS, DomainStatusTag } from "@/components/features/DomainStatusTag";
import { PageGuide } from "@/components/features/PageGuide";
import { DOMAIN_INVENTORY_GUIDES } from "@/lib/guides/domains";
import { AddDomainModal } from "./AddDomainModal";
import { BulkAssignBar } from "./BulkAssignBar";
import { BulkSeoBar } from "./BulkSeoBar";
import { EditDomainModal } from "./EditDomainModal";
import { GscQuickModal } from "./GscQuickModal";
import { ArchiveDomainModal } from "./ArchiveDomainModal";
import { FlagDomainModal } from "./FlagDomainModal";
import { purgeCache } from "@/lib/api/cloudflare";
import { SubdomainsTable } from "./SubdomainsTable";

const GSC_COLORS: Record<GscState, string> = {
  not_started: "default",
  pending: "gold",
  verified: "green",
};

type ExpiryFilter = { expiry: "expired" | "expiring" | "attention"; days: number; label: string };

const EXPIRY_FILTERS: Record<string, ExpiryFilter> = {
  expired: { expiry: "expired", days: 30, label: "Expired" },
  expiring_30: { expiry: "expiring", days: 30, label: "Expiring in 30 days" },
  expiring_60: { expiry: "expiring", days: 60, label: "Expiring in 60 days" },
  expiring_90: { expiry: "expiring", days: 90, label: "Expiring in 90 days" },
  attention_90: { expiry: "attention", days: 90, label: "Expired + expiring in 90 days" },
};

const PURPOSE_OPTIONS: { value: DomainPurpose; label: string }[] = [
  { value: "html", label: "HTML Site" },
  { value: "wordpress", label: "WordPress" },
  { value: "redirect", label: "Redirect" },
  { value: "other", label: "Other" },
];

const TLD_OPTIONS = [".com", ".net", ".org", ".io", ".co", ".biz", ".info", ".online", ".app", ".xyz"];

const STATUS_OPTIONS: { value: DomainStatus; label: string }[] = (
  Object.entries(DOMAIN_STATUS_LABELS) as [DomainStatus, string][]
).map(([value, label]) => ({ value, label }));

export default function DomainsPage() {
  const router = useRouter();
  const { message, modal } = App.useApp();
  const { user: currentUser } = useAuth();
  const [domains, setDomains] = useState<Domain[]>([]);
  const [brands, setBrands] = useState<Brand[]>([]);
  const [total, setTotal] = useState(0);
  const { page, pageSize, setPage, onChange: onPageChange } = useServerPagination(DEFAULT_PAGE_SIZE, "domains");
  const [search, setSearch] = useState("");
  const [status, setStatus] = useState<DomainStatus | undefined>();
  const [brandId, setBrandId] = useState<number | undefined>();
  const [regionId, setRegionId] = useState<number | undefined>();
  const [regions, setRegions] = useState<Region[]>([]);
  const [refreshingExpiryId, setRefreshingExpiryId] = useState<number | null>(null);
  const [gscState, setGscState] = useState<GscState | undefined>();
  const [tld, setTld] = useState<string | undefined>();
  const [serverFilter, setServerFilter] = useState<number | string | undefined>();
  const [domainKind, setDomainKind] = useState<"main" | "subdomain" | undefined>();
  const [purpose, setPurpose] = useState<DomainPurpose | undefined>();
  const [serverAccounts, setServerAccounts] = useState<ServerAccount[]>([]);
  const [serverGroups, setServerGroups] = useState<ServerGroup[]>([]);
  const [summary, setSummary] = useState<DomainSummary | null>(null);
  const [sortField, setSortField] = useState<string | undefined>();
  const [sortOrder, setSortOrder] = useState<"ascend" | "descend" | undefined>();
  const [expiryKey, setExpiryKey] = useState<string | undefined>();
  const expiryFilter = expiryKey ? EXPIRY_FILTERS[expiryKey] : undefined;
  const [noBrandOnly, setNoBrandOnly] = useState(false);
  const [missingBrandCount, setMissingBrandCount] = useState(0);
  const [isLoading, setIsLoading] = useState(true);
  const [isAddOpen, setIsAddOpen] = useState(false);
  const [editingDomain, setEditingDomain] = useState<Domain | null>(null);
  const [gscDomain, setGscDomain] = useState<Domain | null>(null);
  const [selectedIds, setSelectedIds] = useState<number[]>([]);
  const [isSyncingAll, setIsSyncingAll] = useState(false);
  const [togglingRobotsId, setTogglingRobotsId] = useState<number | null>(null);
  const [archivingDomain, setArchivingDomain] = useState<Domain | null>(null);
  const [flaggingDomain, setFlaggingDomain] = useState<Domain | null>(null);
  const [purgingCacheId, setPurgingCacheId] = useState<number | null>(null);
  const [expandedKeys, setExpandedKeys] = useState<number[]>([]);

  const load = useCallback(async () => {
    setIsLoading(true);
    try {
      const [result, nextSummary] = await Promise.all([
        listDomains({
        search,
        status,
        brand_id: brandId,
        region_id: regionId,
        no_brand: noBrandOnly || undefined,
        gsc_state: gscState,
        tld,
        expiry: expiryFilter?.expiry,
        expiry_days: expiryFilter?.days,
        ...serverFilterParams(serverFilter),
        purpose,
        domain_kind: domainKind,
        sort: sortField,
        order: sortOrder,
        page,
        per_page: pageSize,
      }),
        getDomainSummary(),
      ]);
      setSummary(nextSummary);
      setDomains(result.data);
      setTotal(result.meta.total);
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't load domains.");
    } finally {
      setIsLoading(false);
    }
  }, [search, status, brandId, regionId, noBrandOnly, gscState, tld, expiryFilter, serverFilter, purpose, domainKind, sortField, sortOrder, page, pageSize, message]);

  const refreshMissingBrandCount = useCallback(async () => {
    try {
      const result = await listDomains({ no_brand: true, page: 1 });
      setMissingBrandCount(result.meta.total);
    } catch {
      // Non-critical - the badge just stays at its last known value.
    }
  }, []);

  useEffect(() => {
    // Inventory lives on the Laravel API, not in Next's own data layer -
    // a client fetch on mount/filter-change, not a state-sync bug.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void load();
  }, [load]);

  useEffect(() => {
    void listBrands().then(setBrands);
    void listRegions().then(setRegions);
    void listServerAccounts().then(setServerAccounts);
    void listServerGroups().then(setServerGroups);
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void refreshMissingBrandCount();
  }, [refreshMissingBrandCount]);

  const runSync = useCallback(
    async (showSummary: boolean) => {
      setIsSyncingAll(true);
      try {
        const result = await syncAllDomains();

        if (showSummary) {
          const lines = [
            ...result.registrars.map(
              (r) => `Registrar ${r.label}: ${r.error ?? `found ${r.domains_found}, added ${r.domains_added}`}`,
            ),
            ...result.cloudflare.map(
              (c) =>
                `Cloudflare ${c.label}: ${
                  c.error ??
                  `linked ${c.domains_linked}, added ${c.domains_added} (${c.gsc_records} Search Console record(s) found), against ${c.zones} zone(s)`
                }`,
            ),
            ...result.servers.map(
              (s) => `Server ${s.label}: ${s.error ?? `found ${s.accounts_found}, added ${s.accounts_added}`}`,
            ),
          ];
          modal.info({
            title: "Sync All Finished",
            content: lines.length ? (
              <ul className="list-disc pl-4">
                {lines.map((line, i) => (
                  <li key={i}>{line}</li>
                ))}
              </ul>
            ) : (
              "No registrar, Cloudflare, or server accounts configured yet."
            ),
            width: 520,
          });
        }

        void load();
        void refreshMissingBrandCount();
      } catch (err) {
        // Silent on the automatic background run - the page still shows
        // whatever's already in inventory, and a manual "Sync All" click
        // always surfaces the real error.
        if (showSummary) message.error(err instanceof ApiError ? err.message : "Couldn't sync.");
      } finally {
        setIsSyncingAll(false);
      }
    },
    [load, refreshMissingBrandCount, message, modal],
  );

  async function handleSyncAll() {
    await runSync(true);
  }

  async function handleUnflag(domain: Domain) {
    try {
      const updated = await unflagDomain(domain.id);
      setDomains((current) => current.map((d) => (d.id === domain.id ? { ...d, ...updated } : d)));
      message.success("Flag cleared.");
      void load();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't clear that flag.");
    }
  }

  async function handlePurgeCache(domain: Domain) {
    setPurgingCacheId(domain.id);
    try {
      await purgeCache(domain.id);
      message.success(`Cloudflare cache cleared for ${domain.fqdn}.`);
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't clear the Cloudflare cache.");
    } finally {
      setPurgingCacheId(null);
    }
  }

  function confirmUnflag(domain: Domain) {
    modal.confirm({ title: "Clear this flag?", okText: "Clear flag", onOk: () => handleUnflag(domain) });
  }

  function confirmDelete(domain: Domain) {
    modal.confirm({
      title: "Permanently delete this domain?",
      content: "This cannot be undone. Its subdomains and redirect history are deleted too.",
      okText: "Delete",
      okButtonProps: { danger: true },
      onOk: () => handleDelete(domain),
    });
  }

  async function handleDelete(domain: Domain) {
    try {
      await deleteDomain(domain.id);
      message.success(`${domain.fqdn} permanently deleted.`);
      void load();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't delete that domain.");
    }
  }

  async function handleToggleRobots(domain: Domain, index: boolean) {
    setTogglingRobotsId(domain.id);
    try {
      const updated = await toggleRobots(domain.id, index);
      setDomains((current) => current.map((d) => (d.id === updated.id ? updated : d)));
      message.success(index ? "Set to index." : "Set to noindex.");
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't change that domain's robots state.");
    } finally {
      setTogglingRobotsId(null);
    }
  }

  useEffect(() => {
    // Arriving from Brands ("/domains?brand_id=3") pre-selects that brand - read once after mount.
    const query = new URLSearchParams(window.location.search);
    const brandFromUrl = Number(query.get("brand_id"));
    const regionFromUrl = Number(query.get("region_id"));
    // eslint-disable-next-line react-hooks/set-state-in-effect
    if (brandFromUrl > 0) setBrandId(brandFromUrl);
    if (regionFromUrl > 0) setRegionId(regionFromUrl);
  }, []);

  async function handleCopyDomain(fqdn: string) {
    try {
      await navigator.clipboard.writeText(fqdn);
      message.success(`${fqdn} copied.`);
    } catch {
      message.error("Couldn't copy to the clipboard.");
    }
  }

  async function handleRefreshExpiry(domain: Domain) {
    setRefreshingExpiryId(domain.id);
    try {
      const updated = await refreshDomainExpiry(domain.id);
      message.success(`Expiry fetched from ${updated.expiry_source === "whois" ? "WHOIS" : "the registrar"}.`);
      void load();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't fetch the expiry date.");
    } finally {
      setRefreshingExpiryId(null);
    }
  }

  const columns: ColumnsType<Domain> = [
    {
      title: "Domain",
      width: 220,
      dataIndex: "fqdn",
      sorter: true,
      sortOrder: sortField === "fqdn" ? sortOrder : null,
      sortDirections: SORT_DIRECTIONS,
      render: (fqdn: string, domain) => {
        const subdomainCount = domain.subdomains?.length ?? 0;
        const isExpanded = expandedKeys.includes(domain.id);

        return (
          <div className="dms_domains_name_cell">
            <Tooltip title="Open in a new tab">
              <a href={`https://${fqdn}`} target="_blank" rel="noopener noreferrer" className="font-mono dms_domains_name_link">
                {fqdn}
              </a>
            </Tooltip>
            <div className="flex items-center gap-1 mt-1 dms_domains_name_tools">
              <Tooltip title="Copy domain">
                <Button size="small" type="text" icon={<CopyOutlined />} onClick={() => void handleCopyDomain(fqdn)} className="dms_domains_copy_btn" />
              </Tooltip>
              {subdomainCount > 0 && (
                <Tooltip title={isExpanded ? "Hide subdomains" : `Show ${subdomainCount} subdomain${subdomainCount === 1 ? "" : "s"}`}>
                  <Button
                    size="small"
                    type="text"
                    icon={isExpanded ? <DownOutlined /> : <RightOutlined />}
                    onClick={() =>
                      setExpandedKeys((keys) => (isExpanded ? keys.filter((k) => k !== domain.id) : [...keys, domain.id]))
                    }
                    className="dms_domains_expand_btn"
                  >
                    {subdomainCount}
                  </Button>
                </Tooltip>
              )}
            </div>
            {domain.notes && (
              <div className="text-xs mt-1 opacity-75 dms_domains_notes" title={domain.notes}>
                {domain.notes}
              </div>
            )}
            {domain.flagged_at && domain.flag_notes && (
              <div className="text-xs mt-1 dms_domains_flag_note" style={{ color: "#cf1322" }}>
                <FlagFilled /> {domain.flag_notes}
              </div>
            )}
          </div>
        );
      },
    },
    {
      title: "Project",
      className: "dms_col_small",
      render: (_, domain) => <span>{domain.brand?.name ?? "—"}</span>,
    },
    {
      title: "Brand",
      className: "dms_col_small",
      render: (_, domain) => <span>{domain.project_brand?.name ?? "—"}</span>,
    },
    {
      title: "Region",
      className: "dms_col_small",
      dataIndex: "region",
      render: (region: Domain["region"]) => (region ? region.country_code : "—"),
    },
    { title: "Provider", className: "dms_col_small", render: (_, domain) => providerName(domain) },
    { title: "Cloudflare", className: "dms_col_small", render: (_, domain) => cloudflareName(domain) },
    { title: "Held At", render: (_, domain) => <HeldAtTags account={domain.server_account} /> },
    { title: "Type", className: "dms_col_small", render: (_, domain) => purposeName(domain) },
    {
      title: "Expires",
      dataIndex: "expires_at",
      sorter: true,
      sortOrder: sortField === "expires_at" ? sortOrder : null,
      sortDirections: SORT_DIRECTIONS,
      render: (v: string | null, domain) => {
        const info = expiryInfo(v);

        return (
          <span className="flex items-center gap-1 dms_domains_expiry_cell" style={{ color: info?.color }}>
            {formatShortDate(v)}
            <Tooltip title={v ? "Re-fetch the expiry date" : "Fetch the expiry date"}>
              <Button
                size="small"
                type="text"
                icon={<ReloadOutlined />}
                loading={refreshingExpiryId === domain.id}
                onClick={() => void handleRefreshExpiry(domain)}
                className="dms_domains_refresh_expiry_btn"
              />
            </Tooltip>
          </span>
        );
      },
    },
    {
      title: "Robots",
      dataIndex: "robots_state",
      render: (value: Domain["robots_state"], domain) => (
        <Tooltip title={value === "index" ? "Set to noindex" : "Set to index"}>
          <Switch
            size="small"
            checked={value === "index"}
            loading={togglingRobotsId === domain.id}
            onChange={(checked) => confirmRobotsChange(modal, domain.fqdn, checked, () => handleToggleRobots(domain, checked))}
            className="dms_domains_robots_toggle"
          />
        </Tooltip>
      ),
    },
    {
      title: "Status",
      dataIndex: "status",
      render: (value: DomainStatus) => <DomainStatusTag status={value} />,
    },
    {
      title: "GSC",
      className: "dms_col_small",
      dataIndex: "gsc_state",
      render: (value: GscState) => <Tag color={GSC_COLORS[value]}>{titleCase(value.replace("_", " "))}</Tag>,
    },
    {
      title: "Actions",
      render: (_, domain) => (
        <div className="flex items-center gap-2">
          <Tooltip title="View domain">
            <Button size="small" icon={<EyeOutlined />} onClick={() => router.push(`/domains/${domain.id}`)} aria-label="View domain" className="dms_domains_view_btn" />
          </Tooltip>
          <Tooltip title="Edit">
            <Button size="small" icon={<EditOutlined />} onClick={() => setEditingDomain(domain)} className="dms_domains_edit_btn" />
          </Tooltip>
          <Tooltip title={domain.cf_zone_id ? "Clear the Cloudflare cache for this whole domain" : "No Cloudflare zone yet"}>
            <Button
              size="small"
              icon={<FormatPainterOutlined />}
              disabled={!domain.cf_zone_id}
              loading={purgingCacheId === domain.id}
              onClick={() => void handlePurgeCache(domain)}
              className="dms_domains_cloudflare_btn"
            />
          </Tooltip>
          <Tooltip title="Add GSC / check verification">
            <Button size="small" icon={<SafetyCertificateOutlined />} onClick={() => setGscDomain(domain)} className="dms_domains_add_gsc_btn" />
          </Tooltip>
          <Dropdown
            trigger={["click"]}
            menu={{
              items: [
                domain.flagged_at
                  ? { key: "unflag", icon: <FlagFilled />, label: "Clear flag", onClick: () => confirmUnflag(domain) }
                  : { key: "flag", icon: <FlagOutlined />, label: "Flag a concern", onClick: () => setFlaggingDomain(domain) },
                { key: "archive", icon: <InboxOutlined />, label: "Archive", onClick: () => setArchivingDomain(domain) },
                ...(currentUser?.is_crowned_or_above
                  ? [{ key: "delete", icon: <DeleteOutlined />, label: "Delete permanently", danger: true, onClick: () => confirmDelete(domain) }]
                  : []),
              ],
            }}
          >
            <Button size="small" icon={<EllipsisOutlined />} aria-label="More actions" className="dms_domains_more_btn" />
          </Dropdown>
        </div>
      ),
    },
  ];

  return (
    <div className="flex flex-col gap-4 page page-domains">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-3">
          <Typography.Title level={4} style={{ margin: 0 }}>
            Domain Inventory
          </Typography.Title>
          <PageGuide guides={DOMAIN_INVENTORY_GUIDES} label="Domain Inventory guide" />
          {missingBrandCount > 0 && (
            <Tooltip title="Domains synced in without a project assigned yet - toggle 'Missing project' below to triage them.">
              <Badge count={missingBrandCount} color="gold" overflowCount={999} />
            </Tooltip>
          )}
        </div>
        <div className="flex gap-2">
          <Tooltip title="Re-pull domains, Cloudflare zones, and cPanel accounts from every configured account right now. This also runs quietly in the background when a Cloudflare account is added or a domain is bought.">
            <Button
              icon={<SyncOutlined />}
              loading={isSyncingAll}
              onClick={handleSyncAll}
              className="dms_domains_sync_all_btn"
            >
              Sync Now
            </Button>
          </Tooltip>
          <Button
            type="primary"
            icon={<PlusOutlined />}
            onClick={() => setIsAddOpen(true)}
            className="dms_domains_add_domain_btn"
          >
            Add Domain
          </Button>
        </div>
      </div>

      <div className="dms_domains_summary">
        <div className="dms_summary_card dms_summary_total">
          <small>Total domains</small>
          <b>{summary?.total ?? "—"}</b>
          <em>{summary ? `+ ${summary.subdomains} subdomain${summary.subdomains === 1 ? "" : "s"}` : ""}</em>
        </div>
        <Tooltip title="Live, Redirected or Deploying - doing something right now">
          <div className="dms_summary_card dms_summary_using">
            <small>Using</small>
            <b>{summary?.in_use ?? "—"}</b>
            <em>Live, redirected, deploying</em>
          </div>
        </Tooltip>
        <Tooltip title="Parked or still being set up - not serving anything yet">
          <div className="dms_summary_card dms_summary_unused">
            <small>Unused</small>
            <b>{summary?.unused ?? "—"}</b>
            <em>Parked or setting up</em>
          </div>
        </Tooltip>
      </div>

      <div className="dms_domains_filters">
        <Input.Search
          placeholder="Search domains…"
          allowClear
          onSearch={(value) => {
            setPage(1);
            setSearch(value);
          }}
        />
        <Select
          allowClear
          placeholder="Status"
          options={STATUS_OPTIONS}
          onChange={(value) => {
            setPage(1);
            setStatus(value);
          }}
        />
        <Select
          allowClear
          placeholder="Project"
          value={brandId}
          options={brands.map((b) => ({ value: b.id, label: b.name }))}
          onChange={(value) => {
            setPage(1);
            setBrandId(value);
          }}
        />
        <Select
          allowClear
          showSearch
          optionFilterProp="label"
          placeholder="Region"
          value={regionId}
          options={regions.map((r) => ({ value: r.id, label: `${r.name} (${r.country_code})` }))}
          onChange={(value) => {
            setPage(1);
            setRegionId(value);
          }}
        />
        <Select
          allowClear
          showSearch
          optionFilterProp="label"
          placeholder="Server"
          options={buildServerFilterOptions(serverAccounts, serverGroups)}
          onChange={(value) => {
            setPage(1);
            setServerFilter(value);
          }}
        />
        <Select
          allowClear
          placeholder="Domain"
          options={[
            { value: "main", label: "Main Domains" },
            { value: "subdomain", label: "Subdomains" },
          ]}
          onChange={(value) => {
            setPage(1);
            setDomainKind(value);
          }}
        />
        <Select
          allowClear
          placeholder="Type"
          options={PURPOSE_OPTIONS}
          onChange={(value) => {
            setPage(1);
            setPurpose(value);
          }}
        />
        <Select
          allowClear
          placeholder="TLD"
          options={TLD_OPTIONS.map((t) => ({ value: t, label: t }))}
          onChange={(value) => {
            setPage(1);
            setTld(value);
          }}
        />
        <Select
          allowClear
          placeholder="GSC state"
          options={[
            { value: "not_started", label: "Not Started" },
            { value: "pending", label: "Pending" },
            { value: "verified", label: "Verified" },
          ]}
          onChange={(value) => {
            setPage(1);
            setGscState(value);
          }}
        />
      </div>

      <Space wrap>
        <Dropdown
          menu={{
            items: [
              ...Object.entries(EXPIRY_FILTERS).map(([key, f]) => ({ key, label: f.label })),
              ...(expiryKey ? [{ type: "divider" as const }, { key: "clear", label: "Clear expiry filter" }] : []),
            ],
            selectedKeys: expiryKey ? [expiryKey] : [],
            onClick: ({ key }) => {
              setPage(1);
              setExpiryKey(key === "clear" ? undefined : key);
            },
          }}
        >
          <Button
            icon={<ClockCircleOutlined />}
            type={expiryKey ? "primary" : "default"}
            className="dms_domains_expiry_check_btn"
          >
            {expiryFilter ? expiryFilter.label : "Check Expired / Expiring"}
          </Button>
        </Dropdown>
        <div className="flex items-center gap-2">
          <Switch
            checked={noBrandOnly}
            onChange={(checked) => {
              setPage(1);
              setNoBrandOnly(checked);
            }}
          />
          <Typography.Text>Missing project only</Typography.Text>
        </div>
        <BulkSeoBar
          selectedIds={selectedIds}
          filters={{ search: search || undefined, status, brand_id: brandId, gsc_state: gscState }}
          totalFiltered={total}
          onDone={() => {
            setSelectedIds([]);
            void load();
          }}
        />
        <BulkAssignBar
          selectedIds={selectedIds}
          filters={{
            search: search || undefined,
            status,
            brand_id: brandId,
            region_id: regionId,
            no_brand: noBrandOnly || undefined,
            gsc_state: gscState,
            ...serverFilterParams(serverFilter),
            purpose,
          }}
          totalFiltered={total}
          brands={brands}
          onBrandCreated={() => void listBrands().then(setBrands)}
          onDone={() => {
            setSelectedIds([]);
            void load();
            void refreshMissingBrandCount();
          }}
        />
      </Space>

      <Table<Domain>
        rowKey="id"
        columns={columns}
        dataSource={domains}
        loading={isLoading}
        rowSelection={{
          selectedRowKeys: selectedIds,
          onChange: (keys) => setSelectedIds(keys as number[]),
        }}
        rowClassName={(domain) => (domain.flagged_at ? "dms_domains_row_flagged" : "")}
        expandable={{
          showExpandColumn: false,
          expandedRowKeys: expandedKeys,
          rowExpandable: (domain) => (domain.subdomains?.length ?? 0) > 0,
          expandedRowRender: (domain) => (
            <SubdomainsTable subdomains={domain.subdomains ?? []} onEdit={setEditingDomain} onArchive={setArchivingDomain} />
          ),
        }}
        onChange={(_pagination, _filters, sorter, extra) => {
          if (extra.action !== "sort") return;
          const active = Array.isArray(sorter) ? sorter[0] : sorter;
          setPage(1);
          setSortField(active?.order ? String(active.field) : undefined);
          setSortOrder(active?.order ?? undefined);
        }}
        scroll={{ x: "max-content" }}
        pagination={serverPaginationConfig(total, page, pageSize, onPageChange, "domains")}
        className="dms_domains_table"
      />

      <ArchiveDomainModal
        domain={archivingDomain}
        onClose={() => setArchivingDomain(null)}
        onArchived={() => {
          setArchivingDomain(null);
          void load();
        }}
      />
      <FlagDomainModal
        domain={flaggingDomain}
        onClose={() => setFlaggingDomain(null)}
        onFlagged={() => {
          setFlaggingDomain(null);
          void load();
        }}
      />
      <AddDomainModal
        open={isAddOpen}
        onClose={() => setIsAddOpen(false)}
        onCreated={() => {
          setIsAddOpen(false);
          void load();
        }}
      />

      <EditDomainModal
        open={!!editingDomain}
        domain={editingDomain}
        onClose={() => setEditingDomain(null)}
        onSaved={() => {
          setEditingDomain(null);
          void load();
        }}
      />

      <GscQuickModal
        open={!!gscDomain}
        domain={gscDomain}
        onClose={() => setGscDomain(null)}
        onSaved={(updated) => {
          setDomains((current) => current.map((d) => (d.id === updated.id ? updated : d)));
        }}
      />
    </div>
  );
}
