import { type FormEvent, useCallback, useEffect, useMemo, useState } from 'react';

import type { CatalogListParams } from '../api/catalogApi';
import { getApiErrorMessage } from '../api/errors';
import type { PageResponse } from '../api/types';
import { useAuth } from '../auth/AuthContext';
import { DataTable, type TableColumn } from '../components/DataTable';
import { Layout } from '../components/Layout';
import { Badge } from '../components/ui/Badge';
import { Button } from '../components/ui/Button';
import { Card } from '../components/ui/Card';
import { ConfirmDialog } from '../components/ui/ConfirmDialog';
import { ErrorState } from '../components/ui/ErrorState';
import { FormField } from '../components/ui/FormField';
import { Input } from '../components/ui/Input';
import { LoadingState } from '../components/ui/LoadingState';
import { PageHeader } from '../components/ui/PageHeader';
import { Pagination } from '../components/ui/Pagination';
import { Select } from '../components/ui/Select';
import { Textarea } from '../components/ui/Textarea';

type CatalogItem = {
  id: string;
  active: boolean;
};

export type CatalogFormState = Record<string, string>;

export type CatalogField = {
  name: string;
  label: string;
  required?: boolean;
  type?: 'text' | 'email' | 'textarea';
  disabledOnEdit?: boolean;
};

type CatalogPageProps<TItem extends CatalogItem> = {
  title: string;
  description: string;
  columns: TableColumn<TItem>[];
  fields: CatalogField[];
  emptyForm: CatalogFormState;
  list: (params: CatalogListParams) => Promise<PageResponse<TItem>>;
  create: (form: CatalogFormState) => Promise<unknown>;
  update: (id: string, form: CatalogFormState, item: TItem) => Promise<unknown>;
  remove: (id: string) => Promise<unknown>;
};

const PAGE_SIZE = 20;

export function CatalogPage<TItem extends CatalogItem>({
  title,
  description,
  columns,
  fields,
  emptyForm,
  list,
  create,
  update,
  remove,
}: CatalogPageProps<TItem>) {
  const { user } = useAuth();
  const canWrite = user?.role === 'ADMIN' || user?.role === 'MANAGER';
  const [pageData, setPageData] = useState<PageResponse<TItem> | null>(null);
  const [page, setPage] = useState(0);
  const [searchInput, setSearchInput] = useState('');
  const [search, setSearch] = useState('');
  const [activeFilter, setActiveFilter] = useState<'true' | 'false'>('true');
  const [isLoading, setIsLoading] = useState(false);
  const [isSaving, setIsSaving] = useState(false);
  const [error, setError] = useState('');
  const [form, setForm] = useState<CatalogFormState>(emptyForm);
  const [editingItem, setEditingItem] = useState<TItem | null>(null);
  const [deactivateItem, setDeactivateItem] = useState<TItem | null>(null);
  const [isFormOpen, setIsFormOpen] = useState(false);

  const loadData = useCallback(async () => {
    setIsLoading(true);
    setError('');

    try {
      const data = await list({
        page,
        size: PAGE_SIZE,
        search,
        active: activeFilter === 'true',
      });
      setPageData(data);
    } catch (caughtError) {
      setError(getApiErrorMessage(caughtError, 'Не удалось загрузить записи справочника'));
    } finally {
      setIsLoading(false);
    }
  }, [activeFilter, list, page, search]);

  useEffect(() => {
    void loadData();
  }, [loadData]);

  const tableColumns = useMemo<TableColumn<TItem>[]>(
    () => [
      ...columns,
      {
        key: 'active',
        label: 'Статус',
        render: (item) => <Badge variant={item.active ? 'success' : 'neutral'}>{item.active ? 'Активен' : 'Неактивен'}</Badge>,
      },
    ],
    [columns],
  );

  const openCreateForm = () => {
    setEditingItem(null);
    setForm(emptyForm);
    setIsFormOpen(true);
    setError('');
  };

  const openEditForm = (item: TItem) => {
    const nextForm = fields.reduce<CatalogFormState>((acc, field) => {
      const value = item[field.name as keyof TItem];
      acc[field.name] = value == null ? '' : String(value);
      return acc;
    }, {});

    setEditingItem(item);
    setForm(nextForm);
    setIsFormOpen(true);
    setError('');
  };

  const closeForm = () => {
    setEditingItem(null);
    setForm(emptyForm);
    setIsFormOpen(false);
  };

  const handleSearchSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setPage(0);
    setSearch(searchInput.trim());
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setIsSaving(true);
    setError('');

    try {
      if (editingItem) {
        await update(editingItem.id, form, editingItem);
      } else {
        await create(form);
        setPage(0);
      }

      closeForm();
      await loadData();
    } catch (caughtError) {
      setError(getApiErrorMessage(caughtError, 'Не удалось сохранить запись'));
    } finally {
      setIsSaving(false);
    }
  };

  const handleDeactivate = async () => {
    if (!deactivateItem) {
      return;
    }
    setError('');

    try {
      await remove(deactivateItem.id);
      setDeactivateItem(null);
      await loadData();
    } catch (caughtError) {
      setError(getApiErrorMessage(caughtError, 'Не удалось деактивировать запись'));
    }
  };

  return (
    <Layout title={title}>
      <div className="space-y-6">
        <PageHeader
          title={title}
          description={description}
          actions={
            canWrite ? (
              <Button type="button" onClick={openCreateForm}>
              Создать
              </Button>
            ) : null
          }
        />

        <form onSubmit={handleSearchSubmit} className="flex flex-col gap-3 rounded-md border border-slate-200 bg-white p-4 shadow-sm sm:flex-row">
          <Input
            type="search"
            value={searchInput}
            onChange={(event) => setSearchInput(event.target.value)}
            placeholder="Поиск"
            className="min-w-0 flex-1"
          />
          <Select
            value={activeFilter}
            onChange={(event) => {
              setPage(0);
              setActiveFilter(event.target.value as 'true' | 'false');
            }}
          >
            <option value="true">Только активные</option>
            <option value="false">Только неактивные</option>
          </Select>
          <Button type="submit" variant="secondary">
            Найти
          </Button>
        </form>

        {isFormOpen && (
          <Card>
            <form onSubmit={handleSubmit} className="space-y-4 p-4">
            <div className="flex items-center justify-between gap-4">
              <h2 className="text-base font-semibold">{editingItem ? 'Редактировать запись' : 'Создать запись'}</h2>
              <Button type="button" variant="ghost" size="sm" onClick={closeForm}>
                Отмена
              </Button>
            </div>

            <div className="grid gap-4 md:grid-cols-2">
              {fields.map((field) => {
                const disabled = Boolean(editingItem && field.disabledOnEdit);

                return (
                  <FormField key={field.name} label={field.label} required={field.required}>
                    {field.type === 'textarea' ? (
                      <Textarea
                        value={form[field.name] ?? ''}
                        onChange={(event) => setForm((current) => ({ ...current, [field.name]: event.target.value }))}
                        required={field.required}
                        disabled={disabled}
                      />
                    ) : (
                      <Input
                        type={field.type ?? 'text'}
                        value={form[field.name] ?? ''}
                        onChange={(event) => setForm((current) => ({ ...current, [field.name]: event.target.value }))}
                        required={field.required}
                        disabled={disabled}
                      />
                    )}
                  </FormField>
                );
              })}
            </div>

            <Button type="submit" disabled={isSaving}>
              {isSaving ? 'Сохранение...' : 'Сохранить'}
            </Button>
          </form>
          </Card>
        )}

        {error && <ErrorState message={error} />}

        {isLoading ? (
          <LoadingState />
        ) : (
          <DataTable
            items={pageData?.items ?? []}
            columns={tableColumns}
            getRowKey={(item) => item.id}
            emptyTitle="Записи не найдены"
            actions={
              canWrite
                ? (item) => (
                    <div className="flex justify-end gap-2">
                      <Button type="button" variant="secondary" size="sm" onClick={() => openEditForm(item)}>
                        Изменить
                      </Button>
                      <Button type="button" variant="danger" size="sm" onClick={() => setDeactivateItem(item)}>
                        Деактивировать
                      </Button>
                    </div>
                  )
                : undefined
            }
          />
        )}

        {pageData && (
          <Pagination
            page={pageData.page}
            totalPages={pageData.totalPages}
            totalElements={pageData.totalElements}
            hasPrevious={pageData.hasPrevious}
            hasNext={pageData.hasNext}
            onPrevious={() => setPage((current) => Math.max(current - 1, 0))}
            onNext={() => setPage((current) => current + 1)}
          />
        )}

        <ConfirmDialog
          open={Boolean(deactivateItem)}
          title="Деактивировать запись"
          message="Запись будет помечена как неактивная. История и связанные данные останутся доступными."
          confirmLabel="Деактивировать"
          isConfirming={isSaving}
          onCancel={() => setDeactivateItem(null)}
          onConfirm={handleDeactivate}
        />
      </div>
    </Layout>
  );
}
