'use client';

import {
  closestCenter,
  DndContext,
  type DragEndEvent,
  PointerSensor,
  useSensor,
  useSensors,
} from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  useSortable,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import {
  EmptyState,
  Page,
  PageHeader,
  SearchBar,
} from '@/components/entity-list';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
import {
  Sheet,
  SheetDescription,
  SheetHeader,
  SheetTitle,
} from '@/components/ui/sheet';
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from '@/components/ui/tooltip';
import { PaginatedResult } from '@/types/pagination-result';
import {
  Setting,
  SettingComponentEnum,
  SettingGroup,
  SettingSubgroup,
} from '@hed-hog/api-types';
import { useApp, useQuery } from '@hed-hog/next-app-provider';
import {
  GripVertical,
  ListTree,
  Pencil,
  Plus,
  Save,
  Settings2,
  SlidersHorizontal,
  Trash2,
} from 'lucide-react';
import { useTranslations } from 'next-intl';
import { CSSProperties, useEffect, useMemo, useRef, useState } from 'react';

type SettingWithList = Setting & {
  subgroup_id?: number | null;
  setting_subgroup?: SettingSubgroup | null;
  setting_list?: Array<{
    id: number;
    order?: number;
    value: string;
  }>;
};

type SubgroupSection = {
  key: string;
  slug: string;
  name: string;
  description?: string;
  settings: SettingWithList[];
};

type DraftOption = {
  id: string;
  value: string;
};

const createEmptyPage = <T,>(pageSize: number): PaginatedResult<T> => ({
  total: 0,
  lastPage: 0,
  page: 1,
  pageSize,
  prev: null,
  next: null,
  data: [],
});

export default function CoreSettingListPage() {
  const t = useTranslations('core.SettingListPage');
  const { request, currentLocaleCode, showToastHandler } = useApp();
  const [search, setSearch] = useState('');
  const [activeGroupSlug, setActiveGroupSlug] = useState<string>('all');
  const [activeSubgroupSlug, setActiveSubgroupSlug] = useState<string>('all');
  const [savingOptionBySettingId, setSavingOptionBySettingId] = useState<
    Record<number, boolean>
  >({});
  const [editingSettingId, setEditingSettingId] = useState<number | null>(null);
  const [sheetOpen, setSheetOpen] = useState(false);
  const [sheetDraftOptions, setSheetDraftOptions] = useState<DraftOption[]>([]);
  const draftIdRef = useRef(0);

  const createDraftOption = (value = ''): DraftOption => {
    draftIdRef.current += 1;
    return { id: `draft-option-${draftIdRef.current}`, value };
  };

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: { distance: 6 },
    })
  );

  const { data: groupsPage } = useQuery<PaginatedResult<SettingGroup>>({
    queryKey: ['core-setting-list-groups', currentLocaleCode],
    queryFn: async () => {
      const response = await request<PaginatedResult<SettingGroup>>({
        url: '/setting/group?page=1&pageSize=100',
        method: 'GET',
      });
      return response.data;
    },
    placeholderData: (previous) =>
      previous ?? createEmptyPage<SettingGroup>(100),
  });

  const groups = useMemo<SettingGroup[]>(
    () => groupsPage?.data ?? [],
    [groupsPage]
  );

  useEffect(() => {
    if (!groups.length) return;

    if (activeGroupSlug === 'all') {
      return;
    }

    const groupExists = groups.some(
      (group: SettingGroup) => group.slug === activeGroupSlug
    );
    if (!groupExists) {
      setActiveGroupSlug('all');
    }
  }, [activeGroupSlug, groups]);

  useEffect(() => {
    setActiveSubgroupSlug('all');
  }, [activeGroupSlug]);

  const {
    data: settingsPage,
    isLoading,
    refetch,
  } = useQuery<PaginatedResult<SettingWithList>>({
    queryKey: ['core-setting-list-items', activeGroupSlug, currentLocaleCode],
    queryFn: async () => {
      const url =
        activeGroupSlug === 'all'
          ? '/setting?page=1&pageSize=500'
          : `/setting/group/${activeGroupSlug}?page=1&pageSize=200`;

      const response = await request<PaginatedResult<SettingWithList>>({
        url,
        method: 'GET',
      });
      return response.data;
    },
    placeholderData: (previous) =>
      previous ?? createEmptyPage<SettingWithList>(200),
  });

  const allSettings = useMemo<SettingWithList[]>(
    () => settingsPage?.data ?? [],
    [settingsPage]
  );

  const listTypeSettings = useMemo<SettingWithList[]>(
    () =>
      allSettings.filter((setting) => {
        const component = setting.component;
        const isListComponent =
          component === SettingComponentEnum.COMBOBOX ||
          component === SettingComponentEnum.CHECKBOX;

        return isListComponent;
      }),
    [allSettings]
  );

  const subgroupOptions = useMemo(() => {
    const map = new Map<
      string,
      { value: string; label: string; description?: string }
    >();

    listTypeSettings.forEach((setting) => {
      const subgroup = setting.setting_subgroup;
      if (!subgroup?.slug) {
        return;
      }

      map.set(subgroup.slug, {
        value: subgroup.slug,
        label: subgroup.name?.toString() || subgroup.slug,
        description: subgroup.description?.toString() || undefined,
      });
    });

    return Array.from(map.values()).sort((left, right) =>
      left.label.localeCompare(right.label)
    );
  }, [listTypeSettings]);

  const visibleSettings = useMemo(() => {
    if (activeSubgroupSlug === 'all') {
      return listTypeSettings;
    }

    return listTypeSettings.filter(
      (setting) => setting.setting_subgroup?.slug === activeSubgroupSlug
    );
  }, [activeSubgroupSlug, listTypeSettings]);

  const currentGroup = useMemo(
    () => groups.find((group) => group.slug === activeGroupSlug) ?? null,
    [activeGroupSlug, groups]
  );

  const filteredSettings = useMemo(() => {
    const normalizedQuery = search.trim().toLowerCase();

    if (!normalizedQuery) {
      return visibleSettings;
    }

    return visibleSettings.filter((setting: SettingWithList) => {
      const options = (setting.setting_list ?? []).map(
        (option: { value: string }) => option.value
      );
      const text = [
        setting.name,
        setting.slug,
        setting.description,
        setting.setting_subgroup?.name,
        setting.setting_subgroup?.slug,
        options.join(' '),
      ]
        .filter(Boolean)
        .join(' ')
        .toLowerCase();

      return text.includes(normalizedQuery);
    });
  }, [search, visibleSettings]);

  const groupedSections = useMemo<SubgroupSection[]>(() => {
    const sectionsMap = new Map<string, SubgroupSection>();

    filteredSettings.forEach((setting) => {
      const subgroup = setting.setting_subgroup ?? null;
      const subgroupSlug = subgroup?.slug ?? '__ungrouped__';
      const sectionKey = subgroupSlug;

      if (!sectionsMap.has(sectionKey)) {
        sectionsMap.set(sectionKey, {
          key: sectionKey,
          slug: subgroupSlug,
          name:
            subgroup?.name?.toString() ||
            (subgroup ? subgroup.slug : t('sections.withoutSubgroup')),
          description: subgroup?.description?.toString() || undefined,
          settings: [],
        });
      }

      sectionsMap.get(sectionKey)?.settings.push(setting);
    });

    const sections = Array.from(sectionsMap.values());
    sections.forEach((section) => {
      section.settings.sort((left, right) =>
        left.slug.localeCompare(right.slug)
      );
    });

    sections.sort((left, right) => {
      if (left.slug === '__ungrouped__') return 1;
      if (right.slug === '__ungrouped__') return -1;
      return left.name.localeCompare(right.name);
    });

    return sections;
  }, [filteredSettings, t]);

  const settingsWithOptionsCount = useMemo(
    () => listTypeSettings.length,
    [listTypeSettings]
  );

  const totalOptions = useMemo(
    () =>
      listTypeSettings.reduce(
        (acc: number, setting: SettingWithList) =>
          acc + (setting.setting_list?.length ?? 0),
        0
      ),
    [listTypeSettings]
  );

  const controls = useMemo(
    () => [
      {
        id: 'group',
        type: 'select' as const,
        value: activeGroupSlug,
        onChange: (value: string) => setActiveGroupSlug(value),
        placeholder: t('filters.groupPlaceholder'),
        options: [
          { value: 'all', label: t('filters.allGroups') },
          ...groups.map((group: SettingGroup) => ({
            value: group.slug,
            label: group.name || group.slug,
          })),
        ],
      },
      {
        id: 'subgroup',
        type: 'select' as const,
        value: activeSubgroupSlug,
        onChange: (value: string) => setActiveSubgroupSlug(value),
        placeholder: t('filters.subgroupPlaceholder'),
        options: [
          { value: 'all', label: t('filters.allSubgroups') },
          ...subgroupOptions.map((subgroup) => ({
            value: subgroup.value,
            label: subgroup.label,
          })),
        ],
      },
    ],
    [activeGroupSlug, activeSubgroupSlug, groups, subgroupOptions, t]
  );

  const editingSetting = useMemo(
    () =>
      editingSettingId == null
        ? null
        : (listTypeSettings.find(
            (setting) => setting.id === editingSettingId
          ) ?? null),
    [editingSettingId, listTypeSettings]
  );

  const openSheetForSetting = (setting: SettingWithList) => {
    if (setting.id == null) {
      return;
    }

    const sortedOptions = [...(setting.setting_list ?? [])].sort(
      (left, right) => (left.order ?? 0) - (right.order ?? 0)
    );

    setEditingSettingId(setting.id);
    setSheetDraftOptions(
      sortedOptions.map((option) => createDraftOption(option.value))
    );
    setSheetOpen(true);
  };

  const handleSheetDragEnd = ({ active, over }: DragEndEvent) => {
    if (!over || active.id === over.id) {
      return;
    }

    setSheetDraftOptions((previous) => {
      const oldIndex = previous.findIndex((item) => item.id === active.id);
      const newIndex = previous.findIndex((item) => item.id === over.id);

      if (oldIndex < 0 || newIndex < 0) {
        return previous;
      }

      return arrayMove(previous, oldIndex, newIndex);
    });
  };

  const saveSettingOptions = async (
    setting: SettingWithList,
    overrideValues?: string[],
    successMessageKey:
      | 'optionsSavedSuccess'
      | 'optionsClearedSuccess' = 'optionsSavedSuccess',
    errorMessageKey:
      | 'optionsSavedError'
      | 'optionsClearedError' = 'optionsSavedError'
  ) => {
    const settingId = setting.id;
    if (!settingId) {
      return;
    }

    const values = (overrideValues ?? []).map((value) => value.trim());

    setSavingOptionBySettingId((previous) => ({
      ...previous,
      [settingId]: true,
    }));

    try {
      await request({
        url: `/setting/${settingId}/options`,
        method: 'PUT',
        data: {
          options: values.map((value, index) => ({
            value,
            order: index + 1,
          })),
        },
      });

      showToastHandler('success', t(successMessageKey));
      await refetch();
    } catch (_error) {
      showToastHandler('error', t(errorMessageKey));
    } finally {
      setSavingOptionBySettingId((previous) => ({
        ...previous,
        [settingId]: false,
      }));
    }
  };

  const handleSaveSheet = async () => {
    if (!editingSetting) {
      return;
    }

    const values = sheetDraftOptions.map((option) => option.value);
    await saveSettingOptions(editingSetting, values);
    setSheetOpen(false);
    setEditingSettingId(null);
  };

  const handleClearOptions = async (setting: SettingWithList) => {
    await saveSettingOptions(
      setting,
      [],
      'optionsClearedSuccess',
      'optionsClearedError'
    );
  };

  const addSheetOption = () => {
    setSheetDraftOptions((previous) => [...previous, createDraftOption('')]);
  };

  const removeSheetOption = (draftId: string) => {
    setSheetDraftOptions((previous) =>
      previous.filter((option) => option.id !== draftId)
    );
  };

  const updateSheetOption = (draftId: string, value: string) => {
    setSheetDraftOptions((previous) =>
      previous.map((option) =>
        option.id === draftId ? { ...option, value } : option
      )
    );
  };

  return (
    <Page>
      <PageHeader
        breadcrumbs={[
          { label: t('breadcrumbs.home'), href: '/' },
          { label: t('breadcrumbs.core') },
          { label: t('title') },
        ]}
        title={t('title')}
        description={t('description')}
      />

      <KpiCardsGrid
        items={[
          {
            key: 'groups',
            title: t('kpis.groups'),
            value: groups.length,
            icon: Settings2,
            iconContainerClassName: 'bg-sky-50 text-sky-700',
            accentClassName: 'from-sky-500/20 via-sky-500/10 to-transparent',
          },
          {
            key: 'settings',
            title: t('kpis.settingsTotal'),
            value: settingsWithOptionsCount,
            icon: ListTree,
            iconContainerClassName: 'bg-emerald-50 text-emerald-700',
            accentClassName:
              'from-emerald-500/20 via-emerald-500/10 to-transparent',
          },
          {
            key: 'subgroups',
            title: t('kpis.subgroups'),
            value: subgroupOptions.length,
            icon: SlidersHorizontal,
            iconContainerClassName: 'bg-amber-50 text-amber-700',
            accentClassName:
              'from-amber-500/20 via-amber-500/10 to-transparent',
          },
          {
            key: 'options',
            title: t('kpis.totalOptions'),
            value: totalOptions,
            icon: SlidersHorizontal,
            iconContainerClassName: 'bg-violet-50 text-violet-700',
            accentClassName:
              'from-violet-500/20 via-violet-500/10 to-transparent',
          },
        ]}
      />

      <Card>
        <CardContent className="flex flex-wrap items-center justify-between gap-3 py-3">
          <div className="space-y-0.5">
            <p className="text-sm font-medium text-foreground">
              {activeGroupSlug === 'all'
                ? t('filters.allGroups')
                : currentGroup?.name || activeGroupSlug}
            </p>
            {activeGroupSlug !== 'all' && currentGroup?.description ? (
              <p className="text-xs text-muted-foreground">
                {currentGroup.description}
              </p>
            ) : null}
          </div>
          <Badge variant="secondary">
            {t('kpis.options')}: {totalOptions}
          </Badge>
        </CardContent>
      </Card>

      <SearchBar
        searchQuery={search}
        onSearchChange={setSearch}
        onSearch={() => undefined}
        placeholder={t('searchPlaceholder')}
        controls={controls}
      />

      {isLoading ? (
        <Card>
          <CardContent className="py-8 text-sm text-muted-foreground">
            {t('loading')}
          </CardContent>
        </Card>
      ) : filteredSettings.length === 0 ? (
        <EmptyState
          icon={<ListTree className="h-10 w-10" />}
          title={t('emptyTitle')}
          description={
            listTypeSettings.length === 0
              ? t('emptyDescriptionNoSettings')
              : t('emptyDescriptionFiltered')
          }
        />
      ) : (
        <div className="grid gap-3">
          {groupedSections.map((section) => (
            <Card key={section.key}>
              <CardHeader className="pb-3">
                <CardTitle className="text-base">{section.name}</CardTitle>
                {section.description ? (
                  <CardDescription>{section.description}</CardDescription>
                ) : null}
              </CardHeader>

              <CardContent className="space-y-4">
                {section.settings.map((setting: SettingWithList) => {
                  const settingId = setting.id;
                  const isSaving =
                    settingId != null
                      ? savingOptionBySettingId[settingId] === true
                      : false;
                  const optionsCount = setting.setting_list?.length ?? 0;

                  return (
                    <div
                      key={setting.id}
                      className="flex items-center justify-between gap-3 border-b border-border/60 pb-3 last:border-0 last:pb-0"
                    >
                      <div className="min-w-0 flex-1">
                        <p className="truncate text-sm font-semibold text-foreground">
                          {setting.name}
                        </p>
                      </div>

                      <div className="flex items-center gap-2">
                        <Badge variant="secondary">
                          {t('itemCount', { count: optionsCount })}
                        </Badge>

                        <Tooltip>
                          <TooltipTrigger asChild>
                            <Button
                              type="button"
                              size="icon"
                              variant="ghost"
                              onClick={() => openSheetForSetting(setting)}
                              disabled={settingId == null || isSaving}
                              aria-label={t('actions.editOptions')}
                            >
                              <Pencil className="h-4 w-4" />
                            </Button>
                          </TooltipTrigger>
                          <TooltipContent>
                            <p>{t('actions.editOptions')}</p>
                          </TooltipContent>
                        </Tooltip>

                        <Tooltip>
                          <TooltipTrigger asChild>
                            <Button
                              type="button"
                              size="icon"
                              variant="ghost"
                              onClick={() => handleClearOptions(setting)}
                              disabled={settingId == null || isSaving}
                              aria-label={t('actions.clearOptions')}
                            >
                              <Trash2 className="h-4 w-4" />
                            </Button>
                          </TooltipTrigger>
                          <TooltipContent>
                            <p>{t('actions.clearOptions')}</p>
                          </TooltipContent>
                        </Tooltip>
                      </div>
                    </div>
                  );
                })}
              </CardContent>
            </Card>
          ))}
        </div>
      )}

      <Sheet
        open={sheetOpen}
        onOpenChange={(open) => {
          setSheetOpen(open);
          if (!open) {
            setEditingSettingId(null);
            setSheetDraftOptions([]);
          }
        }}
      >
        <ResizableSheetContent
          sheetId="core-setting-list-options-sheet"
          defaultWidth={768}
          minWidth={520}
          maxWidth={1100}
          className="w-full overflow-y-auto sm:max-w-2xl"
        >
          <SheetHeader>
            <SheetTitle>{t('sheet.title')}</SheetTitle>
            <SheetDescription>
              {editingSetting
                ? t('sheet.description', { name: editingSetting.name ?? '' })
                : t('sheet.descriptionFallback')}
            </SheetDescription>
          </SheetHeader>

          <div className="mt-6 space-y-4">
            <div className="flex items-center justify-between gap-2">
              <Badge variant="secondary">
                {t('itemCount', { count: sheetDraftOptions.length })}
              </Badge>
              <Button
                type="button"
                size="sm"
                variant="outline"
                onClick={addSheetOption}
              >
                <Plus className="mr-1 h-3.5 w-3.5" />
                {t('actions.addOption')}
              </Button>
            </div>

            {sheetDraftOptions.length === 0 ? (
              <p className="text-sm text-muted-foreground">
                {t('emptyOptions')}
              </p>
            ) : (
              <DndContext
                sensors={sensors}
                collisionDetection={closestCenter}
                onDragEnd={handleSheetDragEnd}
              >
                <SortableContext
                  items={sheetDraftOptions.map((option) => option.id)}
                  strategy={verticalListSortingStrategy}
                >
                  <div className="space-y-2">
                    {sheetDraftOptions.map((option, index) => (
                      <SortableOptionItem
                        key={option.id}
                        option={option}
                        index={index}
                        placeholder={t('optionPlaceholder')}
                        removeLabel={t('actions.removeOption')}
                        onChange={updateSheetOption}
                        onRemove={removeSheetOption}
                      />
                    ))}
                  </div>
                </SortableContext>
              </DndContext>
            )}

            <div className="flex justify-end">
              <Button
                type="button"
                onClick={handleSaveSheet}
                disabled={
                  editingSettingId == null ||
                  (editingSettingId != null &&
                    savingOptionBySettingId[editingSettingId] === true)
                }
              >
                <Save className="mr-1 h-3.5 w-3.5" />
                {editingSettingId != null &&
                savingOptionBySettingId[editingSettingId]
                  ? t('actions.saving')
                  : t('actions.saveOptions')}
              </Button>
            </div>
          </div>
        </ResizableSheetContent>
      </Sheet>
    </Page>
  );
}

function SortableOptionItem({
  option,
  index,
  placeholder,
  removeLabel,
  onChange,
  onRemove,
}: {
  option: DraftOption;
  index: number;
  placeholder: string;
  removeLabel: string;
  onChange: (id: string, value: string) => void;
  onRemove: (id: string) => void;
}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: option.id });

  const style: CSSProperties = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.6 : undefined,
  };

  return (
    <div
      ref={setNodeRef}
      style={style}
      className="flex items-center gap-2 rounded-md border p-2"
    >
      <button
        type="button"
        {...attributes}
        {...listeners}
        className="flex cursor-grab touch-none items-center justify-center rounded p-1 text-muted-foreground active:cursor-grabbing"
        aria-label={`reorder-option-${index + 1}`}
      >
        <GripVertical className="h-4 w-4" />
      </button>

      <Input
        value={option.value}
        onChange={(event) => onChange(option.id, event.target.value)}
        placeholder={placeholder}
        className="bg-background"
      />

      <Button
        type="button"
        size="icon"
        variant="ghost"
        onClick={() => onRemove(option.id)}
        aria-label={removeLabel}
      >
        <Trash2 className="h-4 w-4" />
      </Button>
    </div>
  );
}
