/**
 * Dashboard widget registry — the single source of truth for which
 * widgets exist, what they're called, where they live, and what they
 * default to. The DashboardShell loops over this; the settings UI also
 * loops over this. Adding a new widget = adding one entry here.
 *
 * Widget visibility is persisted per-user in
 * `user_profiles.preferences.dashboardWidgets` as `{ [id]: boolean }`.
 * Missing entries default to `defaultVisible`, so a brand-new widget
 * surfaces for existing users automatically.
 */

import type { ReactNode } from 'react';
import dynamic from 'next/dynamic';

import { ActiveDealsTile } from './active-deals-tile';
import { ActivityFeed } from './activity-feed';
import { BerthHeatWidget } from './berth-heat-widget';
import { ClientsByCountryWidget } from './clients-by-country-widget';
import { HotDealsCard } from './hot-deals-card';
import { PipelineValueTile } from './pipeline-value-tile';
import { WebsiteGlanceTile } from './website-glance-tile';
import { MyRemindersRail } from './my-reminders-rail';
import { AlertRail } from '@/components/alerts/alert-rail';
import type { DateRange } from '@/lib/analytics/range';

// Recharts-backed widgets are dynamic-imported so the recharts bundle
// (~80-150KB) doesn't ship on every dashboard load when the rep has
// disabled charts. perf-test-auditor HIGH H3 caught the static import.
// Each one gets a placeholder loading state matching its grid slot.
const ChartFallback = () => (
  <div className="rounded-lg border bg-muted/30 p-8 text-center text-sm text-muted-foreground">
    Loading chart…
  </div>
);
const BerthStatusChart = dynamic(
  () => import('./berth-status-chart').then((m) => ({ default: m.BerthStatusChart })),
  { loading: ChartFallback, ssr: false },
);
const LeadSourceChart = dynamic(
  () => import('./lead-source-chart').then((m) => ({ default: m.LeadSourceChart })),
  { loading: ChartFallback, ssr: false },
);
const OccupancyTimelineChart = dynamic(
  () => import('./occupancy-timeline-chart').then((m) => ({ default: m.OccupancyTimelineChart })),
  { loading: ChartFallback, ssr: false },
);
const PipelineFunnelChart = dynamic(
  () => import('./pipeline-funnel-chart').then((m) => ({ default: m.PipelineFunnelChart })),
  { loading: ChartFallback, ssr: false },
);
const SourceConversionChart = dynamic(
  () => import('./source-conversion-chart').then((m) => ({ default: m.SourceConversionChart })),
  { loading: ChartFallback, ssr: false },
);

/**
 * Where a widget lives on the dashboard. The shell renders three
 * separate auto-fit regions so charts and rails don't compete for the
 * same horizontal slots (preserves the visual hierarchy the team has
 * gotten used to).
 *
 *   - 'chart'      → main analytics region (wider min-col)
 *   - 'rail'       → side-rail region (narrower min-col)
 *   - 'feed'       → full-width row underneath everything else
 */
export type WidgetGroup = 'chart' | 'rail' | 'feed';

/**
 * External integrations a widget can depend on. When the corresponding
 * integration isn't connected for the active port, the widget is hidden
 * from the picker AND from the rendered dashboard so reps can't toggle
 * something that would render nothing. Wire new integrations through
 * `useDashboardIntegrations()`.
 */
export type WidgetIntegration = 'umami' | 'documenso';

export interface DashboardWidget {
  /** Stable persistence key. Don't rename — old preferences would break. */
  id: string;
  label: string;
  description: string;
  /**
   * Renders the widget. Receives the active date-range so chart widgets
   * can react; non-chart widgets simply ignore it. Keeping this a
   * function instead of a `ComponentType` lets each widget pick its own
   * prop shape without leaking the union into the registry type.
   */
  render: (range: DateRange) => ReactNode;
  group: WidgetGroup;
  defaultVisible: boolean;
  /**
   * Some widgets self-gate (e.g. WebsiteGlanceTile renders null when
   * Umami isn't configured). When `true`, the settings UI still shows
   * the toggle so admins can enable it once the integration is wired —
   * but the widget itself decides whether to render content.
   */
  selfGates?: boolean;
  /**
   * Names the external integration this widget depends on. When the
   * integration isn't connected for the active port, the widget is
   * filtered out of both the picker and the rendered dashboard.
   */
  requires?: WidgetIntegration;
}

export const DASHBOARD_WIDGETS: readonly DashboardWidget[] = [
  // ── KPI tiles (rail) ────────────────────────────────────────────────
  // Off by default — keep the existing dashboard layout unchanged for
  // users on first paint after the upgrade; reps can flip them on from
  // the Customize menu.
  {
    id: 'kpi_active_deals',
    label: 'Active Deals',
    description: 'Compact tile: count of in-flight interests.',
    render: () => <ActiveDealsTile />,
    group: 'rail',
    defaultVisible: false,
  },
  {
    id: 'kpi_pipeline_value',
    label: 'Pipeline Value',
    description:
      'Gross + weighted forecast, broken down by pipeline stage so leadership can see what is near-close vs speculative.',
    render: (range) => <PipelineValueTile range={range} />,
    // Lives in the chart grid (not the narrow rail) so the per-stage
    // breakdown rows have room to breathe alongside the headline numbers,
    // and the rail stays reserved for reminders / alerts / glance tiles.
    group: 'chart',
    defaultVisible: true,
  },

  // ── Charts (main area) ──────────────────────────────────────────────
  {
    id: 'pipeline_funnel',
    label: 'Pipeline Funnel',
    description: 'Interests by stage with conversion-rate vs open.',
    render: (range) => <PipelineFunnelChart range={range} />,
    group: 'chart',
    defaultVisible: true,
  },
  {
    id: 'occupancy_timeline',
    label: 'Occupancy Timeline',
    description: 'Daily berth occupancy across the range.',
    render: (range) => <OccupancyTimelineChart range={range} />,
    group: 'chart',
    defaultVisible: true,
  },
  {
    id: 'lead_source',
    label: 'Lead Source Attribution',
    description: 'Where new interests came from.',
    render: (range) => <LeadSourceChart range={range} />,
    group: 'chart',
    defaultVisible: true,
  },
  {
    id: 'berth_status',
    label: 'Berth Status',
    description: 'Donut: available / under offer / sold split.',
    render: () => <BerthStatusChart />,
    group: 'chart',
    defaultVisible: false,
  },
  {
    id: 'source_conversion',
    label: 'Source Conversion',
    description: 'Win rate per lead source — which channels deliver buyers, not just leads.',
    render: () => <SourceConversionChart />,
    group: 'chart',
    // Flipped on 2026-05-14 — investor-facing conversion-funnel-by-source
    // surface (PRE-DEPLOY-PLAN § 1.6.23). Reads inquiry → client linkage
    // (clients.source_inquiry_id) added in migration 0065.
    defaultVisible: true,
  },
  {
    id: 'berth_heat',
    label: 'Berth Demand',
    description:
      'Ranks berths by active interest. Surfaces the leading mooring with its runners-up.',
    render: () => <BerthHeatWidget />,
    group: 'chart',
    defaultVisible: true,
  },
  {
    id: 'clients_by_country',
    label: 'Clients by country',
    description:
      'Per-country distribution of the active client book. Click a row to filter the clients list by country.',
    render: () => <ClientsByCountryWidget />,
    // Same rail-tile idiom as BerthHeatWidget + HotDealsCard — compact
    // ranked list with mini-bars. Variant (a) per the master-doc design;
    // the world-map variant lands alongside the recharts→ECharts pass.
    group: 'rail',
    defaultVisible: true,
  },
  {
    id: 'website_analytics',
    label: 'Website Analytics',
    description: 'Quick glance at marketing site traffic. Requires Umami.',
    render: (range) => <WebsiteGlanceTile range={range} />,
    group: 'rail',
    defaultVisible: true,
    selfGates: true,
    requires: 'umami',
  },
  {
    id: 'my_reminders',
    label: 'My Reminders',
    description: 'Your upcoming and overdue reminders.',
    render: () => <MyRemindersRail />,
    group: 'rail',
    defaultVisible: true,
  },
  {
    id: 'alerts',
    label: 'Alerts',
    description: 'System-flagged action items.',
    render: () => <AlertRail />,
    group: 'rail',
    defaultVisible: true,
  },
  {
    id: 'hot_deals',
    label: 'Hot Deals',
    description: 'Top 5 active interests closest to closing.',
    render: () => <HotDealsCard />,
    group: 'rail',
    defaultVisible: false,
  },
  {
    id: 'activity_feed',
    label: 'Recent Activity',
    description: 'Audit log of changes across the port.',
    render: () => <ActivityFeed />,
    group: 'feed',
    defaultVisible: true,
  },
];

/** Lookup helper so consumers don't have to scan the array. */
export const WIDGETS_BY_ID: Record<string, DashboardWidget> = Object.fromEntries(
  DASHBOARD_WIDGETS.map((w) => [w.id, w]),
);
