// Opt the whole module out of the React Compiler: the widget body (and its inner
// helpers) is serialised to source and run in a separate WidgetKit runtime that
// has no `react-compiler-runtime`, so a compiler-injected `_c` cache would throw
// "Can't find variable: _c". The `'widget'` directive only opts out the outer
// function; this covers the inner helper components too.
'use no memo';

import { Circle, HStack, Image, Link, RoundedRectangle, Spacer, Text, VStack, ZStack } from '@expo/ui/swift-ui';
import {
  containerBackground,
  fixedSize,
  font,
  foregroundStyle,
  frame,
  lineLimit,
  minimumScaleFactor,
  multilineTextAlignment,
  opacity,
  padding,
  shadow,
  widgetURL,
} from '@expo/ui/swift-ui/modifiers';
import { createWidget, type WidgetEnvironment } from 'expo-widgets';
import { type ReactNode } from 'react';
import { type SFSymbol } from 'sf-symbols-typescript';

import { type GlideWidgetPayload } from './widget-payload';

/**
 * The Glide home-screen widget. One definition renders all three families. The
 * shown task is implicitly the current one (no "NOW" badge), and the app's
 * signature element — a thick-bordered, hard-shadowed card — is the hero at
 * every size:
 *   • systemSmall  — the current task in a hero card.
 *   • systemMedium — a header (wordmark + ready count) over a hero card.
 *   • systemLarge  — header, hero card, a NEXT UP list of bordered rows, the
 *                    remaining count, and Plan / New shortcuts.
 *
 * IMPORTANT — read before editing: the `'widget'` body is serialised to source
 * at build time and executed inside a separate WidgetKit runtime. It must be
 * fully self-contained: it may only reference its `props` / `environment`, plain
 * JS built-ins, and the `@expo/ui/swift-ui` primitives that runtime injects as
 * globals. That's why the palette is inlined (mirroring `src/constants/theme.ts`)
 * instead of imported, why every helper lives inside the function, and why
 * children are flat arrays rather than fragments. Tapping anywhere opens the app
 * via `widgetURL`; the large size layers Plan / New deep links on top.
 *
 * The Pop look (thick rounded border + hard offset shadow) is built from shapes
 * in a ZStack — a shadowed border-colour `RoundedRectangle` with an inset surface
 * one on top, behind the content — so the shadow falls on the box, never the
 * text, and the border is a clean single stroke (a capsule via layered fills
 * renders a deviant concentric double outline, so everything is rounded-rect).
 */
const GlideWidget = (props: GlideWidgetPayload, environment: WidgetEnvironment) => {
  'widget';

  const dark = environment.colorScheme === 'dark';
  // Mirrors src/constants/theme.ts — inlined because the widget runtime can't
  // import it (see the note above).
  const c = dark
    ? {
        bg: '#15121C',
        surface: '#241D33',
        text: '#F7F3FF',
        textSecondary: '#B7AECB',
        border: '#0A0810',
        accent: '#9B7BFF',
        accentSoft: '#2E2747',
        clear: '#22D67E',
      }
    : {
        bg: '#FFF6E9',
        surface: '#FFFFFF',
        text: '#1A1626',
        textSecondary: '#6B6280',
        border: '#1A1626',
        accent: '#7B5BFF',
        accentSoft: '#EBE5FF',
        clear: '#12C46A',
      };
  const shadowColor = dark ? '#000000' : c.border;

  const family = environment.widgetFamily;
  const current = props.current;
  const next = props.next ?? [];
  const readyCount = props.readyCount ?? 0;
  const unsorted = props.unsorted ?? 0;
  const done = props.done ?? 0;

  const fillW = frame({ maxWidth: Infinity, alignment: 'leading' });

  // The shadowed, bordered surface behind a card's content. Shapes are flexible,
  // so they fill whatever the ZStack ends up being (content-hugging or grown).
  const popBacking = (radius: number, fill: string, sx: number, sy: number) => [
    <RoundedRectangle
      key="shadow"
      cornerRadius={radius}
      modifiers={[foregroundStyle(c.border), shadow({ radius: 0, x: sx, y: sy, color: shadowColor })]}
    />,
    <RoundedRectangle
      key="fill"
      cornerRadius={radius - 2.5}
      modifiers={[foregroundStyle(fill), padding({ all: 2.5 })]}
    />,
  ];

  // A full-width Pop card. `grow` fills the remaining height and centres the
  // content (small/medium hero); otherwise it hugs the content (large hero, rows).
  const Card = ({
    children,
    radius,
    pad,
    fill,
    sx,
    sy,
    grow,
  }: {
    children: ReactNode;
    radius: number;
    pad: number;
    fill: string;
    sx: number;
    sy: number;
    grow?: boolean;
  }) => (
    <ZStack
      modifiers={grow ? [fillW, frame({ maxHeight: Infinity })] : [fillW, fixedSize({ horizontal: false, vertical: true })]}>
      {[
        ...popBacking(radius, fill, sx, sy),
        <VStack key="content" alignment="leading" spacing={6} modifiers={[padding({ all: pad }), fillW]}>
          {children}
        </VStack>,
      ]}
    </ZStack>
  );

  // A content-hugging Pop pill/button (the app's count pill / PopButton).
  const Bubble = ({
    children,
    radius,
    padX,
    padY,
    fill,
    sx,
    sy,
  }: {
    children: ReactNode;
    radius: number;
    padX: number;
    padY: number;
    fill: string;
    sx: number;
    sy: number;
  }) => (
    <ZStack modifiers={[fixedSize({ horizontal: true, vertical: true })]}>
      {[
        ...popBacking(radius, fill, sx, sy),
        <HStack key="content" alignment="center" spacing={6} modifiers={[padding({ horizontal: padX, vertical: padY })]}>
          {children}
        </HStack>,
      ]}
    </ZStack>
  );

  // The app's outlined check circle (border ring on surface).
  const Ring = ({ size }: { size: number }) => (
    <ZStack modifiers={[frame({ width: size, height: size })]}>
      {[
        <Circle key="b" modifiers={[foregroundStyle(c.border)]} />,
        <Circle key="f" modifiers={[foregroundStyle(c.surface), padding({ all: 2.5 })]} />,
      ]}
    </ZStack>
  );

  const Chip = ({ label, fg, bg }: { label: string; fg: string; bg: string }) => (
    <Bubble radius={13} padX={12} padY={5} fill={bg} sx={2} sy={3}>
      <Text modifiers={[font({ size: 12, weight: 'heavy', design: 'rounded' }), foregroundStyle(fg)]}>{label}</Text>
    </Bubble>
  );

  // Ready = green (cleared, to do); Done = accent purple (actioned this session).
  const readyChip = readyCount > 0 ? <Chip label={`${readyCount} ready`} fg="#062313" bg={c.clear} /> : null;
  const doneChip = done > 0 ? <Chip label={`${done} done`} fg="#FFFFFF" bg={c.accent} /> : null;

  const Wordmark = ({ size }: { size: number }) => (
    <Text modifiers={[font({ size, weight: 'heavy', design: 'rounded' }), foregroundStyle(c.text)]}>Glide</Text>
  );

  // A horizontal count badge for the small scoreboard — a coloured card with the
  // number and label in a row. `ink` is the on-fill colour (dark on green Ready,
  // white on purple Done).
  const HBadge = ({ value, label, fill, ink }: { value: number; label: string; fill: string; ink: string }) => (
    <Card radius={18} pad={8} fill={fill} sx={4} sy={5} grow>
      <HStack alignment="center" spacing={8}>
        <Spacer />
        <Text
          modifiers={[
            font({ size: 34, weight: 'heavy', design: 'rounded' }),
            foregroundStyle(ink),
            lineLimit(1),
            minimumScaleFactor(0.5),
          ]}>
          {String(value)}
        </Text>
        <Text
          modifiers={[
            font({ size: 15, weight: 'heavy', design: 'rounded' }),
            foregroundStyle(ink),
            opacity(0.8),
            lineLimit(1),
            minimumScaleFactor(0.5),
          ]}>
          {label}
        </Text>
        <Spacer />
      </HStack>
    </Card>
  );

  // The current task, sized to its size class. Shrinks to fit rather than
  // truncating, so even a long title stays readable.
  const Task = ({ size, limit, scale }: { size: number; limit: number; scale: number }) => (
    <Text
      modifiers={[
        font({ size, weight: 'heavy', design: 'rounded' }),
        foregroundStyle(c.text),
        lineLimit(limit),
        multilineTextAlignment('leading'),
        minimumScaleFactor(scale),
      ]}>
      {current ?? ''}
    </Text>
  );

  // Friendly fallback — distinguishes "go plan" from a genuinely-empty all-clear
  // state. Emoji + title in a card, mirroring the in-app EmptyState. Vertical
  // (emoji above title) by default — what the in-app view and the roomy large
  // widget use; the short medium widget passes `row` to set the emoji beside the
  // title instead, where a vertical stack felt cramped. All-clear carries no
  // tagline (it was needless); "nothing ready" keeps a functional count.
  const empty: { emoji: string; title: string; sub: string } =
    unsorted > 0
      ? { emoji: '🗂️', title: 'Nothing ready', sub: `${unsorted} to plan` }
      : { emoji: '🎉', title: 'All clear', sub: '' };
  const emptyCard = (emojiSize: number, titleSize: number, row: boolean) => {
    const icon = (
      <Text key="icon" modifiers={[font({ size: emojiSize })]}>
        {empty.emoji}
      </Text>
    );
    const title = (
      <Text
        key="title"
        modifiers={[
          font({ size: titleSize, weight: 'heavy', design: 'rounded' }),
          foregroundStyle(c.text),
          multilineTextAlignment('center'),
        ]}>
        {empty.title}
      </Text>
    );
    const sub = empty.sub ? (
      <Text
        key="sub"
        modifiers={[font({ size: 13, weight: 'bold', design: 'rounded' }), foregroundStyle(c.textSecondary)]}>
        {empty.sub}
      </Text>
    ) : null;

    return (
      <Card key="empty" radius={22} pad={16} fill={c.surface} sx={5} sy={6}>
        {row ? (
          <HStack spacing={10} alignment="center" modifiers={[frame({ maxWidth: Infinity })]}>
            {[
              <Spacer key="sl" />,
              icon,
              sub ? (
                <VStack key="text" spacing={1} alignment="leading">
                  {[title, sub]}
                </VStack>
              ) : (
                title
              ),
              <Spacer key="sr" />,
            ]}
          </HStack>
        ) : (
          <VStack spacing={6} alignment="center" modifiers={[frame({ maxWidth: Infinity })]}>
            {[icon, title, sub].filter(Boolean)}
          </VStack>
        )}
      </Card>
    );
  };

  const root = (children: ReactNode) => (
    <VStack
      spacing={10}
      alignment="leading"
      modifiers={[padding({ all: 3 }), containerBackground(c.bg, 'widget'), widgetURL('glide://')]}>
      {children}
    </VStack>
  );

  // The current-task hero. Sized per family and hardened for the 64-char title cap
  // (see TITLE_MAX_LENGTH in src/domain/glide.ts): the line limit × shrink floor
  // leaves enough room for the longest allowed title without truncating, while
  // short titles stay large. The empty state renders as a separate compact card.
  const heroCard = (taskSize: number, limit: number, scale: number, grow: boolean) => (
    <Card radius={22} pad={16} fill={c.surface} sx={5} sy={6} grow={grow}>
      <Task size={taskSize} limit={limit} scale={scale} />
    </Card>
  );

  if (family === 'systemMedium') {
    const header = (
      <HStack key="head" spacing={6} alignment="center">
        <Wordmark size={16} />
        <Spacer />
        {readyChip}
        {doneChip}
      </HStack>
    );
    return current
      ? root([header, heroCard(22, 4, 0.5, true)])
      : root([header, <Spacer key="s1" />, emptyCard(30, 20, true), <Spacer key="s2" />]);
  }

  if (family === 'systemLarge') {
    const shown = Math.min(next.length, 2);
    const rows = next.slice(0, shown);
    const more = Math.max(readyCount - 1 - shown, 0);

    const ActionLink = ({
      label,
      symbol,
      url,
      fg,
      bg,
    }: {
      label: string;
      symbol: SFSymbol;
      url: string;
      fg: string;
      bg: string;
    }) => (
      <Link destination={url}>
        <Bubble radius={16} padX={16} padY={10} fill={bg} sx={3} sy={4}>
          <Image systemName={symbol} size={13} color={fg} />
          <Text modifiers={[font({ size: 14, weight: 'bold', design: 'rounded' }), foregroundStyle(fg)]}>
            {label}
          </Text>
        </Bubble>
      </Link>
    );

    const NextRow = ({ title }: { title: string }) => (
      <Card radius={14} pad={10} fill={c.surface} sx={3} sy={4}>
        <HStack spacing={10} alignment="center">
          <Ring size={20} />
          <Text
            modifiers={[
              font({ size: 15, weight: 'semibold', design: 'rounded' }),
              foregroundStyle(c.text),
              lineLimit(1),
            ]}>
            {title}
          </Text>
          <Spacer />
        </HStack>
      </Card>
    );

    const nextSection =
      shown > 0
        ? [
            <Text
              key="label"
              modifiers={[font({ size: 12, weight: 'heavy', design: 'rounded' }), foregroundStyle(c.textSecondary)]}>
              NEXT UP
            </Text>,
            ...rows.map((title, i) => <NextRow key={`r${i}`} title={title} />),
            more > 0 ? (
              <Text
                key="more"
                modifiers={[font({ size: 13, weight: 'bold', design: 'rounded' }), foregroundStyle(c.textSecondary)]}>
                {`+${more} more ready`}
              </Text>
            ) : null,
          ].filter(Boolean)
        : [];

    const header = (
      <HStack key="head" spacing={6} alignment="center">
        <Wordmark size={19} />
        <Spacer />
        {readyChip}
        {doneChip}
      </HStack>
    );
    const actions = (
      <HStack key="actions" spacing={10} alignment="center">
        <ActionLink label="Plan" symbol="rectangle.stack.fill" url="glide://plan" fg={c.text} bg={c.surface} />
        <ActionLink label="New task" symbol="plus" url="glide://new" fg="#FFFFFF" bg={c.accent} />
        <Spacer />
      </HStack>
    );

    if (!current) {
      return root([header, <Spacer key="s1" />, emptyCard(48, 26, false), <Spacer key="s2" />, actions]);
    }

    return root([
      header,
      heroCard(24, 3, 0.5, false),
      nextSection.length > 0 ? (
        <VStack key="next" spacing={8} alignment="leading" modifiers={[padding({ top: 8 })]}>
          {nextSection}
        </VStack>
      ) : null,
      <Spacer key="fill" />,
      actions,
    ]);
  }

  // systemSmall (and any unconfigured accessory family) — a Ready / Done
  // scoreboard as two horizontal *coloured* badges (green / purple), echoing the
  // count pills: white cards are reserved for task content everywhere else.
  return root([
    <HBadge key="ready" value={readyCount} label="Ready" fill={c.clear} ink="#062313" />,
    <HBadge key="done" value={done} label="Done" fill={c.accent} ink="#FFFFFF" />,
  ]);
};

export default createWidget('GlideWidget', GlideWidget);
