// The example module (#126): the smallest module MM-Work's shell loads, and the reference for module repos.
// It is built with mmworkModule() (vite.config.mjs), uses only the shared design system, and reaches Drive
// through MM-Work's surface actions, never its own Google access.
import { useEffect, useState } from 'react';
import { Button, Card, Cluster, DataList, Page, Stack, Text } from '@mutmutco/mmwork-ds';

export const manifest = { id: '{{REPO_SLUG}}', name: '{{REPO_NAME}}' };

// In the registry, declare `settings: { groups: [{ title: 'Display', items: [
//   { key: 'compact', label: 'Compact view', type: 'toggle', default: false, lockable: true },
//   { key: 'welcome', label: 'Welcome message', type: 'text', default: 'Hello', admin: true }
// ] }] }`. MM-Work draws those choices; ctx.settings.get('compact') reads the effective value.
// Declare `search: { title: 'Example module' }` and `sections: [{ label: 'Home', path: '/' },
// { label: 'Details', path: '/details' }]` in the registry. Results are data, not rendered rows.
export const search = {
  find: async (query, ctx, signal) => {
    if (signal.aborted) return { results: [] };
    const files = await ctx.act('files.search', { query }); // this person's Google grant and access rules
    if (signal.aborted) return { results: [] };
    return { results: files.items.slice(0, 20).map((file) => ({ title: file.name, context: 'Recent files', path: '/details' })), count: files.items.length };
  },
};

// Registry job: `jobs: [{ key: 'files.review', label: 'Review files', default: 'admins' }]`.
// The screen checks the job below; the module backend checks the sign-in `jobs` claim too.
// Home widgets (docs/decisions/2026-09-26-home-widgets.md): data only, MM-Work draws them. The registry entry declares
// { id: 'files', type: 'figure' }, { id: 'todo', type: 'list' } and { id: 'note', type: 'quick-add' }.
const due = (days) => new Date(Date.now() + days * 86_400_000).toISOString().slice(0, 10);
export const widgets = {
  // A surface action's `reconnect` refusal becomes the not-connected state by itself.
  files: { load: async (ctx) => {
    const r = await ctx.act('files.list', { source: 'recent' });
    return { figures: [{ label: 'Recent files', value: r.items.length, foot: 'In your Drive', href: ctx.base }] };
  } },
  todo: { refresh: 300, load: async (ctx) => ({
    count: 1,
    total: 7,
    rows: [
      { id: '1', title: 'Send the campaign brief', context: 'Autumn campaign', due: due(-1), priority: 'urgent', href: `${ctx.base}/details` },
      { id: '2', title: 'Review the logo options', context: 'Brand refresh', due: due(0), priority: 'high', unread: true, href: `${ctx.base}/details` },
      { id: '3', title: 'Book the studio', context: 'Autumn campaign', due: due(3), href: `${ctx.base}/details` },
      { id: '4', title: 'Share the moodboard', context: 'Brand refresh', due: due(9), priority: 'low', href: `${ctx.base}/details` },
    ],
  }) },
  note: { placeholder: 'Jot a note', submit: async (ctx) => ({ href: `${ctx.base}/details` }) },
};

export default function ExampleModule({ ctx }) {
  const [files, setFiles] = useState(null);
  const [failed, setFailed] = useState(false);
  // Help (GUIDE: Help (required)): name this screen's registry help; its tour rings the data-tour pieces below.
  useEffect(() => { ctx.help.screen('home'); }, [ctx.help]);
  useEffect(() => {
    ctx.act('files.list', { source: 'recent' })
      .then((r) => setFiles(r.items))
      .catch(() => setFailed(true));
  }, [ctx]);

  return (
    <Page title={`Hello, ${ctx.person.name ?? ctx.person.email}`} width="narrow">
      <Stack gap="groups">
        <Text as="p" variant="lead" data-tour="example-intro">
          This screen comes from a module's own bundle. It uses MM-Work's React and design system, and reads Drive through MM-Work.
        </Text>
        <Card data-tour="example-overview" title="What the module sees" icon="info">
          <DataList rows={[
            { label: 'Workspace', value: ctx.workspace.name },
            { label: 'Address in MM-Work', value: `${ctx.base}${ctx.path === '/' ? '' : ctx.path}` },
            { label: 'Recent files (files.list)', value: failed ? 'Drive is not connected' : files === null ? 'Loading…' : String(files.length) },
            { label: 'Review files job', value: ctx.can('files.review') ? 'Held' : 'Not held' },
          ]} />
        </Card>
        <Cluster data-tour="example-actions">
          <Button variant="secondary" onClick={() => ctx.navigate(`${ctx.base}/details`)}>Open a page inside the module</Button>
          <Button variant="ghost" onClick={() => ctx.navigate('/files')}>Go to Files</Button>
        </Cluster>
      </Stack>
    </Page>
  );
}

// Module file adapter (#2177, GUIDE "Module file adapter"): the module's own media at
// /files/example/<id> opens in the shell's file viewer through this export. The shell holds only
// the opaque id; this check decides, so a sealed item never reaches the shell as plaintext.
// `hello` is the demo file, `private` answers forbidden, anything else is not found. Link to it with
// ctx.navigate(ctx.files.href({ source: 'module', module: 'example', id: 'hello' })).
function ExampleFileBody({ ctx, id }) {
  return (
    <Card title={`File ${id}`} icon="doc">
      <DataList rows={[
        { label: 'Workspace', value: ctx.workspace.name },
        { label: 'Opened by', value: ctx.person.name ?? ctx.person.email },
      ]} />
      <Text as="p">This body comes from the module's own bundle, inside the shell's file viewer.</Text>
    </Card>
  );
}
export const files = {
  open: async (id, ctx, signal) => {
    if (signal.aborted) return null;
    if (id === 'private') throw Object.assign(new Error('Not a member'), { code: 'forbidden' });
    if (id !== 'hello') return null;
    return { name: 'Hello from the module', kind: 'doc', typeLabel: 'Example file', Body: ExampleFileBody };
  },
};
