"use client";

import type { Role } from "@domainops/shared-types";
import type { ThemeSettings } from "@domainops/shared-types";
import { App, Select, Switch, Table, Tag, Typography } from "antd";
import { useRouter } from "next/navigation";
import type { ColumnsType } from "antd/es/table";
import { useCallback, useEffect, useState } from "react";
import { listRoles, updateRoleTabs } from "@/lib/api/users";
import { updateTheme } from "@/lib/api/theme";
import { useThemeSettings } from "@/lib/theme/ThemeProvider";
import { ApiError } from "@/lib/api/client";
import { titleCase } from "@/lib/utils/text";

/**
 * Keep in sync with ALL_NAV_ITEMS in ../layout.tsx - each `key` here is the
 * `tabKey` a sidebar item checks via User::canAccessTab()/hasTab(). Buy
 * Domains/Auctions/Users also still need their own action permission
 * (managed on the Users page's "Manage Feature Access") - turning a tab off
 * here hides the item regardless, but turning it on doesn't grant the
 * underlying feature by itself.
 */
type NavLayout = ThemeSettings["nav_layout"];

const NAV_LAYOUT_OPTIONS: { value: NavLayout; label: string; description: string }[] = [
  { value: "top", label: "Top Bar", description: "The menu runs across the top of every page, leaving the full width for content." },
  { value: "sidebar", label: "Left Sidebar", description: "The menu stays pinned down the left side, with sub-pages always one click away." },
];

const SIDEBAR_ITEMS: { key: string; label: string }[] = [
  { key: "dashboard", label: "Dashboard" },
  { key: "registrars", label: "Registrars" },
  { key: "acquire", label: "Buy Domains" },
  { key: "auctions", label: "Auctions" },
  { key: "domains", label: "Domains" },
  { key: "wordpress", label: "WordPress" },
  { key: "servers", label: "Servers" },
  { key: "seo", label: "SEO Controls" },
  { key: "cloudflare", label: "Cloudflare" },
  { key: "logs", label: "Activity Log" },
  { key: "users", label: "Users" },
];

interface Row {
  key: string;
  label: string;
}

/**
 * Crowned-Admin-or-above only - see RoleController::update()'s docblock. Controls
 * which items show in the sidebar per role (Role.default_tab_permissions),
 * separate from Manage Feature Access's action permissions. The protected
 * super admin role always sees everything (User::canAccessTab() bypasses
 * this entirely for is_admin), so it's shown here as a fixed "Always on"
 * column, not an editable one - A Crowned Admin is NOT protected (by design, see
 * User::isCrownedOrAbove()'s docblock), so it shows as a normal editable
 * column even though it starts with every item on.
 */
export function SidebarTab() {
  const { message } = App.useApp();
  const [roles, setRoles] = useState<Role[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [savingCell, setSavingCell] = useState<string | null>(null);
  const theme = useThemeSettings();
  const router = useRouter();
  const [navLayout, setNavLayout] = useState<NavLayout>(theme.nav_layout);
  const [isSavingLayout, setIsSavingLayout] = useState(false);

  const load = useCallback(async () => {
    setIsLoading(true);
    try {
      setRoles(await listRoles());
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't load roles.");
    } finally {
      setIsLoading(false);
    }
  }, [message]);

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void load();
  }, [load]);

  const editableRoles = roles.filter((r) => !r.is_protected);

  // A role's default_tab_permissions can be the literal ["*"] (e.g. a Crowned Admin,
  // seeded with full access) rather than an explicit list of item keys -
  // canAccessTab() treats that as "everything", but a plain .includes(key)
  // check here would show every switch off and toggling one off would
  // silently no-op (nothing removes "*" itself). Expanding it to the full
  // item list up front means the switches reflect reality, and the first
  // edit crystallizes "*" into an explicit list from then on.
  function resolvedPermissions(role: Role): string[] {
    const current = role.default_tab_permissions ?? [];
    return current.includes("*") ? SIDEBAR_ITEMS.map((item) => item.key) : current;
  }

  async function handleLayoutChange(value: NavLayout) {
    const previous = navLayout;
    setNavLayout(value);
    setIsSavingLayout(true);
    try {
      await updateTheme({ nav_layout: value });
      message.success("Menu layout updated.");
      router.refresh();
    } catch (err) {
      setNavLayout(previous);
      message.error(err instanceof ApiError ? err.message : "Couldn't change the menu layout.");
    } finally {
      setIsSavingLayout(false);
    }
  }

  async function handleToggle(role: Role, itemKey: string, visible: boolean) {
    const current = resolvedPermissions(role);
    const next = visible ? [...current, itemKey] : current.filter((k) => k !== itemKey);

    setSavingCell(`${role.id}:${itemKey}`);
    try {
      await updateRoleTabs(role.id, next);
      setRoles((prev) => prev.map((r) => (r.id === role.id ? { ...r, default_tab_permissions: next } : r)));
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't update sidebar visibility.");
    } finally {
      setSavingCell(null);
    }
  }

  const columns: ColumnsType<Row> = [
    { title: "Menu Item", dataIndex: "label", render: (v: string) => <span className="font-semibold">{v}</span> },
    {
      title: "Super Admin",
      render: () => <Tag color="green">Always on</Tag>,
    },
    ...editableRoles.map((role) => ({
      title: titleCase(role.label.replace(/[_-]+/g, " ")),
      key: `role-${role.id}`,
      render: (_: unknown, row: Row) => {
        const visible = resolvedPermissions(role).includes(row.key);
        return (
          <Switch
            checked={visible}
            loading={savingCell === `${role.id}:${row.key}`}
            onChange={(checked) => void handleToggle(role, row.key, checked)}
            className={`dms_settings_sidebar_toggle dms_settings_sidebar_toggle_${role.label.replace(/\s+/g, "_")}`}
          />
        );
      },
    })),
  ];

  return (
    <div className="flex flex-col gap-3">
      <div className="flex flex-col gap-1 dms_settings_menu_layout">
        <Typography.Text strong>Menu layout</Typography.Text>
        <Select<NavLayout>
          value={navLayout}
          loading={isSavingLayout}
          onChange={(value) => void handleLayoutChange(value)}
          options={NAV_LAYOUT_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
          style={{ maxWidth: 320 }}
          className="dms_settings_menu_layout_select"
        />
        <Typography.Text type="secondary" className="dms_settings_menu_layout_hint">
          {NAV_LAYOUT_OPTIONS.find((o) => o.value === navLayout)?.description} Applies to everyone.
        </Typography.Text>
      </div>
      <Typography.Paragraph type="secondary" style={{ marginBottom: 0 }}>
        Turn a menu item off for a role and it disappears from their menu entirely - Super Admin always sees
        everything. Buy Domains, Auctions, and Users also still need their own feature access (Users → Manage
        Feature Access) to actually work once visible.
      </Typography.Paragraph>
      <Table<Row>
        rowKey="key"
        loading={isLoading}
        dataSource={SIDEBAR_ITEMS}
        columns={columns}
        pagination={false}
        className="dms_settings_sidebar_table"
      />
    </div>
  );
}
