import { Button, Input, Select, Slider, Spin } from "antd";
import {
  Bell,
  ChevronLeft,
  ChevronRight,
  ExternalLink,
  RefreshCw,
  ScanSearch,
  Trash2,
} from "lucide-react";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import type {
  CameraMarker,
  LatestCameraEvent,
} from "~/pages/desktop/floor_plan.types";
import type { FlatDeviceChannelOption } from "~/service/api/device/device";
import type { FloorPlanInteractionMode } from "~/pages/desktop/floor_plan.storage";
import { formatEventTimeAbsolute } from "~/pages/desktop/floor_plan.relative-time";

/**
 * 为什么要用“设备名称”而不是 device_id 做分组主键：
 * 现场绑点时，用户识别的是“卖场1、卖场2”这类业务名称，而不是长串国标 ID。
 * 把分组口径改成设备名称，能让多设备场景下的查找路径和用户心智一致，减少误绑和反复展开分组的成本。
 */
function buildGroupedChannelOptions(channelOptions: FlatDeviceChannelOption[]) {
  const grouped = new Map<
    string,
    { label: string; options: Array<{ value: string; label: string }> }
  >();

  for (const item of channelOptions) {
    const groupKey = item.deviceName;
    const group = grouped.get(groupKey) ?? {
      label: item.deviceName,
      options: [],
    };

    group.options.push({
      value: item.value,
      label: `${item.deviceName} / ${item.channelName}`,
    });

    grouped.set(groupKey, group);
  }

  return Array.from(grouped.values())
    .map((group) => ({
      ...group,
      options: group.options.sort((left, right) =>
        left.label.localeCompare(right.label, "zh-CN"),
      ),
    }))
    .sort((left, right) => left.label.localeCompare(right.label, "zh-CN"));
}

/**
 * 为什么摄像头绑定面板要直接暴露加载状态和异常状态：
 * 绑定失败往往不是交互问题，而是后端分页、网络或数据规模导致的数据不完整。
 * 把这些状态留在面板里直接提示，能让现场排查更快定位到“是没加载全”，而不是误判成摄像头或布局有问题。
 */
export function CameraBindingPanel({
  camera,
  channelOptions,
  channelsLoading,
  channelsError,
  onBindChannel,
  onAngleChange,
  onFovChange,
  onRangeChange,
  onDelete,
  interactionMode = "edit",
  channelFilter = "",
  onChannelFilterChange,
  selectedLatestEvent = null,
  selectedEventLoading = false,
  channelOnline = null,
  playbackTo = null,
  alertsTo = null,
  eventOccurredAgo = "",
  dataFetchedAgo = "",
  onRefreshEvent,
  filterMatchCount = 0,
  filterMatchActiveIndex = 0,
  onFilterPrev,
  onFilterNext,
  onFilterFrameAll,
}: {
  camera: CameraMarker | null;
  channelOptions: FlatDeviceChannelOption[];
  channelsLoading: boolean;
  channelsError: string | null;
  onBindChannel: (value: string | null) => void;
  onAngleChange: (value: number) => void;
  onFovChange: (value: number) => void;
  onRangeChange: (value: number) => void;
  onDelete: () => void;
  interactionMode?: FloorPlanInteractionMode;
  channelFilter?: string;
  onChannelFilterChange?: (value: string) => void;
  selectedLatestEvent?: LatestCameraEvent | null;
  selectedEventLoading?: boolean;
  channelOnline?: boolean | null;
  playbackTo?: { pathname: string; search: string } | null;
  alertsTo?: { pathname: string; search: string } | null;
  eventOccurredAgo?: string;
  dataFetchedAgo?: string;
  onRefreshEvent?: () => void;
  filterMatchCount?: number;
  filterMatchActiveIndex?: number;
  onFilterPrev?: () => void;
  onFilterNext?: () => void;
  onFilterFrameAll?: () => void;
}) {
  const { t } = useTranslation("desktop");

  const groupedChannelOptions = useMemo(
    () => buildGroupedChannelOptions(channelOptions),
    [channelOptions],
  );

  const editLocked = interactionMode === "browse";

  const filterTrimmed = channelFilter.trim();
  const showFilterNav =
    Boolean(onChannelFilterChange) &&
    Boolean(filterTrimmed) &&
    Boolean(onFilterPrev && onFilterNext && onFilterFrameAll);

  return (
    <div className="space-y-4">
      {onChannelFilterChange ? (
        <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
          <div className="mb-2 text-sm font-medium text-gray-900">
            {t("filter_cameras")}
          </div>
          <Input
            allowClear
            value={channelFilter}
            placeholder={t("filter_cameras_placeholder")}
            onChange={(e) => onChannelFilterChange(e.target.value)}
          />
          <div className="mt-2 text-xs text-gray-500">
            {t("filter_cameras_hint")}
          </div>
          {showFilterNav ? (
            <div className="mt-3 flex flex-wrap items-center gap-2 border-t border-gray-100 pt-3">
              {filterMatchCount > 0 ? (
                <>
                  <span className="text-xs text-gray-600">
                    {t("filter_match_position", {
                      current: filterMatchActiveIndex + 1,
                      total: filterMatchCount,
                    })}
                  </span>
                  <Button
                    type="default"
                    size="small"
                    icon={<ChevronLeft className="h-3.5 w-3.5" />}
                    onClick={onFilterPrev}
                    title={t("filter_prev_match")}
                  />
                  <Button
                    type="default"
                    size="small"
                    icon={<ChevronRight className="h-3.5 w-3.5" />}
                    onClick={onFilterNext}
                    title={t("filter_next_match")}
                  />
                  <Button
                    type="default"
                    size="small"
                    icon={<ScanSearch className="h-3.5 w-3.5" />}
                    onClick={onFilterFrameAll}
                  >
                    {t("filter_frame_all")}
                  </Button>
                  <span className="w-full text-[11px] text-gray-400">
                    {t("filter_keyboard_hint")}
                  </span>
                </>
              ) : (
                <span className="text-xs text-amber-700">
                  {t("filter_no_matches")}
                </span>
              )}
            </div>
          ) : null}
        </div>
      ) : null}

      {camera ? (
        <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
          <div className="mb-2 flex flex-wrap items-center justify-between gap-2">
            <div className="text-sm font-semibold text-gray-900">
              {t("panel_latest_ai_event")}
            </div>
            <div className="flex flex-wrap items-center justify-end gap-1">
              {camera.channelId && onRefreshEvent ? (
                <Button
                  type="text"
                  size="small"
                  icon={<RefreshCw className="h-3.5 w-3.5" />}
                  loading={selectedEventLoading}
                  onClick={onRefreshEvent}
                  title={t("refresh_event_tooltip")}
                />
              ) : null}
              {camera.channelId && alertsTo ? (
                <Link
                  to={alertsTo}
                  className="inline-flex items-center gap-1 rounded-lg bg-amber-600 px-2 py-1 text-xs font-medium text-white hover:bg-amber-700"
                >
                  <Bell className="h-3.5 w-3.5" />
                  {t("open_alerts")}
                </Link>
              ) : null}
              {camera.channelId && playbackTo ? (
                <Link
                  to={playbackTo}
                  className="inline-flex items-center gap-1 rounded-lg bg-gray-900 px-2 py-1 text-xs font-medium text-white hover:bg-gray-800"
                >
                  <ExternalLink className="h-3.5 w-3.5" />
                  {t("open_playback")}
                </Link>
              ) : null}
            </div>
          </div>
          {selectedEventLoading ? (
            <div className="flex min-h-20 items-center justify-center">
              <Spin size="small" />
            </div>
          ) : selectedLatestEvent ? (
            <div className="space-y-2 text-xs text-gray-600">
              {selectedLatestEvent.imageSrc ? (
                <img
                  src={selectedLatestEvent.imageSrc}
                  alt={selectedLatestEvent.label}
                  className="h-32 w-full rounded-lg border border-gray-200 object-cover"
                  onError={() => {
                    console.warn("[floor-plan] panel event image failed", {
                      channelId: selectedLatestEvent.channelId,
                    });
                  }}
                />
              ) : null}
              <div>
                <span className="font-medium text-gray-900">
                  {t("latest_ai_event")}:{" "}
                </span>
                {selectedLatestEvent.label}
              </div>
              <div>
                <span className="font-medium text-gray-900">
                  {t("event_time")}:{" "}
                </span>
                {formatEventTimeAbsolute(selectedLatestEvent.startedAt)}
              </div>
              <div>
                <span className="font-medium text-gray-900">
                  {t("score")}:{" "}
                </span>
                {(selectedLatestEvent.score * 100).toFixed(1)}%
              </div>
              {eventOccurredAgo ? (
                <div className="text-[11px] text-gray-500">
                  {t("event_occurred_ago", { ago: eventOccurredAgo })}
                </div>
              ) : null}
              {dataFetchedAgo ? (
                <div className="text-[11px] text-gray-400">
                  {t("data_fetched_ago", { ago: dataFetchedAgo })}
                </div>
              ) : null}
            </div>
          ) : (
            <div className="space-y-2">
              <div className="rounded-lg bg-gray-50 px-3 py-3 text-center text-sm text-gray-500">
                {camera.channelId ? t("no_ai_event") : t("camera_unbound")}
              </div>
              {camera.channelId && dataFetchedAgo ? (
                <div className="text-center text-[11px] text-gray-400">
                  {t("data_fetched_ago", { ago: dataFetchedAgo })}
                </div>
              ) : null}
            </div>
          )}
        </div>
      ) : null}

      {!camera ? (
        <div className="rounded-xl border border-dashed border-gray-300 bg-gray-50 p-4 text-sm text-gray-500">
          {t("no_camera_selected")}
        </div>
      ) : (
        <>
          <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
            <div className="mb-1 text-sm font-semibold text-gray-900">
              {t("camera_settings")}
            </div>
            <div className="text-xs text-gray-500">
              {t("position")}: {Math.round(camera.x)}, {Math.round(camera.y)}
            </div>
            {camera.channelId != null ? (
              <div className="mt-1 text-xs text-gray-600">
                <span className="font-medium text-gray-900">
                  {t("channel_status")}:{" "}
                </span>
                {channelOnline === null
                  ? t("channel_online_unknown")
                  : channelOnline
                    ? t("channel_online")
                    : t("channel_offline")}
              </div>
            ) : null}
            {editLocked ? (
              <div className="mt-2 text-xs leading-5 text-gray-500">
                {t("browse_camera_panel_edit_hidden_hint")}
              </div>
            ) : null}
          </div>

          {editLocked ? null : (
            <>
              <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
                <div className="mb-2 text-sm font-medium text-gray-900">
                  {t("bind_channel")}
                </div>
                {channelsLoading ? (
                  <div className="flex min-h-14 items-center justify-center">
                    <Spin size="small" />
                  </div>
                ) : (
                  <>
                    <Select
                      className="w-full"
                      showSearch
                      allowClear
                      placeholder={t("bind_channel_placeholder")}
                      optionFilterProp="label"
                      value={camera.channelId ?? undefined}
                      options={groupedChannelOptions}
                      onChange={(value) => onBindChannel(value ?? null)}
                    />
                    <div className="mt-2 text-xs text-gray-500">
                      {t("channel_count_loaded", {
                        count: channelOptions.length,
                      })}
                    </div>
                    {channelsError ? (
                      <div className="mt-1 text-xs text-amber-600">
                        {t("channel_load_warning")}
                      </div>
                    ) : null}
                  </>
                )}
                <div className="mt-2 text-xs text-gray-500">
                  {camera.channelName || t("camera_unbound")}
                </div>
              </div>

              <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
                <div className="mb-3 text-sm font-medium text-gray-900">
                  {t("direction")}
                </div>
                <Slider
                  min={0}
                  max={359}
                  value={camera.angle}
                  onChange={(value) => onAngleChange(Number(value))}
                />

                <div className="mb-3 mt-4 text-sm font-medium text-gray-900">
                  {t("fov")}
                </div>
                <Slider
                  min={20}
                  max={160}
                  value={camera.fov}
                  onChange={(value) => onFovChange(Number(value))}
                />

                <div className="mb-3 mt-4 text-sm font-medium text-gray-900">
                  {t("range")}
                </div>
                <Slider
                  min={80}
                  max={800}
                  step={10}
                  value={camera.range}
                  onChange={(value) => onRangeChange(Number(value))}
                />
              </div>

              <button
                type="button"
                onClick={onDelete}
                className="inline-flex w-full items-center justify-center gap-2 rounded-lg bg-red-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600"
              >
                <Trash2 className="h-4 w-4" />
                {t("delete_camera")}
              </button>
            </>
          )}
        </>
      )}
    </div>
  );
}
