// Copyright (c) 2025-2026, RTE (https://www.rte-france.com)
// This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
// If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-License-Identifier: MPL-2.0
// This file is part of Co-Study4Grid a Power Grid Study tool Assistant Interface to help solve contigencies for a grid state under study.

import React, { useState, useRef, useCallback, useLayoutEffect, type CSSProperties } from 'react';
import type { ActionOverviewFilters } from '../types';
import { colors, radius, space, text } from '../styles/tokens';
import ActionFilterRings from './ActionFilterRings';

interface SidebarSummaryProps {
  /** Currently APPLIED contingency — list of element IDs disconnected. */
  selectedContingency: string[];
  n1LinesOverloaded: string[] | undefined;
  n1LinesOverloadedRho: number[] | undefined;
  /** N-state pre-existing overloads. Surfaced via the info bubble next to
   *  the Overloads label, since they are no longer shown in a dedicated
   *  OverloadPanel card. */
  nLinesOverloaded?: string[];
  nLinesOverloadedRho?: number[];
  selectedOverloads: Set<string> | null | undefined;
  displayName: (id: string) => string;
  onContingencyZoom: (assetName: string) => void;
  onOverloadClick: (actionId: string, assetName: string, tab: 'n' | 'contingency') => void;
  /** Toggle an N-1 overload's inclusion in the analysis monitoring set. */
  onToggleOverload?: (overload: string) => void;
  /** Whether deselected overloads are still kept in the monitoring scope. */
  monitorDeselected?: boolean;
  onToggleMonitorDeselected?: () => void;
  /** Optional one-line monitoring coverage hint (replaces the legacy
   *  OverloadPanel hint when present). */
  monitoringHint?: string | null;
  /** Drop the current contingency and return to the contingency picker.
   *  Triggers the confirmation dialog at the call site. */
  onClearContingency?: () => void;
  /** Shared severity + action-type filters; forwarded to the inline
   *  ActionFilterRings row. */
  overviewFilters?: ActionOverviewFilters;
  onOverviewFiltersChange?: (next: ActionOverviewFilters) => void;
  /** Whether the action feed has any card to filter right now. */
  hasActions?: boolean;
}

/**
 * Compact sticky strip at the top of the sidebar that keeps the
 * clickable fields of interest visible while the rest of the sidebar
 * scrolls. Shows the selected contingency (with zoom-to shortcut)
 * and the contingency-state overloaded lines (with per-line
 * navigation + rho percentages). Rendered only when at least one of
 * those pieces of state is present.
 *
 * Hosts:
 * - a Clear button next to the Contingency label so the operator
 *   can drop the current contingency and go investigate another
 *   one (confirmation dialog handled by the parent);
 * - an info bubble next to the Overloads label that opens a small
 *   popover listing N-state pre-existing overloads, letting the
 *   operator deselect N-1 overloads (toggling their inclusion in
 *   the analysis monitoring set) and flip the "monitor deselected"
 *   switch — the former OverloadPanel functionality folded into
 *   the sticky banner.
 */
export default function SidebarSummary({
  selectedContingency,
  n1LinesOverloaded,
  n1LinesOverloadedRho,
  nLinesOverloaded,
  nLinesOverloadedRho,
  selectedOverloads,
  displayName,
  onContingencyZoom,
  onOverloadClick,
  onToggleOverload,
  monitorDeselected = false,
  onToggleMonitorDeselected,
  monitoringHint,
  onClearContingency,
  overviewFilters,
  onOverviewFiltersChange,
  hasActions,
}: SidebarSummaryProps) {
  const hasOverloads = (n1LinesOverloaded?.length ?? 0) > 0;
  const hasContingency = selectedContingency.length > 0;
  const hasFilters = !!hasActions && !!overviewFilters && !!onOverviewFiltersChange;
  const [popoverOpen, setPopoverOpen] = useState(false);
  const hideTimerRef = useRef<number | null>(null);
  const bubbleRef = useRef<HTMLButtonElement>(null);

  const cancelHide = useCallback(() => {
    if (hideTimerRef.current != null) {
      window.clearTimeout(hideTimerRef.current);
      hideTimerRef.current = null;
    }
  }, []);
  const scheduleHide = useCallback(() => {
    cancelHide();
    hideTimerRef.current = window.setTimeout(() => setPopoverOpen(false), 180);
  }, [cancelHide]);

  if (!hasContingency && !hasOverloads && !hasFilters) return null;

  const hasNOverloads = (nLinesOverloaded?.length ?? 0) > 0;
  const overloadsBubbleEnabled = hasOverloads || hasNOverloads || !!monitoringHint;

  return (
    <div
      data-testid="sticky-feed-summary"
      style={{
        flexShrink: 0,
        padding: `6px ${space[3]}`,
        background: colors.surfaceMuted,
        borderBottom: `1px solid ${colors.border}`,
        fontSize: text.xs,
        lineHeight: 1.5,
        boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'flex-start',
        gap: space.half,
      }}
    >
      {hasContingency && (
        <div
          data-testid="sidebar-summary-contingency"
          style={{ display: 'flex', alignItems: 'baseline', gap: space[1], flexWrap: 'wrap', width: '100%' }}
        >
          <span style={{ color: colors.textSecondary, fontWeight: 600, whiteSpace: 'nowrap' }}>
            ⚡ Contingency{selectedContingency.length > 1 ? ` (N-${selectedContingency.length})` : ''}:
          </span>
          {selectedContingency.map((id, i) => (
            <React.Fragment key={id}>
              {i > 0 && <span style={{ color: colors.textSecondary }}>, </span>}
              <button
                onClick={(e) => { e.stopPropagation(); onContingencyZoom(id); }}
                title={`Zoom to ${id} in the current diagram`}
                style={{
                  background: 'none',
                  border: 'none',
                  cursor: 'pointer',
                  padding: 0,
                  fontSize: text.xs,
                  color: colors.brand,
                  fontWeight: 600,
                  textDecoration: 'underline dotted',
                  wordBreak: 'break-word',
                  textAlign: 'left',
                }}
              >
                {displayName(id)}
              </button>
            </React.Fragment>
          ))}
          {onClearContingency && (
            <button
              type="button"
              data-testid="sidebar-summary-clear"
              onClick={(e) => { e.stopPropagation(); onClearContingency(); }}
              title="Clear the current contingency and pick a new one"
              style={{
                padding: '3px 10px',
                background: colors.danger,
                color: colors.textOnBrand,
                border: 'none',
                borderRadius: radius.sm,
                cursor: 'pointer',
                fontSize: '11px',
                fontWeight: 'bold',
                flexShrink: 0,
                marginLeft: space[1],
                whiteSpace: 'nowrap',
              }}
            >
              Clear
            </button>
          )}
        </div>
      )}
      {hasOverloads && (
        <div
          data-testid="sidebar-summary-overloads"
          style={{ display: 'flex', alignItems: 'baseline', gap: space[1], position: 'relative' }}
        >
          <span style={{ color: colors.dangerStrong, fontWeight: 600, whiteSpace: 'nowrap' }}>⚠️ Overloads:</span>
          <span style={{ wordBreak: 'break-word' }}>
            {n1LinesOverloaded!.map((name, i) => {
              const rho = n1LinesOverloadedRho?.[i];
              const rhoPct = rho != null && !Number.isNaN(rho) ? `${(rho * 100).toFixed(1)}%` : null;
              const isSelected = selectedOverloads?.has(name) ?? true;
              return (
                <React.Fragment key={name}>
                  {i > 0 && ', '}
                  <button
                    onClick={(e) => { e.stopPropagation(); onOverloadClick('', name, 'contingency'); }}
                    onDoubleClick={(e) => {
                      if (onToggleOverload) {
                        e.stopPropagation();
                        onToggleOverload(name);
                      }
                    }}
                    title={onToggleOverload
                      ? (isSelected
                        ? `Zoom to ${name} (Double-click to unselect)`
                        : `Zoom to ${name} (Double-click to select)`)
                      : `Open Contingency tab and zoom to ${name}`}
                    style={{
                      background: 'none',
                      border: 'none',
                      cursor: 'pointer',
                      padding: 0,
                      fontSize: text.xs,
                      color: isSelected ? colors.brand : colors.borderStrong,
                      fontWeight: isSelected ? 600 : 400,
                      textDecoration: isSelected ? 'underline dotted' : 'none',
                    }}
                  >
                    {displayName(name)}
                  </button>
                  {rhoPct && (
                    <span style={{ color: isSelected ? colors.textPrimary : colors.borderStrong, marginLeft: space.half }}>
                      ({rhoPct})
                    </span>
                  )}
                </React.Fragment>
              );
            })}
          </span>
          {overloadsBubbleEnabled && (
            <span
              data-testid="sidebar-summary-overloads-bubble-wrap"
              style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}
              onMouseEnter={() => { cancelHide(); setPopoverOpen(true); }}
              onMouseLeave={scheduleHide}
            >
              <button
                ref={bubbleRef}
                data-testid="sidebar-summary-overloads-bubble"
                onClick={(e) => { e.stopPropagation(); setPopoverOpen(o => !o); }}
                aria-label="Overload details"
                title="Show N-state overloads, toggle N-1 overload selection, and monitor-deselected"
                style={{
                  background: colors.chromeSoft,
                  color: colors.textOnBrand,
                  border: 'none',
                  borderRadius: '50%',
                  width: '14px',
                  height: '14px',
                  fontSize: '10px',
                  lineHeight: 1,
                  cursor: 'pointer',
                  display: 'inline-flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  padding: 0,
                  marginLeft: space[1],
                }}
              >
                ?
              </button>
              {popoverOpen && (
                <OverloadInfoPopover
                  nLinesOverloaded={nLinesOverloaded ?? []}
                  nLinesOverloadedRho={nLinesOverloadedRho}
                  n1LinesOverloaded={n1LinesOverloaded ?? []}
                  n1LinesOverloadedRho={n1LinesOverloadedRho}
                  selectedOverloads={selectedOverloads}
                  onToggleOverload={onToggleOverload}
                  monitorDeselected={monitorDeselected}
                  onToggleMonitorDeselected={onToggleMonitorDeselected}
                  monitoringHint={monitoringHint}
                  onAssetClick={onOverloadClick}
                  displayName={displayName}
                  anchorRef={bubbleRef}
                />
              )}
            </span>
          )}
        </div>
      )}
      {hasFilters && (
        <ActionFilterRings
          filters={overviewFilters!}
          onFiltersChange={onOverviewFiltersChange!}
        />
      )}
    </div>
  );
}

interface OverloadInfoPopoverProps {
  nLinesOverloaded: string[];
  nLinesOverloadedRho?: number[];
  n1LinesOverloaded: string[];
  n1LinesOverloadedRho?: number[];
  selectedOverloads: Set<string> | null | undefined;
  onToggleOverload?: (overload: string) => void;
  monitorDeselected: boolean;
  onToggleMonitorDeselected?: () => void;
  monitoringHint?: string | null;
  onAssetClick: (actionId: string, assetName: string, tab: 'n' | 'contingency') => void;
  displayName: (id: string) => string;
  /** The `?` bubble button the popover anchors to. The popover renders with
   *  `position: fixed` computed from this element's bounding rect so it
   *  escapes the sidebar's `overflow: hidden` clipping and always paints
   *  above the network visualization. */
  anchorRef: React.RefObject<HTMLButtonElement | null>;
}

function OverloadInfoPopover({
  nLinesOverloaded,
  nLinesOverloadedRho,
  n1LinesOverloaded,
  n1LinesOverloadedRho,
  selectedOverloads,
  onToggleOverload,
  monitorDeselected,
  onToggleMonitorDeselected,
  monitoringHint,
  onAssetClick,
  displayName,
  anchorRef,
}: OverloadInfoPopoverProps) {
  const formatRho = (v: number | undefined) =>
    v == null || Number.isNaN(v) ? null : `${(v * 100).toFixed(1)}%`;
  const hasDeselected = n1LinesOverloaded.some(name => !(selectedOverloads?.has(name) ?? true));

  // The sidebar that hosts the `?` bubble sets `overflow: hidden`, so an
  // absolutely-positioned popover gets clipped (and visually masked by the
  // network visualization) the moment it spills past the sidebar edge.
  // Anchor with `position: fixed` against the button's viewport rect instead
  // so the popover escapes the clip and always paints on top.
  const POPOVER_MAX_WIDTH = 320;
  const MARGIN = 8;
  const [fixedPos, setFixedPos] = useState<CSSProperties>({
    position: 'fixed',
    visibility: 'hidden',
    top: 0,
    left: 0,
  });

  useLayoutEffect(() => {
    const anchor = anchorRef.current;
    if (!anchor) return;
    const rect = anchor.getBoundingClientRect();
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    // Align the popover's left edge to the bubble, clamped to the viewport.
    const left = Math.max(
      MARGIN,
      Math.min(rect.left, vw - POPOVER_MAX_WIDTH - MARGIN),
    );
    setFixedPos({
      position: 'fixed',
      left,
      top: Math.min(rect.bottom + 4, vh - MARGIN),
      maxHeight: vh - rect.bottom - 2 * MARGIN,
      overflowY: 'auto',
    });
  }, [anchorRef]);

  return (
    <div
      data-testid="overload-info-popover"
      role="dialog"
      style={{
        ...fixedPos,
        zIndex: 300,
        background: colors.surface,
        border: `1px solid ${colors.border}`,
        borderRadius: radius.md,
        boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
        padding: `${space[2]} ${space[3]}`,
        minWidth: '240px',
        maxWidth: '320px',
        fontSize: text.xs,
        color: colors.textPrimary,
        lineHeight: 1.5,
      }}
      onClick={(e) => e.stopPropagation()}
    >
      {monitoringHint && (
        <div
          data-testid="overload-info-monitoring-hint"
          style={{
            marginBottom: space[1],
            fontSize: '11px',
            color: colors.textTertiary,
            fontStyle: 'italic',
          }}
        >
          {monitoringHint}
        </div>
      )}
      <div style={{ marginBottom: space[1] }}>
        <strong style={{ color: colors.textSecondary }}>N Overloads:</strong>{' '}
        {nLinesOverloaded.length > 0 ? (
          <span style={{ wordBreak: 'break-word' }}>
            {nLinesOverloaded.map((name, i) => {
              const rhoPct = formatRho(nLinesOverloadedRho?.[i]);
              return (
                <React.Fragment key={name}>
                  {i > 0 && ', '}
                  <button
                    onClick={(e) => { e.stopPropagation(); onAssetClick('', name, 'n'); }}
                    title={`Open N tab and zoom to ${name}`}
                    style={{
                      background: 'none',
                      border: 'none',
                      cursor: 'pointer',
                      padding: 0,
                      fontSize: 'inherit',
                      color: colors.brand,
                      fontWeight: 600,
                      textDecoration: 'underline dotted',
                    }}
                  >
                    {displayName(name)}
                  </button>
                  {rhoPct && <span style={{ marginLeft: space.half }}>({rhoPct})</span>}
                </React.Fragment>
              );
            })}
          </span>
        ) : (
          <span style={{ color: colors.textTertiary, fontStyle: 'italic' }}>None</span>
        )}
      </div>
      <div>
        <strong style={{ color: colors.textSecondary }}>N-1 Overloads:</strong>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '2px', marginTop: space.half }}>
          {n1LinesOverloaded.map((name, i) => {
            const rhoPct = formatRho(n1LinesOverloadedRho?.[i]);
            const isSelected = selectedOverloads?.has(name) ?? true;
            return (
              <label
                key={name}
                data-testid={`overload-info-toggle-${name}`}
                title={isSelected
                  ? `Uncheck to exclude ${displayName(name)} from monitoring`
                  : `Check to include ${displayName(name)} in monitoring`}
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  gap: space[1],
                  cursor: onToggleOverload ? 'pointer' : 'default',
                  color: isSelected ? colors.textPrimary : colors.borderStrong,
                }}
              >
                <input
                  type="checkbox"
                  checked={isSelected}
                  disabled={!onToggleOverload}
                  onChange={() => onToggleOverload?.(name)}
                  onClick={(e) => e.stopPropagation()}
                  style={{ margin: 0, cursor: onToggleOverload ? 'pointer' : 'default' }}
                />
                <button
                  onClick={(e) => { e.stopPropagation(); onAssetClick('', name, 'contingency'); }}
                  title={`Open Contingency tab and zoom to ${name}`}
                  style={{
                    background: 'none',
                    border: 'none',
                    cursor: 'pointer',
                    padding: 0,
                    fontSize: 'inherit',
                    color: isSelected ? colors.brand : colors.borderStrong,
                    fontWeight: isSelected ? 600 : 400,
                    textDecoration: isSelected ? 'underline dotted' : 'none',
                  }}
                >
                  {displayName(name)}
                </button>
                {rhoPct && (
                  <span style={{ color: isSelected ? colors.textPrimary : colors.borderStrong }}>
                    ({rhoPct})
                  </span>
                )}
              </label>
            );
          })}
        </div>
        {hasDeselected && onToggleMonitorDeselected && (
          <label
            data-testid="overload-info-monitor-deselected"
            style={{
              display: 'inline-flex',
              alignItems: 'center',
              gap: '4px',
              cursor: 'pointer',
              fontSize: '11px',
              color: monitorDeselected ? colors.brandStrong : colors.textSecondary,
              fontWeight: monitorDeselected ? 600 : 400,
              marginTop: space[1],
            }}
            title="When checked, deselected overloads are still included in the analysis monitoring scope"
          >
            <input
              type="checkbox"
              checked={monitorDeselected}
              onChange={onToggleMonitorDeselected}
              onClick={(e) => e.stopPropagation()}
              style={{ margin: 0, cursor: 'pointer' }}
            />
            monitor deselected
          </label>
        )}
      </div>
    </div>
  );
}
