/**
 * SyncStateToast — polled sync-state feedback toast (Plan 03-06).
 *
 * Shows after every write (create, edit, delete) to surface the outbox status:
 *   pending  → "Syncing…" spinner (role="status", refetchInterval 3000ms)
 *   done     → "Saved" Check icon, auto-dismiss after 2s, invalidates ['events']
 *   failed   → "Didn't save. Try again." or conflict copy, persists until dismissed
 *   dead     → "Not saved. Check your connection.", persists until dismissed
 *
 * No SSE — polling only (D-09).
 *
 * Position: bottom of screen (bottom-center). Above FAB on phone.
 *
 * Accessibility:
 *   - role="status" for pending/done (polite live region)
 *   - role="alert" for failed/dead (assertive live region)
 *   - dismiss button: aria-label="Dismiss sync notification", 44px touch target
 *
 * Security: T-03-19 — fetchSyncStatus is member-scoped server-side.
 * T-03-18 — failed/dead toast persists; no silent loss.
 */

import { useEffect, useRef } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Loader2, Check, AlertCircle, X } from 'lucide-react';
import { useCalendarStore } from '../store/calendarStore.js';
import { fetchSyncStatus, type SyncStatus } from '../api/client.js';

// ── Component ──────────────────────────────────────────────────────────────────

export function SyncStateToast() {
  const lastSyncedUid = useCalendarStore((s) => s.lastSyncedUid);
  const setLastSyncedUid = useCalendarStore((s) => s.setLastSyncedUid);
  const queryClient = useQueryClient();

  // Track whether we've already invalidated for this uid to avoid duplicate calls
  const invalidatedRef = useRef<string | null>(null);

  const { data, isLoading } = useQuery<SyncStatus>({
    queryKey: ['syncStatus', lastSyncedUid],
    queryFn: () => fetchSyncStatus(lastSyncedUid!),
    enabled: lastSyncedUid !== null,
    // refetchInterval: active (3000ms) only while pending; disabled once terminal
    refetchInterval: (query) => {
      const status = query.state.data?.status;
      return status === 'pending' || status === undefined ? 3000 : false;
    },
    staleTime: 0,
    gcTime: 0,
  });

  const status = data?.status;
  // WR-06: an edit-as-move whose create hits 412 dead-ends with no retry path (the
  // original event is preserved, but the new uid's only outbox row is failed). The
  // worker tags that case with a 'move-failed:' lastError so we can show distinct copy
  // guiding the user to re-open and re-save, rather than the etag-conflict copy.
  const isMoveFailed = status === 'failed' && !!data?.error?.startsWith('move-failed');
  const isConflict = status === 'failed' && !isMoveFailed && data?.error?.includes('412');
  const isPersistent = status === 'failed' || status === 'dead';

  // Invalidate events on done OR on conflict (D-06/D-08)
  useEffect(() => {
    if (!lastSyncedUid) return;
    if (invalidatedRef.current === lastSyncedUid) return;

    if (status === 'done' || isConflict) {
      invalidatedRef.current = lastSyncedUid;
      void queryClient.invalidateQueries({ queryKey: ['events'] });
    }
  }, [status, isConflict, lastSyncedUid, queryClient]);

  // Auto-dismiss after 2s on done
  useEffect(() => {
    if (status !== 'done') return;
    const timer = setTimeout(() => {
      setLastSyncedUid(null);
    }, 2000);
    return () => clearTimeout(timer);
  }, [status, setLastSyncedUid]);

  // Reset invalidation guard when uid changes (new write)
  useEffect(() => {
    if (lastSyncedUid === null) {
      invalidatedRef.current = null;
    }
  }, [lastSyncedUid]);

  // Nothing to show
  if (!lastSyncedUid || (!isLoading && !data)) return null;

  // ── State-specific content ─────────────────────────────────────────────────

  const dismiss = () => setLastSyncedUid(null);

  // Determine ARIA role: status (polite) for pending/done; alert (assertive) for failed/dead
  const ariaRole: 'status' | 'alert' =
    status === 'failed' || status === 'dead' ? 'alert' : 'status';

  // Toast copy (exact strings from UI-SPEC §Copywriting)
  let copy: string;
  let icon: React.ReactNode;

  if (status === 'done') {
    copy = 'Saved';
    icon = <Check size={14} style={{ color: '#50C878', flexShrink: 0 }} aria-hidden="true" />;
  } else if (isConflict) {
    copy = 'This event changed elsewhere — review the latest version';
    icon = (
      <AlertCircle
        size={14}
        style={{ color: 'var(--color-destructive)', flexShrink: 0 }}
        aria-hidden="true"
      />
    );
  } else if (isMoveFailed) {
    // WR-06: the move could not be applied; the original event is unchanged.
    copy = "Couldn't move the event. Open it and save again.";
    icon = (
      <AlertCircle
        size={14}
        style={{ color: 'var(--color-destructive)', flexShrink: 0 }}
        aria-hidden="true"
      />
    );
  } else if (status === 'failed') {
    copy = "Didn't save. Try again.";
    icon = (
      <AlertCircle
        size={14}
        style={{ color: 'var(--color-destructive)', flexShrink: 0 }}
        aria-hidden="true"
      />
    );
  } else if (status === 'dead') {
    copy = 'Not saved. Check your connection.';
    icon = (
      <AlertCircle
        size={14}
        style={{ color: 'var(--color-destructive)', flexShrink: 0 }}
        aria-hidden="true"
      />
    );
  } else {
    // pending (or loading)
    copy = 'Syncing…';
    icon = (
      <Loader2
        size={14}
        style={{
          color: 'var(--color-text-secondary)',
          flexShrink: 0,
          animation: 'spin 1s linear infinite',
        }}
        aria-hidden="true"
      />
    );
  }

  return (
    <div
      role={ariaRole}
      aria-live={ariaRole === 'alert' ? 'assertive' : 'polite'}
      style={{
        position: 'fixed',
        bottom: 'var(--space-12)',
        left: '50%',
        transform: 'translateX(-50%)',
        zIndex: 300,
        background: 'var(--color-surface-raised)',
        border: '1px solid var(--color-border)',
        borderRadius: 'var(--space-1)',
        boxShadow: '0 2px 8px rgba(0,0,0,0.12)',
        padding: 'var(--space-2) var(--space-3)',
        display: 'flex',
        alignItems: 'center',
        gap: 'var(--space-2)',
        fontSize: 'var(--text-label-size)',
        fontWeight: 'var(--text-label-weight)',
        lineHeight: 'var(--text-label-line-height)',
        fontFamily: 'var(--font-family-base)',
        color:
          status === 'done'
            ? '#50C878'
            : status === 'failed' || status === 'dead'
              ? 'var(--color-destructive)'
              : 'var(--color-text-secondary)',
        whiteSpace: 'nowrap',
        maxWidth: '90vw',
      }}
    >
      {icon}
      {/* Plain text — XSS guard */}
      <span>{copy}</span>
      {/* Dismiss button — only for persistent states (failed/dead) */}
      {isPersistent && (
        <button
          aria-label="Dismiss sync notification"
          onClick={dismiss}
          style={{
            background: 'none',
            border: 'none',
            cursor: 'pointer',
            minWidth: '44px',
            minHeight: '44px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            color: 'var(--color-text-secondary)',
            borderRadius: 'var(--space-1)',
            padding: 0,
            marginLeft: 'var(--space-1)',
          }}
        >
          <X size={16} aria-hidden="true" />
        </button>
      )}
    </div>
  );
}
