"use client";

// ============================================================================
// Placements Detail Page - All placements grouped by channel
// ============================================================================

import { useEffect, useMemo, useState } from "react";
import { useParams, useRouter, useSearchParams } from "next/navigation";
import Link from "next/link";
import {
  Loader2,
  Plus,
  ArrowLeft,
  ChevronDown,
  ChevronRight,
  Search,
  Filter,
  Eye,
  Users,
  ExternalLink,
  CircleDollarSign,
  TrendingUp,
  RefreshCw,
  Loader2 as LoaderIcon,
  BarChart3,
  ArrowUpDown,
  X,
  MoreVertical,
  Trash2,
  Copy,
  Pencil,
  Send,
  Check,
} from "lucide-react";
import { DashboardHeader } from "@/components/layout/dashboard-header";
import { useWorkspace } from "@/components/providers/workspace-provider";
import { demoAwarePlacementsApi } from "@/lib/demo";
import { placementsApi, creativesApi } from "@/lib/api";
import { AddChannelDialog } from "@/components/dialog-add-channel";
import { CreatePlacementsDialog } from "@/components/dialog-create-placements";
import { FiltersModal, PlacementFilters } from "@/components/filters-modal";
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
  DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Checkbox } from "@/components/ui/checkbox";
import { ColumnSelectorDialog } from "@/components/column-selector-dialog";
import { CreativeSelectDialog } from "@/components/creative-select-dialog";
import { cn } from "@/lib/utils";
import type {
  Project,
  Placement,
  PlacementWithStats,
  PlacementsGroupedByChannel,
  PlacementStatus,
  PlacementPostStatus,
  PlacementType,
  CostType,
  UpdatePlacementInProjectRequest,
} from "@/lib/types/api";
import {
  ALL_COLUMNS,
  DEFAULT_VISIBLE_COLUMNS,
  PLACEMENT_DEAL_STATUSES,
  PLACEMENT_POST_STATUSES,
  canEditPostStatus,
} from "@/lib/config/placement-columns";

type PlacementInlineDraft = {
  status?: PlacementStatus;
  comment?: string | null;
  creative_id?: string | null;
  creative_name?: string | null;
  placement_at?: string | null;
  payment_at?: string | null;
  cost?: { type: CostType; value: number } | null;
  cost_before_bargain?: { type: CostType; value: number } | null;
  placement_type?: PlacementType | null;
  format?: string | null;
  cpm?: number | null;
};

type EditableField = "status" | "placement_at" | "payment_at" | "cost" | "cost_before_bargain" | "format" | "comment" | "cost_format" | "cpm" | "creative_id" | "creative_name";

const FIXED_COST_TYPE: CostType = "fixed";

interface EditingCell {
  placementId: string;
  field: EditableField;
}

// ============================================================================
// Helpers
// ============================================================================

const formatNumber = (value: number | null | undefined): string => {
  if (value === null || value === undefined) return "—";
  return new Intl.NumberFormat("ru-RU").format(value);
};

const formatCurrency = (value: number | null | undefined): string => {
  if (value === null || value === undefined) return "—";
  return new Intl.NumberFormat("ru-RU", {
    style: "currency",
    currency: "RUB",
    minimumFractionDigits: 0,
    maximumFractionDigits: 0,
  }).format(value);
};

const formatCompact = (value: number | null | undefined): string => {
  if (value === null || value === undefined) return "—";
  if (value >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
  if (value >= 1000) return `${(value / 1000).toFixed(1)}K`;
  return value.toString();
};

const formatDate = (dateStr: string | null | undefined): string => {
  if (!dateStr) return "—";
  const date = new Date(dateStr);
  return date.toLocaleDateString("ru-RU", {
    day: "2-digit",
    month: "short",
    year: "numeric",
  });
};

const toDateInputValue = (iso: string | null | undefined): string => {
  if (!iso) return "";
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) return "";
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
};

const toIsoFromDateInput = (value: string): string | null => {
  if (!value) return null;
  const date = new Date(`${value}T00:00:00.000Z`);
  if (Number.isNaN(date.getTime())) return null;
  return date.toISOString();
};

const getStatusColor = (status: PlacementStatus): string => {
  switch (status) {
    case "Оплачено":
      return "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800";
    case "Согласование условий":
    case "Ждём ответа":
      return "bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-800";
    case "Отмена":
    case "Не подходит цена":
    case "Неактуально":
    case "Не отвечает":
      return "bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800";
    case "Оплатить":
      return "bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-400 dark:border-blue-800";
    default:
      return "bg-muted text-muted-foreground border-border";
  }
};

// ============================================================================
// Components
// ============================================================================

interface ChannelGroupProps {
  channel: Placement["channel"];
  placements: PlacementWithStats[];
  channelNumber: number;
  canEdit: boolean;
  getPlacementView: (placement: PlacementWithStats) => PlacementWithStats;
  handleDraftChange: (placementId: string, patch: PlacementInlineDraft) => void;
  editingCell: EditingCell | null;
  setEditingCell: (cell: EditingCell | null) => void;
  expanded: boolean;
  selectedPlacementIds: Set<string>;
  onToggle: (channelId: string, placementIds: string[]) => void;
  onTogglePlacement: (placementId: string) => void;
  visibleColumns: Set<string>;
  onOpenCreativeSelect?: (placement: PlacementWithStats) => void;
  onClearCreative?: (placementId: string) => void;
  onOpenCreativeSend?: (placement: PlacementWithStats) => void;
}

// ============================================================================
// Cell Renderer Component
// ============================================================================

interface CellRendererProps {
  column: (typeof ALL_COLUMNS)[number];
  placement: PlacementWithStats;
  idx: number;
  channelNumber: number;
  canEdit: boolean;
  editingCell: EditingCell | null;
  setEditingCell: (cell: EditingCell | null) => void;
  handleDraftChange: (placementId: string, patch: PlacementInlineDraft) => void;
  effectiveCost: number;
  cpf: number | null;
  cpm: number | null;
  selectedPlacementIds: Set<string>;
  onTogglePlacement: (placementId: string) => void;
  onOpenCreativeSelect?: (placement: PlacementWithStats) => void;
  onClearCreative?: (placementId: string) => void;
  onOpenCreativeSend?: (placement: PlacementWithStats) => void;
}

function CellRenderer({
  column,
  placement,
  idx,
  channelNumber,
  canEdit,
  editingCell,
  setEditingCell,
  handleDraftChange,
  effectiveCost,
  cpf,
  cpm,
  selectedPlacementIds,
  onTogglePlacement,
  onOpenCreativeSelect,
  onClearCreative,
  onOpenCreativeSend,
}: CellRendererProps) {
  const isEditing = editingCell?.placementId === placement.id;

  const getStatusColor = (status: PlacementStatus): string => {
    switch (status) {
      case "Оплачено":
        return "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400 dark:border-emerald-800";
      case "Согласование условий":
      case "Ждём ответа":
        return "bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-800";
      case "Отмена":
      case "Не подходит цена":
      case "Неактуально":
      case "Не отвечает":
        return "bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800";
      case "Оплатить":
        return "bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-400 dark:border-blue-800";
      default:
        return "bg-muted text-muted-foreground border-border";
    }
  };

  const formatCurrency = (value: number): string => {
    return new Intl.NumberFormat("ru-RU", {
      style: "currency",
      currency: "RUB",
      minimumFractionDigits: 0,
      maximumFractionDigits: 0,
    }).format(value);
  };

  const formatDate = (dateStr: string | null | undefined): string => {
    if (!dateStr) return "—";
    const date = new Date(dateStr);
    return date.toLocaleDateString("ru-RU", {
      day: "2-digit",
      month: "short",
      year: "numeric",
    });
  };

  const formatNumber = (value: number | null | undefined): string => {
    if (value === null || value === undefined) return "—";
    return new Intl.NumberFormat("ru-RU").format(value);
  };

  const toIsoFromDateInput = (value: string): string | null => {
    if (!value) return null;
    const date = new Date(`${value}T00:00:00.000Z`);
    if (Number.isNaN(date.getTime())) return null;
    return date.toISOString();
  };

  const toDateInputValue = (iso: string | null | undefined): string => {
    if (!iso) return "";
    const date = new Date(iso);
    if (Number.isNaN(date.getTime())) return "";
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  };

  const formatTime = (seconds: number | null | undefined): string => {
    if (!seconds) return "—";
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    if (hours > 24) {
      const days = Math.floor(hours / 24);
      return `${days}д ${hours % 24}ч`;
    }
    return `${hours}ч ${minutes}м`;
  };

  // Render specific columns
  switch (column.id) {
    case "checkbox":
      return (
        <td key={column.id} className="py-2 px-3">
          <Checkbox
            checked={selectedPlacementIds.has(placement.id)}
            onCheckedChange={() => onTogglePlacement(placement.id)}
            onClick={(e) => e.stopPropagation()}
          />
        </td>
      );

    case "number":
      return (
        <td key={column.id} className="py-2 px-3">
          <span className="text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
            {channelNumber}.{idx + 1}
          </span>
        </td>
      );

    case "status_deal":
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "status" ? (
            <select
              aria-label="Статус размещения"
              className={cn(
                "h-7 w-full rounded-md border border-input bg-background px-2 text-xs font-medium shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
                getStatusColor(placement.status)
              )}
              value={placement.status}
              onChange={(e) =>
                handleDraftChange(placement.id, {
                  status: e.target.value as PlacementStatus,
                })
              }
              onBlur={() => setEditingCell(null)}
            >
              {PLACEMENT_DEAL_STATUSES.map((status) => (
                <option key={status} value={status}>
                  {status}
                </option>
              ))}
            </select>
          ) : (
            <button
              type="button"
              disabled={!canEdit}
              className={cn(
                "group h-7 w-full rounded-md border px-2 text-xs font-medium shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default flex items-center justify-between gap-2",
                getStatusColor(placement.status)
              )}
              onClick={() =>
                canEdit && setEditingCell({ placementId: placement.id, field: "status" })
              }
            >
              <span>{placement.status}</span>
              <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-70 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "status_post":
      const postStatus = placement.placement_post?.status;
      return (
        <td key={column.id} className="py-2 px-3">
          {postStatus ? (
            <span className="text-xs px-2 py-1 rounded border inline-block">
              {postStatus}
            </span>
          ) : (
            <span className="text-muted-foreground">—</span>
          )}
        </td>
      );

    case "creative":
      const hasCreative = !!placement.creative_id;
      return (
        <td key={column.id} className="py-2 px-3">
          {hasCreative ? (
            <div className="flex items-center gap-2">
              <div className="w-8 h-8 rounded bg-muted flex items-center justify-center overflow-hidden shrink-0">
                <span className="text-xs">📷</span>
              </div>
              <span className="text-xs truncate max-w-[100px]">{placement.creative_name || "Без названия"}</span>
              <Button
                variant="ghost"
                size="sm"
                className="h-6 w-6 p-0 text-muted-foreground hover:text-primary"
                title="Отправить креатив"
                onClick={() => {
                  if (placement.creative_id) {
                    onOpenCreativeSend?.(placement);
                  }
                }}
              >
                <Send className="h-3.5 w-3.5" />
              </Button>
              <Button
                variant="ghost"
                size="sm"
                className="h-6 w-6 p-0 text-muted-foreground hover:text-destructive"
                title="Очистить креатив"
                onClick={() => {
                  if (placement.creative_id) {
                    onClearCreative?.(placement.id);
                  }
                }}
              >
                <X className="h-3.5 w-3.5" />
              </Button>
            </div>
          ) : (
            <button
              type="button"
              className="flex items-center gap-1 text-muted-foreground text-xs hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring rounded px-2 py-1"
              onClick={() => {
                if (canEdit) {
                  onOpenCreativeSelect?.(placement);
                }
              }}
            >
              <span>Выбрать креатив</span>
              <span className="text-primary font-medium">+</span>
            </button>
          )}
        </td>
      );

    case "invite_link":
      return (
        <td key={column.id} className="py-2 px-3">
          {placement.invite_link ? (
            <code className="text-xs bg-muted px-2 py-1 rounded truncate max-w-[200px]">
              {placement.invite_link}
            </code>
          ) : (
            <span className="text-muted-foreground">—</span>
          )}
        </td>
      );

    case "post_link":
      return (
        <td key={column.id} className="py-2 px-3">
          {placement.placement_post?.post?.url ? (
            <a
              href={placement.placement_post.post.url}
              target="_blank"
              rel="noopener noreferrer"
              className="text-xs text-primary hover:underline"
            >
              Пост
            </a>
          ) : (
            <span className="text-muted-foreground">—</span>
          )}
        </td>
      );

    case "cost":
      const isFixedFormat = placement.details?.cost?.type === "fixed";
      const canEditCost = canEdit && isFixedFormat;
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "cost" ? (
            <Input
              type="number"
              min="0"
              step="0.01"
              autoFocus
              className="h-7 min-w-[80px] bg-background px-2 text-xs"
              defaultValue={effectiveCost}
              onChange={(e) => {
                const raw = e.target.value;
                if (!raw) {
                  handleDraftChange(placement.id, { cost: null });
                  return;
                }
                const value = Number(raw);
                if (Number.isNaN(value)) return;
                handleDraftChange(placement.id, {
                  cost: { type: "fixed", value },
                });
              }}
              onBlur={() => setEditingCell(null)}
            />
          ) : (
            <button
              type="button"
              disabled={!canEditCost}
              className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEditCost && setEditingCell({ placementId: placement.id, field: "cost" })
              }
            >
              <span>{effectiveCost ? formatCurrency(effectiveCost) : "—"}</span>
              <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "cost_before":
      const costBefore = placement.details?.cost_before_bargain?.value;
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {costBefore !== null && costBefore !== undefined ? formatCurrency(costBefore) : "—"}
        </td>
      );

    case "cost_format":
      const costFormat = placement.details?.cost?.type || "fixed" as CostType;
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "cost_format" ? (
            <Select
              value={costFormat}
              onValueChange={(value: CostType) => {
                handleDraftChange(placement.id, {
                  cost: { type: value, value: placement.details?.cost?.value || 0 },
                });
              }}
            >
              <SelectTrigger className="h-7 w-[80px] bg-background px-2 text-xs">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="fixed">Фикс</SelectItem>
                <SelectItem value="cpm">CPM</SelectItem>
              </SelectContent>
            </Select>
          ) : (
            <button
              type="button"
              disabled={!canEdit}
              className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEdit && setEditingCell({ placementId: placement.id, field: "cost_format" })
              }
            >
              <span>{costFormat === "cpm" ? "CPM" : "Фикс"}</span>
              <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "placement_type":
      const placementType = placement.details?.placement_type;
      return (
        <td key={column.id} className="py-2 px-3">
          {placementType === "self_promo" ? "Взаимный пиар" : placementType === "standard" ? "Стандартный" : "—"}
        </td>
      );

    case "discount":
      const costBeforeBargain = placement.details?.cost_before_bargain?.value;
      const cost = placement.details?.cost?.value;
      const discount = costBeforeBargain && cost && costBeforeBargain > 0 ? ((costBeforeBargain - cost) / costBeforeBargain) * 100 : null;
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {discount ? `${discount.toFixed(1)}%` : "—"}
        </td>
      );

    case "payment_date":
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "payment_at" ? (
            <Input
              type="date"
              autoFocus
              className="h-7 min-w-[100px] bg-background px-2 text-xs"
              defaultValue={toDateInputValue(placement.details?.payment_at)}
              onChange={(e) =>
                handleDraftChange(placement.id, {
                  payment_at: toIsoFromDateInput(e.target.value),
                })
              }
              onBlur={() => setEditingCell(null)}
            />
          ) : (
            <button
              type="button"
              disabled={!canEdit}
              className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEdit && setEditingCell({ placementId: placement.id, field: "payment_at" })
              }
            >
              <span>{formatDate(placement.details?.payment_at)}</span>
              <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "planned_date":
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "placement_at" ? (
            <Input
              type="date"
              autoFocus
              className="h-7 min-w-[100px] bg-background px-2 text-xs"
              defaultValue={toDateInputValue(placement.details?.placement_at)}
              onChange={(e) =>
                handleDraftChange(placement.id, {
                  placement_at: toIsoFromDateInput(e.target.value),
                })
              }
              onBlur={() => setEditingCell(null)}
            />
          ) : (
            <button
              type="button"
              disabled={!canEdit}
              className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEdit && setEditingCell({ placementId: placement.id, field: "placement_at" })
              }
            >
              <span>{formatDate(placement.details?.placement_at)}</span>
              <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "actual_date":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {placement.placement_post?.post?.published_at
            ? formatDate(placement.placement_post.post.published_at)
            : "—"}
        </td>
      );

    case "format":
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "format" ? (
            <Input
              autoFocus
              className="h-7 min-w-[140px] bg-background px-2 text-xs"
              defaultValue={placement.details?.format || ""}
              onChange={(e) =>
                handleDraftChange(placement.id, {
                  format: e.target.value ? e.target.value : null,
                })
              }
              onBlur={() => setEditingCell(null)}
            />
          ) : (
            <button
              type="button"
              disabled={!canEdit}
              className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-0 text-left text-xs text-muted-foreground hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEdit && setEditingCell({ placementId: placement.id, field: "format" })
              }
            >
              <span className="truncate w-full">{placement.details?.format || "—"}</span>
              <Pencil className="h-3 w-3 shrink-0 ml-1 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "time_top":
      const timeOnTop = placement.placement_post?.time_on_top;
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {formatTime(timeOnTop)}
        </td>
      );

    case "link_created":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {formatDate(placement.invite_link_created_at)}
        </td>
      );

    case "post_deleted":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {formatDate(placement.placement_post?.post?.deleted_from_channel_at)}
        </td>
      );

    case "subscriptions":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {formatNumber(placement.placement_post?.subscriptions_count)}
        </td>
      );

    case "views":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {formatNumber(placement.placement_post?.views_count)}
        </td>
      );

    case "cpf":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {cpf ? formatCurrency(cpf) : "—"}
        </td>
      );

    case "cpm":
      const isCpmFormat = placement.details?.cost?.type === "cpm";
      const canEditCpm = canEdit && isCpmFormat;
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "cpm" ? (
            <Input
              type="number"
              min="0"
              step="0.01"
              autoFocus
              className="h-7 min-w-[80px] bg-background px-2 text-xs"
              defaultValue={cpm || 0}
              onChange={(e) => {
                const raw = e.target.value;
                if (!raw) return;
                const value = Number(raw);
                if (Number.isNaN(value)) return;
                const currentCost = placement.details?.cost;
                handleDraftChange(placement.id, {
                  cost: { type: "cpm", value: (currentCost?.type === "cpm" ? currentCost.value : 0) },
                  cpm: value,
                });
              }}
              onBlur={() => setEditingCell(null)}
            />
          ) : (
            <button
              type="button"
              disabled={!canEditCpm}
              className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEditCpm && setEditingCell({ placementId: placement.id, field: "cpm" })
              }
            >
              <span>{cpm ? formatCurrency(cpm) : "—"}</span>
              <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    case "conversion_24h":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).conversion_24h !== undefined ? `${(placement as any).conversion_24h}%` : "—"}
        </td>
      );

    case "conversion_48h":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).conversion_48h !== undefined ? `${(placement as any).conversion_48h}%` : "—"}
        </td>
      );

    case "conversion_total":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).conversion_total !== undefined ? `${(placement as any).conversion_total}%` : "—"}
        </td>
      );

    case "total_unsubs":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).total_unsubs !== undefined ? (placement as any).total_unsubs : "—"}
        </td>
      );

    case "unsub_percent":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).unsub_percent !== undefined ? `${(placement as any).unsub_percent}%` : "—"}
        </td>
      );

    case "total_active":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).total_active !== undefined ? (placement as any).total_active : "—"}
        </td>
      );

    case "time_in_feed":
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">
          {(placement as any).time_in_feed !== undefined ? (placement as any).time_in_feed : "—"}
        </td>
      );

    case "invite_type":
      return (
        <td key={column.id} className="py-2 px-3">
          {placement.invite_link_type === "approval" ? "С заявками" : "Открытая"}
        </td>
      );

    case "comment":
      return (
        <td key={column.id} className="py-2 px-3">
          {isEditing && editingCell?.field === "comment" ? (
            <Input
              aria-label="Комментарий"
              autoFocus
              className="h-7 min-w-[240px] bg-background px-2 text-xs"
              value={placement.comment || ""}
              onChange={(e) =>
                handleDraftChange(placement.id, {
                  comment: e.target.value ? e.target.value : null,
                })
              }
              onBlur={() => setEditingCell(null)}
            />
          ) : (
            <button
              type="button"
              disabled={!canEdit}
              className="group inline-flex min-h-[1.75rem] max-w-[240px] items-center justify-between rounded px-0 text-left text-xs text-muted-foreground hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
              onClick={() =>
                canEdit && setEditingCell({ placementId: placement.id, field: "comment" })
              }
            >
              <span className="truncate w-full">{placement.comment || "—"}</span>
              <Pencil className="h-3 w-3 shrink-0 ml-1 opacity-0 group-hover:opacity-50 transition-opacity" />
            </button>
          )}
        </td>
      );

    default:
      return (
        <td key={column.id} className="py-2 px-3 text-muted-foreground text-xs italic">
          Недоступно
        </td>
      );
  }
}

function ChannelGroup({
  channel,
  placements,
  channelNumber,
  canEdit,
  getPlacementView,
  handleDraftChange,
  editingCell,
  setEditingCell,
  expanded,
  selectedPlacementIds,
  onToggle,
  onTogglePlacement,
  visibleColumns,
  onOpenCreativeSelect,
  onClearCreative,
  onOpenCreativeSend,
}: ChannelGroupProps) {
  const [isOpen, setIsOpen] = useState(true);

  useEffect(() => {
    setIsOpen(expanded);
  }, [expanded]);

  const placementIds = placements.map((p) => p.id);
  const allSelected = placementIds.length > 0 && placementIds.every((id) => selectedPlacementIds.has(id));

  const totalCost = placements.reduce((sum, p) => sum + (p.details?.cost?.value || 0), 0);
  const totalSubscriptions = placements.reduce((sum, p) => sum + (p.placement_post?.subscriptions_count || 0), 0);
  const totalViews = placements.reduce((sum, p) => sum + (p.placement_post?.views_count || 0), 0);

  const avgCpf = totalSubscriptions > 0 ? totalCost / totalSubscriptions : null;
  const avgCpm = totalViews > 0 ? (totalCost / totalViews) * 1000 : null;

  if (placements.length === 0) return null;

  return (
    <div className="mb-3">
      <Collapsible open={isOpen} onOpenChange={setIsOpen}>
        <CollapsibleTrigger asChild>
          <div className="flex items-center gap-2 px-3 py-2 bg-muted/50 rounded-lg cursor-pointer hover:bg-muted transition-colors">
            <Checkbox
              checked={allSelected}
              onCheckedChange={() => onToggle(channel.id, placementIds)}
              onClick={(e) => e.stopPropagation()}
            />
            <ChevronDown
              className={cn(
                "h-4 w-4 text-muted-foreground transition-transform",
                !isOpen && "-rotate-90"
              )}
            />
            <span className="text-xs font-mono text-muted-foreground bg-background px-1.5 py-0.5 rounded w-6 text-center">
              {channelNumber}
            </span>
            <Avatar className="h-8 w-8">
              <AvatarImage
                src={channel.username ? `https://t.me/i/userpic/160/${channel.username}.jpg` : undefined}
                alt={channel.title}
              />
              <AvatarFallback className="text-sm">
                {channel.title[0]}
              </AvatarFallback>
            </Avatar>
            <div className="flex flex-col">
              <span className="font-medium text-sm">{channel.title}</span>
              {channel.username && (
                <span className="text-xs text-muted-foreground">@{channel.username}</span>
              )}
            </div>
            {channel.username && (
              <div className="flex items-center gap-1 ml-2">
                <a
                  href={`https://telemetr.me/channel/${channel.username}`}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="flex items-center justify-center h-6 w-6 hover:opacity-75 transition-opacity"
                  onClick={(e) => e.stopPropagation()}
                >
                  <img src="/icons/telemetr.jpg" alt="TgStat" className="h-5 w-5 rounded" />
                </a>
                <a
                  href={`https://tgstat.ru/channel/@${channel.username}`}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="flex items-center justify-center h-6 w-6 hover:opacity-75 transition-opacity"
                  onClick={(e) => e.stopPropagation()}
                >
                  <img src="/icons/tgstat.jpg" alt="TgStat" className="h-5 w-5 rounded" />
                </a>
              </div>
            )}
            <Badge variant="outline" className="ml-auto text-xs">
              {placements.length}
            </Badge>
            <div className="flex items-center gap-3 text-xs text-muted-foreground ml-2">
              <span className="flex items-center gap-1">
                <Users className="h-3 w-3" />
                {formatCompact(channel.subscribers_count)}
              </span>
              <span className="flex items-center gap-1">
                <Eye className="h-3 w-3" />
                {formatCompact(totalViews)}
              </span>
            </div>
          </div>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-b">
                  {ALL_COLUMNS.filter((c) => visibleColumns.has(c.id)).map((column) => (
                    <th
                      key={column.id}
                      className={cn(
                        "py-2 px-3 font-medium text-muted-foreground",
                        column.width || "w-auto"
                      )}
                    >
                      {column.label}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {placements.map((placement, idx) => {
                  const viewPlacement = getPlacementView(placement);
                  const effectiveCost = viewPlacement.details?.cost?.value || 0;

                  const cpf = viewPlacement.placement_post?.subscriptions_count
                    ? effectiveCost / viewPlacement.placement_post.subscriptions_count
                    : null;
                  const cpm = viewPlacement.placement_post?.views_count
                    ? (effectiveCost / viewPlacement.placement_post.views_count) * 1000
                    : null;

                  return (
                    <tr key={placement.id} className="border-b last:border-0 hover:bg-muted/30">
                      {ALL_COLUMNS.filter((c) => visibleColumns.has(c.id)).map((column) => (
                        <CellRenderer
                          key={column.id}
                          column={column}
                          placement={viewPlacement}
                          idx={idx}
                          channelNumber={channelNumber}
                          canEdit={canEdit}
                          editingCell={editingCell}
                          setEditingCell={setEditingCell}
                          handleDraftChange={handleDraftChange}
                          effectiveCost={effectiveCost}
                          cpf={cpf}
                          cpm={cpm}
                          selectedPlacementIds={selectedPlacementIds}
                          onTogglePlacement={onTogglePlacement}
                          onOpenCreativeSelect={onOpenCreativeSelect}
                          onClearCreative={onClearCreative}
                          onOpenCreativeSend={onOpenCreativeSend}
                        />
                      ))}
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </CollapsibleContent>
      </Collapsible>
    </div>
  );
}

// ============================================================================
// Main Component
// ============================================================================

export default function PurchasePlanDetailPage() {
  const params = useParams();
  const router = useRouter();
  const searchParams = useSearchParams();
  const workspaceId = params?.workspaceId as string;
  const projectId = params?.projectId as string;
  const { projects, hasPermission } = useWorkspace();

  const [placements, setPlacements] = useState<Placement[]>([]);
  const [loading, setLoading] = useState(true);
  const [project, setProject] = useState<Project | null>(null);
  const [draftsByPlacementId, setDraftsByPlacementId] = useState<Record<string, PlacementInlineDraft>>({});
  const [savingEdits, setSavingEdits] = useState(false);
  const [saveError, setSaveError] = useState<string | null>(null);
  const [editingCell, setEditingCell] = useState<EditingCell | null>(null);

  // Create placements dialog
  const prefilledCreativeId = searchParams.get("creative_id");
  const [prefilledChannelIds, setPrefilledChannelIds] = useState<string[] | null>(null);
  const [showCreateDialog, setShowCreateDialog] = useState(false);

  const [searchQuery, setSearchQuery] = useState("");
  const [statusFilter, setStatusFilter] = useState("all");
  const [expandedAll, setExpandedAll] = useState(true);
  const [filtersOpen, setFiltersOpen] = useState(false);
  const [selectedPlacementIds, setSelectedPlacementIds] = useState<Set<string>>(new Set());
  const [showCopyDialog, setShowCopyDialog] = useState(false);
  const [targetProjectId, setTargetProjectId] = useState<string | null>(null);
  const [copyingChannels, setCopyingChannels] = useState(false);
  const [visibleColumns, setVisibleColumns] = useState<Set<string>>(
    new Set(DEFAULT_VISIBLE_COLUMNS)
  );
  const [showColumnsDialog, setShowColumnsDialog] = useState(false);

  // Creative selection dialog
  const [showCreativeDialog, setShowCreativeDialog] = useState(false);
  const [selectedPlacementForCreative, setSelectedPlacementForCreative] = useState<PlacementWithStats | null>(null);
  const [creatives, setCreatives] = useState<Array<{ id: string; name: string; thumbnail_url?: string | null }>>([]);

  const loadCreatives = async () => {
    try {
      const response = await creativesApi.list(workspaceId, {
        project_id: projectId,
        include_archived: false,
        size: 100,
      });
      setCreatives(response.items);
    } catch (err) {
      console.error("Failed to load creatives:", err);
    }
  };

  useEffect(() => {
    if (showCreativeDialog && creatives.length === 0) {
      loadCreatives();
    }
  }, [showCreativeDialog]);

  const togglePlacementSelection = (placementId: string) => {
    setSelectedPlacementIds((prev) => {
      const next = new Set(prev);
      if (next.has(placementId)) {
        next.delete(placementId);
      } else {
        next.add(placementId);
      }
      return next;
    });
  };
  const [filters, setFilters] = useState<PlacementFilters>({
    channelFilters: {
      name: { value: "", condition: "contains" },
      username: { value: "", condition: "contains" },
      subscribersMin: null,
      subscribersMax: null,
    },
    placementFilters: {
      statuses: [],
      postStatuses: [],
      hasCreative: null,
      costMin: null,
      costMax: null,
      costFormat: null,
      placementType: null,
      dateFrom: null,
      dateTo: null,
    },
    sort: null,
  });

  const { isDemoMode } = useWorkspace();

  useEffect(() => {
    const proj = projects.find((p) => p.id === projectId);
    if (proj) {
      setProject(proj);
    }
  }, [projects, projectId]);

  // Auto-open create dialog if creative_id is provided
  useEffect(() => {
    if (prefilledCreativeId && project) {
      setShowCreateDialog(true);
    }
  }, [prefilledCreativeId, project]);

  // Auto-open create dialog if channel_ids are provided
  useEffect(() => {
    if (prefilledChannelIds && project) {
      setShowCreateDialog(true);
    }
  }, [prefilledChannelIds, project]);

  useEffect(() => {
    loadPlacements();
  }, [workspaceId, projectId]);

  const loadPlacements = async () => {
    try {
      setLoading(true);
      const api = isDemoMode ? demoAwarePlacementsApi : placementsApi;
      const data = await api.getByProject(workspaceId, projectId);
      
      const placementsWithCreativeNames = await Promise.all(
        data.map(async (placement) => {
          if (placement.creative_id && !placement.creative_name && !isDemoMode) {
            try {
              const creative = await creativesApi.get(workspaceId, placement.creative_id);
              return { ...placement, creative_name: creative.name };
            } catch (err) {
              console.error("Failed to fetch creative name:", err);
              return placement;
            }
          }
          return placement;
        })
      );
      
      setPlacements(placementsWithCreativeNames);
      setDraftsByPlacementId({});
      setSaveError(null);
    } catch (err) {
      console.error("Failed to load placements:", err);
    } finally {
      setLoading(false);
    }
  };

  // Creative selection handlers
  const handleOpenCreativeSelect = (placement: PlacementWithStats) => {
    setSelectedPlacementForCreative(placement);
    setShowCreativeDialog(true);
  };

  const handleClearCreative = (placementId: string) => {
    handleDraftChange(placementId, { creative_id: null, creative_name: null });
  };

  const handleOpenCreativeSend = async (placement: PlacementWithStats) => {
    if (!placement.creative_id || isDemoMode) return;
    
    try {
      await placementsApi.sendCreative(workspaceId, projectId, placement.id);
      alert("Креатив успешно отправлен");
    } catch (err) {
      console.error("Failed to send creative:", err);
      alert("Не удалось отправить креатив");
    }
  };

  const handleSelectCreative = (creative: { id: string; name: string }) => {
    if (selectedPlacementForCreative) {
      handleDraftChange(selectedPlacementForCreative.id, {
        creative_id: creative.id,
        creative_name: creative.name,
      });
    }
  };

  const canEditPlacements = hasPermission("placements_write") && !isDemoMode;
  const hasUnsavedChanges = Object.keys(draftsByPlacementId).length > 0;

  const handleDraftChange = (placementId: string, patch: PlacementInlineDraft) => {
    if (!canEditPlacements) return;
    setDraftsByPlacementId((prev) => ({
      ...prev,
      [placementId]: {
        ...(prev[placementId] || {}),
        ...patch,
      },
    }));
  };

  const getPlacementView = (placement: PlacementWithStats): PlacementWithStats => {
    const draft = draftsByPlacementId[placement.id];
    if (!draft) return placement;

    const nextStatus = draft.status ?? placement.status;
    const nextComment = draft.comment !== undefined ? draft.comment : placement.comment;

    const nextDetails = {
      ...(placement.details || {
        placement_at: null,
        payment_at: null,
        cost: null,
        cost_before_bargain: null,
        placement_type: null,
        format: null,
        comment: null,
        creative_id: null,
      }),
      placement_at:
        draft.placement_at !== undefined
          ? draft.placement_at
          : placement.details?.placement_at ?? null,
      payment_at:
        draft.payment_at !== undefined
          ? draft.payment_at
          : placement.details?.payment_at ?? null,
      cost:
        draft.cost !== undefined
          ? draft.cost
          : placement.details?.cost ?? null,
      cost_before_bargain:
        draft.cost_before_bargain !== undefined
          ? draft.cost_before_bargain
          : placement.details?.cost_before_bargain ?? null,
      placement_type:
        draft.placement_type !== undefined
          ? draft.placement_type
          : placement.details?.placement_type ?? null,
      format:
        draft.format !== undefined
          ? draft.format
          : placement.details?.format ?? null,
      creative_id:
        draft.creative_id !== undefined
          ? draft.creative_id
          : placement.details?.creative_id ?? null,
    };

    return {
      ...placement,
      status: nextStatus,
      comment: nextComment,
      creative_id: draft.creative_id ?? placement.creative_id,
      creative_name: draft.creative_name ?? placement.creative_name,
      details: nextDetails,
    };
  };

  const handleCancelEdits = () => {
    setDraftsByPlacementId({});
    setSaveError(null);
    setEditingCell(null);
  };

  const handleSaveEdits = async () => {
    if (!canEditPlacements) return;
    const entries = Object.entries(draftsByPlacementId);
    if (entries.length === 0) return;

    try {
      setSavingEdits(true);
      setSaveError(null);

      for (const [placementId, draft] of entries) {
        await placementsApi.updateInProject(workspaceId, projectId, placementId, draft as UpdatePlacementInProjectRequest);
      }

      await loadPlacements();
    } catch (err: unknown) {
      console.error("Failed to save placement edits:", err);
      const message =
        typeof err === "object" && err && "detail" in err
          ? String((err as any).detail)
          : "Не удалось сохранить изменения";
      setSaveError(message);
      setSavingEdits(false);
    }
  };

  const toggleChannelSelection = (channelId: string, placementIds: string[]) => {
    const allSelected = placementIds.length > 0 && placementIds.every((id) => selectedPlacementIds.has(id));
    setSelectedPlacementIds((prev) => {
      const next = new Set(prev);
      if (allSelected) {
        placementIds.forEach((id) => next.delete(id));
      } else {
        placementIds.forEach((id) => next.add(id));
      }
      return next;
    });
  };

  const toggleAllChannels = () => {
    const allPlacementIds = new Set(filteredGroups.flatMap((g) => g.placements.map((p) => p.id)));
    if (selectedPlacementIds.size === allPlacementIds.size) {
      setSelectedPlacementIds(new Set());
    } else {
      setSelectedPlacementIds(allPlacementIds);
    }
  };

  const clearSelection = () => {
    setSelectedPlacementIds(new Set());
  };

  const handleDeletePlacements = async () => {
    if (!canEditPlacements) return;
    const selectedIds = Array.from(selectedPlacementIds);
    if (selectedIds.length === 0) return;

    try {
      for (const placementId of selectedIds) {
        await placementsApi.deleteInProject(workspaceId, projectId, placementId);
      }
      await loadPlacements();
      clearSelection();
    } catch (err) {
      console.error("Failed to delete placements:", err);
    }
  };

  const handleCopyPlacements = async () => {
    if (!targetProjectId) return;
    const selectedPlacements = placements.filter((p) => selectedPlacementIds.has(p.id));
    if (selectedPlacements.length === 0) return;

    try {
      setCopyingChannels(true);
      await placementsApi.createBulk(workspaceId, targetProjectId, {
        creative_id: undefined,
        channels: selectedPlacements.map((p) => ({
          channel_id: p.channel.id,
          status: String(p.status),
          comment: p.comment || undefined,
          details: p.details ? {
            placement_at: p.details.placement_at || undefined,
            payment_at: p.details.payment_at || undefined,
            cost: p.details.cost || undefined,
            cost_before_bargain: p.details.cost_before_bargain?.value || undefined,
            placement_type: p.details.placement_type || undefined,
            format: p.details.format || undefined,
            creative_id: p.details.creative_id || undefined,
          } : undefined,
        })),
      });
      setShowCopyDialog(false);
      setTargetProjectId(null);
      clearSelection();
    } catch (err) {
      console.error("Failed to copy placements:", err);
    } finally {
      setCopyingChannels(false);
    }
  };

  const groupedPlacements = useMemo(() => {
    const groups: Record<string, { channel: Placement["channel"]; placements: PlacementWithStats[] }> = {};

    placements.forEach((placement) => {
      const channelId = placement.channel.id;
      if (!groups[channelId]) {
        groups[channelId] = {
          channel: placement.channel,
          placements: [],
        };
      }

      const cpf = placement.placement_post?.subscriptions_count
        ? (placement.details?.cost?.value || 0) / placement.placement_post.subscriptions_count
        : null;
      const cpm = placement.placement_post?.views_count
        ? ((placement.details?.cost?.value || 0) / placement.placement_post.views_count) * 1000
        : null;

      groups[channelId].placements.push({
        ...placement,
        cpf,
        cpm,
      });
    });

    Object.values(groups).forEach((group) => {
      group.placements.sort((a, b) => {
        const dateA = new Date(a.created_at).getTime();
        const dateB = new Date(b.created_at).getTime();
        return dateA - dateB;
      });
    });

    return Object.entries(groups).map(([_, group]) => ({
      channel: group.channel,
      placements: group.placements,
    })).sort((a, b) => a.channel.title.localeCompare(b.channel.title));
  }, [placements]);

  const summary = useMemo(() => {
    const totalCost = placements.reduce((sum, p) => sum + (p.details?.cost?.value || 0), 0);
    const totalSubscriptions = placements.reduce((sum, p) => sum + (p.placement_post?.subscriptions_count || 0), 0);
    const totalViews = placements.reduce((sum, p) => sum + (p.placement_post?.views_count || 0), 0);

    return {
      totalPlacements: placements.length,
      totalChannels: groupedPlacements.length,
      totalCost,
      totalSubscriptions,
      totalViews,
      avgCpf: totalSubscriptions > 0 ? totalCost / totalSubscriptions : null,
      avgCpm: totalViews > 0 ? (totalCost / totalViews) * 1000 : null,
    };
  }, [placements, groupedPlacements]);

   const filteredGroups = useMemo(() => {
    let filtered = groupedPlacements.filter((group) => {
      // Channel filters
      const nameFilter = filters.channelFilters.name;
      const channelNameMatch = !nameFilter.value ||
        (nameFilter.condition === "contains"
          ? group.channel.title.toLowerCase().includes(nameFilter.value.toLowerCase())
          : nameFilter.condition === "not_contains"
          ? !group.channel.title.toLowerCase().includes(nameFilter.value.toLowerCase())
          : group.channel.title.toLowerCase() === nameFilter.value.toLowerCase());

      const usernameFilter = filters.channelFilters.username;
      const channelUsernameMatch = !usernameFilter.value ||
        (usernameFilter.condition === "contains"
          ? group.channel.username?.toLowerCase().includes(usernameFilter.value.toLowerCase())
          : usernameFilter.condition === "not_contains"
          ? !group.channel.username?.toLowerCase().includes(usernameFilter.value.toLowerCase())
          : group.channel.username?.toLowerCase() === usernameFilter.value.toLowerCase());

      const subscribersMinMatch = filters.channelFilters.subscribersMin === null ||
        (group.channel.subscribers_count || 0) >= filters.channelFilters.subscribersMin;
      const subscribersMaxMatch = filters.channelFilters.subscribersMax === null ||
        (group.channel.subscribers_count || 0) <= filters.channelFilters.subscribersMax;

      // Placement filters
      const placementsWithStatus = filters.placementFilters.statuses.length === 0 ||
        group.placements.some((p) => filters.placementFilters.statuses.includes(p.status));

      const costMinMatch = filters.placementFilters.costMin === null ||
        group.placements.some((p) => (p.details?.cost?.value || 0) >= filters.placementFilters.costMin!);
      const costMaxMatch = filters.placementFilters.costMax === null ||
        group.placements.some((p) => (p.details?.cost?.value || 0) <= filters.placementFilters.costMax!);

      let dateMatch = true;
      if (filters.placementFilters.dateFrom || filters.placementFilters.dateTo) {
        dateMatch = group.placements.some((p) => {
          if (!p.details?.placement_at) return false;
          const placementDate = new Date(p.details.placement_at);
          const fromMatch = !filters.placementFilters.dateFrom ||
            placementDate >= new Date(filters.placementFilters.dateFrom);
          const toMatch = !filters.placementFilters.dateTo ||
            placementDate <= new Date(filters.placementFilters.dateTo);
          return fromMatch && toMatch;
        });
      }

      // New filters
      const postStatusMatch = filters.placementFilters.postStatuses.length === 0 ||
        group.placements.some((p) => filters.placementFilters.postStatuses.includes(p.placement_post?.status as PlacementPostStatus));

      const hasCreativeMatch = filters.placementFilters.hasCreative === null ||
        group.placements.some((p) =>
          (filters.placementFilters.hasCreative === true && p.creative_id !== null) ||
          (filters.placementFilters.hasCreative === false && p.creative_id === null)
        );

      const costFormatMatch = filters.placementFilters.costFormat === null ||
        group.placements.some((p) => p.details?.cost?.type === filters.placementFilters.costFormat);

      const placementTypeMatch = filters.placementFilters.placementType === null ||
        group.placements.some((p) => p.details?.placement_type === filters.placementFilters.placementType);

      return channelNameMatch && channelUsernameMatch && subscribersMinMatch &&
        subscribersMaxMatch && placementsWithStatus && costMinMatch && costMaxMatch && dateMatch &&
        postStatusMatch && hasCreativeMatch && costFormatMatch && placementTypeMatch;
    });

    // Sort placements inside each channel
    if (filters.sort) {
      filtered = filtered.map((group) => ({
        ...group,
        placements: [...group.placements].sort((a, b) => {
          let aValue = 0;
          let bValue = 0;

          switch (filters.sort!.field) {
            case "cost":
              aValue = a.details?.cost?.value || 0;
              bValue = b.details?.cost?.value || 0;
              break;
            case "date":
              aValue = a.details?.placement_at ? new Date(a.details.placement_at).getTime() : 0;
              bValue = b.details?.placement_at ? new Date(b.details.placement_at).getTime() : 0;
              break;
            case "cpm":
              aValue = a.cpm || 0;
              bValue = b.cpm || 0;
              break;
            case "cpf":
              aValue = a.cpf || 0;
              bValue = b.cpf || 0;
              break;
            case "subscribers":
              aValue = a.placement_post?.subscriptions_count || 0;
              bValue = b.placement_post?.subscriptions_count || 0;
              break;
            case "views":
              aValue = a.placement_post?.views_count || 0;
              bValue = b.placement_post?.views_count || 0;
              break;
            case "created":
              aValue = new Date(a.created_at).getTime();
              bValue = new Date(b.created_at).getTime();
              break;
          }

          return filters.sort!.direction === "asc"
            ? aValue - bValue
            : bValue - aValue;
        }),
      }));
    }

    return filtered;
  }, [groupedPlacements, filters]);

  const canWrite = hasPermission("placements_write");

  const hasActiveFilters = filters.channelFilters.name.value ||
    filters.channelFilters.username.value ||
    filters.channelFilters.subscribersMin !== null ||
    filters.channelFilters.subscribersMax !== null ||
    filters.placementFilters.statuses.length > 0 ||
    filters.placementFilters.postStatuses.length > 0 ||
    filters.placementFilters.hasCreative !== null ||
    filters.placementFilters.costMin !== null ||
    filters.placementFilters.costMax !== null ||
    filters.placementFilters.costFormat !== null ||
    filters.placementFilters.placementType !== null ||
    filters.placementFilters.dateFrom ||
    filters.placementFilters.dateTo ||
    filters.sort;

  if (loading) {
    return (
      <>
        <DashboardHeader breadcrumbs={[{ label: "Загрузка..." }]} />
        <div className="flex items-center justify-center py-12">
          <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
        </div>
      </>
    );
  }

  return (
    <>
      <DashboardHeader
        breadcrumbs={[
          { label: "Главная", href: `/dashboard/${workspaceId}` },
          { label: "Размещения", href: `/dashboard/${workspaceId}/purchase-plans` },
          { label: project?.title || "Проект" },
        ]}
      />

      <div className="flex flex-1 flex-col gap-4 p-4 pt-0 mt-4">
        {/* Header */}
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-4">
            <Button
              variant="ghost"
              size="icon"
              onClick={() => router.push(`/dashboard/${workspaceId}/purchase-plans`)}
            >
              <ArrowLeft className="h-4 w-4" />
            </Button>
            <div>
              <h1 className="text-2xl font-bold tracking-tight">
                План закупов проекта «{project?.title || "Проект"}»
              </h1>
              <p className="text-muted-foreground">
                Список каналов, в которых планируем размещения для проекта
              </p>
            </div>
          </div>
          <div className="flex items-center gap-2">
            <AddChannelDialog
              workspaceId={workspaceId}
              projectId={projectId}
              existingPlacements={placements}
              onSuccess={loadPlacements}
            >
              <Button variant="outline" disabled={isDemoMode}>
                <Plus className="h-4 w-4 mr-2" />
                Добавить канал
              </Button>
            </AddChannelDialog>
            <CreatePlacementsDialog
              workspaceId={workspaceId}
              projectId={projectId}
              existingPlacements={placements}
              prefilledCreativeId={prefilledCreativeId}
              prefilledChannelIds={prefilledChannelIds}
              open={showCreateDialog}
              onOpenChange={setShowCreateDialog}
              onSuccess={loadPlacements}
            >
              <Button disabled={isDemoMode}>
                <Plus className="h-4 w-4 mr-2" />
                Создать размещения
              </Button>
            </CreatePlacementsDialog>
          </div>
        </div>

        {/* Filters */}
        <div className="flex items-center gap-3">
          <div className="flex items-center gap-2">
            <Checkbox
              id="select-all-channels"
              checked={filteredGroups.length > 0 && filteredGroups.every((g) => g.placements.every((p) => selectedPlacementIds.has(p.id)))}
              onCheckedChange={toggleAllChannels}
            />
            <label htmlFor="select-all-channels" className="text-sm cursor-pointer">
              {selectedPlacementIds.size > 0
                ? `Размещений: ${selectedPlacementIds.size}`
                : "Выбрать все"}
            </label>
          </div>
          <div className="relative flex-1 max-w-sm">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
            <Input
              placeholder="Поиск канала..."
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              className="pl-9"
            />
          </div>
          <Button
            variant={hasActiveFilters ? "default" : "outline"}
            size="sm"
            onClick={() => setFiltersOpen(true)}
          >
            <Filter className="h-4 w-4 mr-1" />
            Фильтры и сортировка
            {hasActiveFilters && (
              <span className="ml-1 px-1.5 py-0.5 text-xs bg-primary-foreground/20 rounded">
                ✓
              </span>
            )}
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => setShowColumnsDialog(true)}
          >
            <BarChart3 className="h-4 w-4 mr-1" />
            Колонки
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => setExpandedAll(!expandedAll)}
          >
            {expandedAll ? "Свернуть все" : "Развернуть все"}
          </Button>
          {selectedPlacementIds.size > 0 && (
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button variant="outline" size="sm">
                  <MoreVertical className="h-4 w-4" />
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end">
                <DropdownMenuItem onClick={() => setShowCopyDialog(true)}>
                  <Copy className="h-4 w-4 mr-2" />
                  Добавить в другой проект
                </DropdownMenuItem>
                <DropdownMenuSeparator />
                <DropdownMenuItem onClick={handleDeletePlacements} className="text-destructive">
                  <Trash2 className="h-4 w-4 mr-2" />
                  Удалить
                </DropdownMenuItem>
                <DropdownMenuItem onClick={clearSelection}>
                  <X className="h-4 w-4 mr-2" />
                  Отменить выбор
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          )}
        </div>

        {/* Placements List */}
        {filteredGroups.length === 0 ? (
          <Card>
            <CardContent className="flex flex-col items-center justify-center py-12">
              <TrendingUp className="h-12 w-12 text-muted-foreground mb-4" />
              <h3 className="text-lg font-semibold mb-2">Нет размещений</h3>
              <p className="text-muted-foreground text-center">
                {placements.length === 0
                  ? "Добавьте первое размещение для этого проекта"
                  : "Попробуйте изменить параметры поиска"}
              </p>
            </CardContent>
          </Card>
        ) : (
          <div className="space-y-1">
            {filteredGroups.map((group, idx) => (
              <ChannelGroup
                key={group.channel.id}
                channel={group.channel}
                placements={group.placements}
                channelNumber={idx + 1}
                canEdit={canEditPlacements}
                getPlacementView={getPlacementView}
                handleDraftChange={handleDraftChange}
                editingCell={editingCell}
                setEditingCell={setEditingCell}
                expanded={expandedAll}
                selectedPlacementIds={selectedPlacementIds}
                onToggle={(channelId, placementIds) => toggleChannelSelection(channelId, placementIds)}
                onTogglePlacement={togglePlacementSelection}
                visibleColumns={visibleColumns}
                onOpenCreativeSelect={handleOpenCreativeSelect}
                onClearCreative={handleClearCreative}
                onOpenCreativeSend={handleOpenCreativeSend}
              />
            ))}
          </div>
        )}
      </div>

      {hasUnsavedChanges && (
        <div className="fixed bottom-4 right-4 z-50">
          <div className="rounded-lg border bg-background/95 p-3 shadow-lg backdrop-blur">
            <div className="flex items-center gap-2">
              <Button
                type="button"
                variant="outline"
                onClick={handleCancelEdits}
                disabled={savingEdits}
              >
                Отменить
              </Button>
              <Button
                type="button"
                onClick={handleSaveEdits}
                disabled={savingEdits}
              >
                {savingEdits ? (
                  <>
                    <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                    Сохранение...
                  </>
                ) : (
                  "Сохранить"
                )}
              </Button>
            </div>
            {saveError && (
              <div className="mt-2 text-xs text-destructive">
                {saveError}
              </div>
            )}
          </div>
        </div>
      )}

      <Dialog open={showCopyDialog} onOpenChange={setShowCopyDialog}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Добавить в другой проект</DialogTitle>
            <DialogDescription>
              Выберите проект, в который хотите добавить {selectedPlacementIds.size} размещений
            </DialogDescription>
          </DialogHeader>
          <div className="py-4">
            <label className="text-sm font-medium mb-2 block">Проект</label>
            <Select
              value={targetProjectId || ""}
              onValueChange={(value) => setTargetProjectId(value)}
            >
              <SelectTrigger>
                <SelectValue placeholder="Выберите проект" />
              </SelectTrigger>
              <SelectContent>
                {projects.filter((p) => p.id !== projectId).map((p) => (
                  <SelectItem key={p.id} value={p.id}>
                    {p.title}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <div className="flex justify-end gap-2">
            <Button variant="outline" onClick={() => setShowCopyDialog(false)}>
              Отмена
            </Button>
            <Button onClick={handleCopyPlacements} disabled={!targetProjectId || copyingChannels}>
              {copyingChannels ? (
                <>
                  <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                  Копирование...
                </>
              ) : (
                "Добавить"
              )}
            </Button>
          </div>
        </DialogContent>
      </Dialog>

      <ColumnSelectorDialog
        open={showColumnsDialog}
        onOpenChange={setShowColumnsDialog}
        visibleColumns={visibleColumns}
        onVisibleColumnsChange={setVisibleColumns}
      />

      <FiltersModal
        open={filtersOpen}
        onOpenChange={setFiltersOpen}
        onApply={setFilters}
        existingPlacements={placements}
        initialFilters={filters}
      />

      <CreativeSelectDialog
        open={showCreativeDialog}
        onOpenChange={setShowCreativeDialog}
        creatives={creatives}
        onSelect={handleSelectCreative}
        onClear={selectedPlacementForCreative ? () => handleClearCreative(selectedPlacementForCreative.id) : null}
        currentCreativeId={selectedPlacementForCreative?.creative_id}
      />
    </>
  );
}
