'use client';

import { useEffect, useState, useCallback, useRef } from 'react';
import { useParams, useRouter } from 'next/navigation';
import {
  ArrowLeft,
  User,
  Phone,
  Mail,
  Building,
  Trophy,
  XCircle,
  Trash2,
  Loader2,
  Save,
  X,
  Pencil,
} from 'lucide-react';
import { cn } from '@/lib/cn';
import { api } from '@/lib/api';
import { useUser } from '@/lib/useUser';
import { StageBadge } from '@/components/crm/stage-badge';
import { BitrixStageSwitcher } from '@/components/crm/bitrix-stage-switcher';
import { TimelineFeed } from '@/components/crm/timeline-feed';
import { ActivityList } from '@/components/crm/activity-list';

/* ─── Types ─── */

interface DealDetail {
  id: string;
  number?: number;
  title: string;
  contactName?: string;
  contactPhone?: string;
  contactEmail?: string;
  contactTelegram?: string;
  contactWhatsapp?: string;
  companyName?: string;
  companyInn?: string;
  source?: string;
  amount?: number;
  closedAt?: string;
  lostComment?: string;
  metadata?: Record<string, any>;
  pipeline?: { id: string; name: string };
  stage?: { id: string; name: string; color: string; type: string };
  assignee?: { id: string; firstName: string; lastName: string };
  createdBy?: { id: string; firstName: string; lastName: string };
  lostReason?: { id: string; name: string };
  _count?: { activities: number; trainings: number; timeline: number };
  customFieldValues?: Record<string, { name: string; type: string; value: any }>;
  createdAt: string;
  updatedAt: string;
}

interface StaffMember {
  id: string;
  firstName: string;
  lastName: string;
}

interface LayoutField {
  fieldKey: string;
  source: 'system' | 'custom';
  width: 'full' | 'half';
}

interface LayoutBlock {
  blockKey: string;
  blockName: string;
  fields: LayoutField[];
}

interface CustomFieldDef {
  id: string;
  name: string;
  fieldName: string;
  type: string;
  roleAccess?: Record<string, string>;
}

/* ─── Constants ─── */

const sourceLabels: Record<string, string> = {
  LANDING: 'Лендинг',
  MANUAL: 'Вручную',
  WEBHOOK: 'Вебхук',
  IMPORT: 'Импорт',
  REFERRAL: 'Реферал',
  PHONE: 'Телефон',
  SOCIAL: 'Соцсети',
};

const inputClass =
  'w-full px-2 py-1 border border-border rounded text-sm focus:outline-none focus:ring-2 focus:ring-primary/30 bg-background text-text';

const SYSTEM_FIELD_META: Record<
  string,
  {
    label: string;
    type: 'text' | 'tel' | 'email' | 'number' | 'select' | 'combobox' | 'readonly' | 'date';
    editKey?: string;
    icon?: any;
  }
> = {
  contactName: { label: 'Контакт', type: 'text', icon: User },
  contactPhone: { label: 'Телефон', type: 'tel', icon: Phone },
  contactEmail: { label: 'Email', type: 'email', icon: Mail },
  companyName: { label: 'Компания', type: 'readonly', icon: Building },
  source: { label: 'Источник', type: 'select' },
  assignee: { label: 'Ответственный', type: 'combobox', editKey: 'assigneeId' },
  amount: { label: 'Сумма', type: 'number' },
  createdBy: { label: 'Создал', type: 'readonly' },
  createdAt: { label: 'Создана', type: 'date' },
  updatedAt: { label: 'Обновлена', type: 'date' },
  closedAt: { label: 'Закрыта', type: 'date' },
  lostReason: { label: 'Причина проигрыша', type: 'readonly' },
  lostComment: { label: 'Комментарий', type: 'readonly' },
};

const DEFAULT_BLOCKS: LayoutBlock[] = [
  {
    blockKey: 'contact',
    blockName: 'Контакт',
    fields: [
      { fieldKey: 'contactName', source: 'system', width: 'full' },
      { fieldKey: 'contactPhone', source: 'system', width: 'half' },
      { fieldKey: 'contactEmail', source: 'system', width: 'half' },
    ],
  },
  {
    blockKey: 'info',
    blockName: 'Информация',
    fields: [
      { fieldKey: 'source', source: 'system', width: 'half' },
      { fieldKey: 'assignee', source: 'system', width: 'half' },
      { fieldKey: 'createdBy', source: 'system', width: 'half' },
      { fieldKey: 'amount', source: 'system', width: 'half' },
      { fieldKey: 'createdAt', source: 'system', width: 'half' },
      { fieldKey: 'closedAt', source: 'system', width: 'half' },
      { fieldKey: 'lostReason', source: 'system', width: 'half' },
      { fieldKey: 'lostComment', source: 'system', width: 'half' },
    ],
  },
];

function normalizeLayout(data: any): LayoutBlock[] {
  if (Array.isArray(data) && data.length > 0 && data[0]?.blockKey) {
    return data as LayoutBlock[];
  }
  if (Array.isArray(data) && data.length > 0) {
    return [{ blockKey: 'main', blockName: 'Основное', fields: data as LayoutField[] }];
  }
  return DEFAULT_BLOCKS;
}

/* ─── Page Component ─── */

export default function DealDetailPage() {
  const params = useParams();
  const router = useRouter();
  const dealParam = params.id as string; // number or UUID
  const user = useUser();
  const userRole = user?.role?.toLowerCase() || '';
  const isAdmin = ['super_admin', 'club_admin'].includes(userRole);

  const [deal, setDeal] = useState<DealDetail | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [activeTab, setActiveTab] = useState<'timeline' | 'activities'>('timeline');

  // Layout
  const [layout, setLayout] = useState<LayoutBlock[]>(DEFAULT_BLOCKS);
  const [layoutIsDefault, setLayoutIsDefault] = useState(true);
  const [showDefaultView, setShowDefaultView] = useState(false);
  const [customFieldDefs, setCustomFieldDefs] = useState<CustomFieldDef[]>([]);

  // Timeline refresh
  const [timelineKey, setTimelineKey] = useState(0);
  const refreshTimeline = () => setTimelineKey((k) => k + 1);

  // Edit mode
  const [dirtyFields, setDirtyFields] = useState<Set<string>>(new Set());
  const [editForm, setEditForm] = useState<Record<string, any>>({});
  const [editSaving, setEditSaving] = useState(false);
  const [staff, setStaff] = useState<StaffMember[]>([]);
  const [staffSearch, setStaffSearch] = useState('');
  const [showStaffDropdown, setShowStaffDropdown] = useState(false);
  const staffRef = useRef<HTMLDivElement>(null);

  const isEditing = dirtyFields.size > 0;

  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (staffRef.current && !staffRef.current.contains(e.target as Node)) {
        setShowStaffDropdown(false);
      }
    };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, []);

  // Fetch deal by number or UUID
  const fetchDeal = useCallback(async () => {
    try {
      const url = `/crm/deals/${dealParam}`;
      const data = await api.get<DealDetail>(url);
      setDeal(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Ошибка загрузки');
    } finally {
      setLoading(false);
    }
  }, [dealParam]);

  // Fetch layout + custom field defs
  const fetchLayout = useCallback(async (forceDefault?: boolean) => {
    try {
      const [layoutData, cfData] = await Promise.all([
        api.get<{ blocks: any; isDefault: boolean }>(
          `/crm/card-layouts?entityType=deal${forceDefault ? '&useDefault=true' : ''}`,
        ),
        api.get<CustomFieldDef[]>('/crm/fields?entityType=deal'),
      ]);
      setLayout(normalizeLayout(layoutData.blocks));
      setLayoutIsDefault(layoutData.isDefault);
      setCustomFieldDefs(cfData);
    } catch {
      // use defaults
    }
  }, []);

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

  // Toggle between personal and default view
  const toggleView = async () => {
    const next = !showDefaultView;
    setShowDefaultView(next);
    await fetchLayout(next);
  };

  /* ─── Field access control ─── */

  const getFieldAccess = (
    fieldKey: string,
    source: 'system' | 'custom',
  ): 'hidden' | 'readonly' | 'editable' => {
    if (source === 'custom') {
      const def = customFieldDefs.find((f) => f.fieldName === fieldKey);
      if (!def) return 'hidden';
      if (!def.roleAccess) return 'editable';
      const level = def.roleAccess[userRole];
      if (level === 'hidden') return 'hidden';
      if (level === 'readonly') return 'readonly';
      return level === 'editable' ? 'editable' : 'readonly';
    }
    // System fields
    const meta = SYSTEM_FIELD_META[fieldKey];
    if (!meta) return 'hidden';
    if (meta.type === 'readonly' || meta.type === 'date') return 'readonly';
    if (['super_admin', 'club_admin'].includes(userRole)) return 'editable';
    if (userRole === 'manager' && deal?.assignee?.id === user?.id) return 'editable';
    return 'readonly';
  };

  /* ─── Check if block has any editable fields ─── */

  const blockHasEditableFields = (block: LayoutBlock): boolean => {
    return block.fields.some((f) => getFieldAccess(f.fieldKey, f.source) === 'editable');
  };

  /* ─── Enter edit mode — all editable fields at once ─── */

  const enterEditMode = () => {
    if (!deal) return;
    // Initialize form with current values
    setEditForm({
      title: deal.title || '',
      contactName: deal.contactName || '',
      contactPhone: deal.contactPhone || '',
      contactEmail: deal.contactEmail || '',
      source: deal.source || 'MANUAL',
      assigneeId: deal.assignee?.id || '',
      amount: deal.amount?.toString() || '',
    });
    setStaffSearch(deal.assignee ? `${deal.assignee.firstName} ${deal.assignee.lastName}` : '');

    // Mark ALL editable fields as dirty
    const allEditable = new Set<string>();
    layout.forEach((block) => {
      block.fields.forEach((f) => {
        if (getFieldAccess(f.fieldKey, f.source) === 'editable') {
          const meta = SYSTEM_FIELD_META[f.fieldKey];
          allEditable.add(meta?.editKey || f.fieldKey);
        }
      });
    });
    allEditable.add('title'); // Title is also editable
    setDirtyFields(allEditable);

    if (staff.length === 0) {
      api
        .get<StaffMember[]>('/users/staff')
        .then(setStaff)
        .catch(() => {});
    }
  };

  const cancelEditing = () => {
    setDirtyFields(new Set());
    setShowStaffDropdown(false);
  };

  const saveEditing = async () => {
    if (!deal) return;
    setEditSaving(true);
    try {
      const data: Record<string, any> = {
        title: editForm.title?.trim() || deal.title,
        contactName: editForm.contactName?.trim() || null,
        contactPhone: editForm.contactPhone?.trim() || null,
        contactEmail: editForm.contactEmail?.trim() || null,
        source: editForm.source || null,
        assigneeId: editForm.assigneeId || null,
        amount: editForm.amount ? parseFloat(editForm.amount) : null,
      };
      await api.patch(`/crm/deals/${deal.id}`, data);
      await fetchDeal();
      setDirtyFields(new Set());
      setShowStaffDropdown(false);
      refreshTimeline();
    } catch {
      // ignore
    } finally {
      setEditSaving(false);
    }
  };

  const setField = (key: string, value: string) => setEditForm((f) => ({ ...f, [key]: value }));

  const selectedStaff = staff.find((s) => s.id === editForm.assigneeId);
  const filteredStaff = staff.filter((s) => {
    const name = `${s.firstName} ${s.lastName}`.toLowerCase();
    return name.includes(staffSearch.toLowerCase());
  });

  /* ─── Add section ─── */

  const openAddSection = async () => {
    const name = prompt('Название нового раздела:');
    if (!name?.trim()) return;
    try {
      const newBlock: LayoutBlock = {
        blockKey: `custom_${Date.now()}`,
        blockName: name.trim(),
        fields: [],
      };
      const newBlocks = [...layout, newBlock];
      await api.put('/crm/card-layouts', {
        entityType: 'deal',
        blocks: newBlocks,
      });
      await fetchLayout();
    } catch {
      // ignore
    }
  };

  /* ─── Actions ─── */

  const [actionLoading, setActionLoading] = useState<'win' | 'lose' | 'delete' | null>(null);

  const handleWin = async () => {
    if (actionLoading || !deal) return;
    setActionLoading('win');
    try {
      await api.post(`/crm/deals/${deal.id}/win`);
      await fetchDeal();
      refreshTimeline();
    } catch {
      // ignore
    } finally {
      setActionLoading(null);
    }
  };

  const handleLose = async () => {
    if (actionLoading || !deal) return;
    setActionLoading('lose');
    try {
      await api.post(`/crm/deals/${deal.id}/lose`, {});
      await fetchDeal();
      refreshTimeline();
    } catch {
      // ignore
    } finally {
      setActionLoading(null);
    }
  };

  const handleDelete = async () => {
    if (actionLoading || !deal) return;
    if (!confirm('Удалить сделку? Она будет перемещена в архив.')) return;
    setActionLoading('delete');
    try {
      await api.delete(`/crm/deals/${deal.id}`);
      router.push('/crm/deal/kanban');
    } catch {
      setActionLoading(null);
    }
  };

  /* ─── System field value getters ─── */

  const getSystemFieldDisplay = (fieldKey: string): string => {
    if (!deal) return '—';
    switch (fieldKey) {
      case 'contactName':
        return deal.contactName || '';
      case 'contactPhone':
        return deal.contactPhone || '';
      case 'contactEmail':
        return deal.contactEmail || '';
      case 'companyName':
        return deal.companyName || '';
      case 'source':
        return deal.source ? sourceLabels[deal.source] || deal.source : '—';
      case 'assignee':
        return deal.assignee ? `${deal.assignee.firstName} ${deal.assignee.lastName}` : '—';
      case 'createdBy':
        return deal.createdBy ? `${deal.createdBy.firstName} ${deal.createdBy.lastName}` : '—';
      case 'amount':
        return deal.amount != null && deal.amount > 0
          ? `${deal.amount.toLocaleString('ru-RU')} ₽`
          : '—';
      case 'createdAt':
        return formatDate(deal.createdAt);
      case 'updatedAt':
        return formatDate(deal.updatedAt);
      case 'closedAt':
        return deal.closedAt ? formatDate(deal.closedAt) : '';
      case 'lostReason':
        return deal.lostReason?.name || '';
      case 'lostComment':
        return deal.lostComment || '';
      default:
        return '';
    }
  };

  /* ─── Render a single field ─── */

  const renderSystemField = (fieldKey: string, access: 'editable' | 'readonly') => {
    const meta = SYSTEM_FIELD_META[fieldKey];
    if (!meta) return null;
    const editKey = meta.editKey || fieldKey;
    const display = getSystemFieldDisplay(fieldKey);
    const isFieldEditing = dirtyFields.has(editKey);

    // Skip empty readonly fields
    if (access === 'readonly' && !display) return null;

    // Fields with icons (contact-style)
    if (meta.icon) {
      const Icon = meta.icon;
      return (
        <div className="flex items-center gap-2 text-sm" key={fieldKey}>
          <Icon className="h-4 w-4 text-muted shrink-0" />
          {access === 'editable' && isFieldEditing ? (
            <input
              type={meta.type === 'text' ? 'text' : meta.type}
              value={editForm[editKey] || ''}
              onChange={(e) => setField(editKey, e.target.value)}
              className={inputClass}
            />
          ) : (
            renderDisplayValue(fieldKey, display, meta.type)
          )}
        </div>
      );
    }

    // Select field
    if (meta.type === 'select' && fieldKey === 'source') {
      return (
        <div className="flex items-start justify-between gap-2" key={fieldKey}>
          <span className="text-muted shrink-0">{meta.label}</span>
          {access === 'editable' && isFieldEditing ? (
            <select
              value={editForm[editKey] || ''}
              onChange={(e) => setField(editKey, e.target.value)}
              className={cn(inputClass, 'w-auto')}
            >
              {Object.entries(sourceLabels).map(([k, v]) => (
                <option key={k} value={k}>
                  {v}
                </option>
              ))}
            </select>
          ) : (
            <span className="text-text text-right">{display}</span>
          )}
        </div>
      );
    }

    // Combobox (assignee)
    if (meta.type === 'combobox' && fieldKey === 'assignee') {
      return (
        <div className="flex items-start justify-between gap-2" key={fieldKey}>
          <span className="text-muted shrink-0">{meta.label}</span>
          {access === 'editable' && isFieldEditing ? (
            <div className="relative" ref={staffRef}>
              <input
                type="text"
                value={
                  showStaffDropdown
                    ? staffSearch
                    : selectedStaff
                      ? `${selectedStaff.firstName} ${selectedStaff.lastName}`
                      : staffSearch
                }
                onChange={(e) => {
                  setStaffSearch(e.target.value);
                  setShowStaffDropdown(true);
                  if (!e.target.value) setField('assigneeId', '');
                }}
                onFocus={() => {
                  setShowStaffDropdown(true);
                  setStaffSearch('');
                }}
                placeholder="Поиск..."
                className={cn(inputClass, 'w-40')}
              />
              {showStaffDropdown && filteredStaff.length > 0 && (
                <div className="absolute z-10 top-full right-0 mt-1 w-48 bg-card border border-border rounded-lg shadow-lg max-h-40 overflow-y-auto">
                  {filteredStaff.map((s) => (
                    <button
                      key={s.id}
                      type="button"
                      onClick={() => {
                        setField('assigneeId', s.id);
                        setStaffSearch(`${s.firstName} ${s.lastName}`);
                        setShowStaffDropdown(false);
                      }}
                      className={`w-full text-left px-3 py-2 text-sm hover:bg-background transition-colors ${s.id === editForm.assigneeId ? 'bg-primary/10 text-primary' : 'text-text'}`}
                    >
                      {s.firstName} {s.lastName}
                    </button>
                  ))}
                </div>
              )}
            </div>
          ) : (
            <span className="text-text text-right">{display}</span>
          )}
        </div>
      );
    }

    // Number field (amount)
    if (meta.type === 'number') {
      return (
        <div className="flex items-start justify-between gap-2" key={fieldKey}>
          <span className="text-muted shrink-0">{meta.label}</span>
          {access === 'editable' && isFieldEditing ? (
            <input
              type="number"
              value={editForm[editKey] || ''}
              onChange={(e) => setField(editKey, e.target.value)}
              min={0}
              className={cn(inputClass, 'w-32 text-right')}
            />
          ) : (
            <span className="text-text text-right">{display}</span>
          )}
        </div>
      );
    }

    // Default: readonly row (date, readonly text)
    return <InfoRow key={fieldKey} label={meta.label} value={display} />;
  };

  const renderDisplayValue = (fieldKey: string, display: string, type: string) => {
    // Phone with tel: link
    if (type === 'tel' && display) {
      return (
        <a href={`tel:${display}`} className="text-primary hover:underline">
          {display}
        </a>
      );
    }
    // Email with mailto: link
    if (type === 'email' && display) {
      return (
        <a href={`mailto:${display}`} className="text-primary hover:underline">
          {display}
        </a>
      );
    }
    // Generic text
    const placeholder =
      fieldKey === 'contactName'
        ? 'Добавить имя'
        : fieldKey === 'contactPhone'
          ? 'Добавить телефон'
          : fieldKey === 'contactEmail'
            ? 'Добавить email'
            : '—';
    return (
      <span className="text-text">
        {display || <span className="text-muted italic">{placeholder}</span>}
      </span>
    );
  };

  const renderCustomField = (fieldKey: string) => {
    const cfValues = deal?.customFieldValues;
    if (!cfValues) return null;
    const cfValue = cfValues[fieldKey];
    if (!cfValue) {
      const def = customFieldDefs.find((f) => f.fieldName === fieldKey);
      if (!def) return null;
      return <InfoRow key={fieldKey} label={def.name} value="—" />;
    }
    const displayValue =
      cfValue.type === 'BOOLEAN'
        ? cfValue.value
          ? 'Да'
          : 'Нет'
        : Array.isArray(cfValue.value)
          ? cfValue.value.join(', ')
          : String(cfValue.value ?? '—');
    return <InfoRow key={fieldKey} label={cfValue.name} value={displayValue} />;
  };

  /* ─── Render ─── */

  if (loading) {
    return (
      <div className="flex items-center justify-center py-20">
        <Loader2 className="h-8 w-8 animate-spin text-primary" />
      </div>
    );
  }

  if (error || !deal) {
    return (
      <div className="bg-error/10 text-error rounded-xl p-6 text-center">
        <p>{error || 'Сделка не найдена'}</p>
        <button onClick={() => router.push('/crm/deal/kanban')} className="mt-2 text-sm underline">
          Вернуться к списку
        </button>
      </div>
    );
  }

  const isWon = deal.stage?.type === 'WON';
  const isLost = deal.stage?.type === 'LOST';
  const isClosed = isWon || isLost;

  // Title editing access
  const canEditTitle =
    ['super_admin', 'club_admin'].includes(userRole) ||
    (userRole === 'manager' && deal.assignee?.id === user?.id);

  return (
    <div className={isEditing ? 'pb-16' : ''}>
      {/* Header */}
      <div className="flex items-center gap-3 mb-4">
        <button
          onClick={() => router.push('/crm/deal/kanban')}
          className="p-2 rounded-lg hover:bg-background transition-colors"
        >
          <ArrowLeft className="h-5 w-5 text-muted" />
        </button>
        <div className="flex-1">
          {canEditTitle && dirtyFields.has('title') ? (
            <input
              type="text"
              value={editForm.title || ''}
              onChange={(e) => setField('title', e.target.value)}
              className="text-xl font-bold text-text bg-transparent border-b-2 border-primary/50 focus:outline-none focus:border-primary w-full"
            />
          ) : (
            <h1 className="text-xl font-bold text-text">
              {deal.number != null && (
                <span className="text-muted font-normal">#{deal.number} </span>
              )}
              {deal.title}
            </h1>
          )}
          <div className="flex items-center gap-2 mt-1">
            {deal.stage && (
              <StageBadge name={deal.stage.name} color={deal.stage.color} type={deal.stage.type} />
            )}
            {deal.pipeline && <span className="text-xs text-muted">{deal.pipeline.name}</span>}
          </div>
        </div>
        <div className="flex items-center gap-2">
          {!isClosed && (
            <>
              <button
                onClick={handleWin}
                disabled={!!actionLoading}
                className="flex items-center gap-1.5 px-3 py-2 text-sm bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors disabled:opacity-50"
              >
                {actionLoading === 'win' ? (
                  <Loader2 className="h-4 w-4 animate-spin" />
                ) : (
                  <Trophy className="h-4 w-4" />
                )}
                {actionLoading === 'win' ? '...' : 'Выиграна'}
              </button>
              <button
                onClick={handleLose}
                disabled={!!actionLoading}
                className="flex items-center gap-1.5 px-3 py-2 text-sm bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50"
              >
                {actionLoading === 'lose' ? (
                  <Loader2 className="h-4 w-4 animate-spin" />
                ) : (
                  <XCircle className="h-4 w-4" />
                )}
                {actionLoading === 'lose' ? '...' : 'Проиграна'}
              </button>
            </>
          )}
          <button
            onClick={handleDelete}
            disabled={!!actionLoading}
            className="p-2 rounded-lg hover:bg-error/10 text-muted hover:text-error transition-colors disabled:opacity-50"
            title="Удалить сделку"
          >
            {actionLoading === 'delete' ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              <Trash2 className="h-4 w-4" />
            )}
          </button>
        </div>
      </div>

      {/* Stage switcher */}
      {deal.pipeline && deal.stage && (
        <BitrixStageSwitcher
          dealId={deal.id}
          currentStageId={deal.stage.id}
          pipelineId={deal.pipeline.id}
          onStageChanged={() => {
            void fetchDeal();
            refreshTimeline();
          }}
        />
      )}

      {/* Main content: 2 columns */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
        {/* Left: Layout-driven blocks */}
        <div className="lg:col-span-1 space-y-4">
          {/* Dynamic blocks from layout */}
          {layout.map((block) => {
            const visibleFields = block.fields.filter(
              (f) => getFieldAccess(f.fieldKey, f.source) !== 'hidden',
            );
            if (visibleFields.length === 0) return null;
            const hasEditable = blockHasEditableFields(block);

            return (
              <div key={block.blockKey} className="bg-card rounded-xl border border-border p-4">
                <div className="flex items-center justify-between mb-3">
                  <h3 className="text-sm font-semibold text-text">{block.blockName}</h3>
                  {hasEditable && !isEditing && (
                    <button
                      onClick={enterEditMode}
                      className="p-1 rounded hover:bg-background transition-colors"
                      title="Редактировать"
                    >
                      <Pencil className="h-3.5 w-3.5 text-muted" />
                    </button>
                  )}
                </div>
                <div className="space-y-2 text-sm">
                  {visibleFields.map((f) => {
                    const access = getFieldAccess(f.fieldKey, f.source);
                    if (access === 'hidden') return null;
                    if (f.source === 'system') {
                      return renderSystemField(f.fieldKey, access);
                    }
                    return renderCustomField(f.fieldKey);
                  })}
                </div>
              </div>
            );
          })}

          {/* Buttons below blocks */}
          <div className="flex items-center gap-2 flex-wrap">
            {!layoutIsDefault && (
              <button
                onClick={toggleView}
                className="text-xs px-3 py-1.5 rounded-lg border border-border hover:bg-background transition-colors"
              >
                {showDefaultView ? 'Мой вид' : 'Общий вид'}
              </button>
            )}
            {isAdmin && (
              <button
                onClick={openAddSection}
                className="text-xs px-3 py-1.5 rounded-lg border border-dashed border-border text-muted hover:bg-background transition-colors"
              >
                + Добавить раздел
              </button>
            )}
          </div>

          {/* Stats — always shown */}
          {deal._count && (
            <div className="bg-card rounded-xl border border-border p-4">
              <h3 className="text-sm font-semibold text-text mb-3">Статистика</h3>
              <div className="grid grid-cols-3 gap-3 text-center">
                <div>
                  <div className="text-lg font-bold text-text">{deal._count.activities}</div>
                  <div className="text-[10px] text-muted">Дела</div>
                </div>
                <div>
                  <div className="text-lg font-bold text-text">{deal._count.trainings}</div>
                  <div className="text-[10px] text-muted">Тренировки</div>
                </div>
                <div>
                  <div className="text-lg font-bold text-text">{deal._count.timeline}</div>
                  <div className="text-[10px] text-muted">Записи</div>
                </div>
              </div>
            </div>
          )}

          {/* UTM — always shown if present */}
          {deal.metadata?.utm && Object.keys(deal.metadata.utm).length > 0 && (
            <div className="bg-card rounded-xl border border-border p-4">
              <h3 className="text-sm font-semibold text-text mb-3">UTM-метки</h3>
              <div className="space-y-1 text-sm">
                {Object.entries(deal.metadata.utm).map(([key, value]) => (
                  <InfoRow key={key} label={key} value={String(value)} />
                ))}
              </div>
            </div>
          )}
        </div>

        {/* Right: Timeline & Activities */}
        <div className="lg:col-span-2">
          <div className="flex items-center gap-1 mb-4 border-b border-border">
            <button
              onClick={() => setActiveTab('timeline')}
              className={cn(
                'px-4 py-2 text-sm font-medium border-b-2 transition-colors -mb-px',
                activeTab === 'timeline'
                  ? 'border-primary text-primary'
                  : 'border-transparent text-muted hover:text-text',
              )}
            >
              Таймлайн
            </button>
            <button
              onClick={() => setActiveTab('activities')}
              className={cn(
                'px-4 py-2 text-sm font-medium border-b-2 transition-colors -mb-px',
                activeTab === 'activities'
                  ? 'border-primary text-primary'
                  : 'border-transparent text-muted hover:text-text',
              )}
            >
              Дела {deal._count?.activities ? `(${deal._count.activities})` : ''}
            </button>
          </div>

          {activeTab === 'timeline' ? (
            <TimelineFeed dealId={deal.id} refreshKey={timelineKey} />
          ) : (
            <ActivityList dealId={deal.id} onTimelineChanged={refreshTimeline} />
          )}
        </div>
      </div>

      {/* Floating save/cancel panel */}
      {isEditing && (
        <div
          className="fixed bottom-0 left-0 right-0 bg-card border-t border-border shadow-lg z-50"
          style={{ animation: 'slideUp 0.2s ease-out' }}
        >
          <style>{`@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }`}</style>
          <div className="max-w-5xl mx-auto px-6 py-3 flex items-center justify-between">
            <span className="text-sm text-muted">Несохранённые изменения</span>
            <div className="flex items-center gap-2">
              <button
                onClick={cancelEditing}
                className="flex items-center gap-1.5 px-4 py-2 text-sm border border-border text-text rounded-lg hover:bg-background transition-colors"
              >
                <X className="h-4 w-4" />
                Отмена
              </button>
              <button
                onClick={saveEditing}
                disabled={editSaving}
                className="flex items-center gap-1.5 px-4 py-2 text-sm bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50"
              >
                <Save className="h-4 w-4" />
                {editSaving ? 'Сохранение...' : 'Сохранить'}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ─── Helpers ─── */

function InfoRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex items-start justify-between gap-2">
      <span className="text-muted shrink-0">{label}</span>
      <span className="text-text text-right">{value}</span>
    </div>
  );
}

function formatDate(dateStr: string): string {
  return new Date(dateStr).toLocaleDateString('ru-RU', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  });
}
