import type { Meta, StoryObj } from '../../story.types';
import type { DataTableColumnDef, DataTableData } from '../data-table/types';
import { GridSelectMultipleElements } from './grid-select-multiple-elements.component';

interface Permission {
  id: string;
  name: string;
  scope: string;
}

// biome-ignore lint/suspicious/noExplicitAny: Story type must be concrete to be rendered by CSF tooling
type Story = StoryObj<GridSelectMultipleElements<any>>;

const LINKED_ROWS: Permission[] = [
  { id: 'p1', name: 'iam.read', scope: 'Iam' },
  { id: 'p3', name: 'iam.delete', scope: 'Iam' },
];

const CANDIDATE_ROWS: Permission[] = [
  { id: 'p1', name: 'iam.read', scope: 'Iam' },
  { id: 'p2', name: 'iam.write', scope: 'Iam' },
  { id: 'p3', name: 'iam.delete', scope: 'Iam' },
  { id: 'p4', name: 'sales.read', scope: 'Sales' },
  { id: 'p5', name: 'sales.write', scope: 'Sales' },
];

const COLUMNS: DataTableColumnDef<Permission>[] = [
  { id: 'name', header: 'Name', accessorKey: 'name', size: 240 },
  { id: 'scope', header: 'Scope', accessorKey: 'scope', size: 140 },
];

const linkedPage = (rows: Permission[]): DataTableData<Permission> => ({
  count: rows.length,
  total: rows.length,
  rows,
});

const candidatePage = (rows: Permission[]): DataTableData<Permission> => ({
  count: rows.length,
  total: rows.length,
  rows,
});

const meta: Meta<GridSelectMultipleElements<Permission>> = {
  title: '@aurora/components/grid-select-multiple-elements',
  component: GridSelectMultipleElements<Permission>,
  tags: ['autodocs'],
  argTypes: {
    linkedData: {
      control: 'object',
      description:
        'Server-paginated `DataTableData<TLinked>` for the outer (linked) table. Host fetches in response to `linkedStateChange`.',
    },
    linkedColumns: {
      control: 'object',
      description:
        'User columns for the linked table. The component prepends an internal action column (single-row unlink) and a selection column.',
    },
    candidatesData: {
      control: 'object',
      description:
        'Server-paginated `DataTableData<TCandidate>` for the inner pick dialog. Host fetches in response to `candidatesStateChange`.',
    },
    candidatesColumns: { control: 'object' },
    linkedIds: {
      control: 'object',
      description:
        'Set of currently-linked entity ids. Forwarded to `<au-grid-pick-dialog>` as `disabledIds`. Host derives via `usePivotMembership`.',
    },
    loading: {
      control: 'object',
      description: '`{ linked?: boolean; candidates?: boolean }` per-table loading flags.',
    },
    disabled: { control: 'boolean' },
    title: { control: 'text' },
    dialogTitle: { control: 'text' },
    linkButtonLabel: { control: 'text' },
    bulkUnlinkButtonLabel: { control: 'text' },
    rowUnlinkActionLabel: { control: 'text' },
    searchPlaceholder: { control: 'text' },
    emptyLinkedMessage: { control: 'text' },
    emptyCandidatesMessage: { control: 'text' },
    linkedStateChange: { action: 'linkedStateChange' },
    candidatesStateChange: { action: 'candidatesStateChange' },
    linkRequested: { action: 'linkRequested' },
    unlinkRequested: { action: 'unlinkRequested' },
  },
  args: {
    linkedData: linkedPage(LINKED_ROWS),
    linkedColumns: COLUMNS,
    candidatesData: candidatePage(CANDIDATE_ROWS),
    candidatesColumns: COLUMNS,
    linkedIds: new Set(LINKED_ROWS.map((r) => r.id)),
    loading: {},
    disabled: false,
    title: 'Permissions',
    dialogTitle: 'Pick permissions',
    linkButtonLabel: 'Link permission',
    bulkUnlinkButtonLabel: 'Unlink selected',
    rowUnlinkActionLabel: 'Unlink',
    searchPlaceholder: 'Filter permissions...',
    emptyLinkedMessage: 'No permissions linked',
    emptyCandidatesMessage: 'No permissions found',
  },
  parameters: {
    aurora: {
      category: 'form',
      when: 'Many-to-many relationship manager (linked table + pick dialog) for pivots whose rows have only the two FKs. Live link/unlink — every action emits an event the host turns into a server mutation. Runtime target of the YAML `webComponent.type: grid-select-multiple-elements`.',
      whenNot:
        'For one-to-many child CRUD use `grid-elements-manager`. For many-to-one pick (single result) use `grid-select-element` (or `async-search-select` for very large catalogs). For inline multi-select without a dialog use `multiple-search-select`.',
    },
  },
};

export default meta;

export const Default: Story = {};

export const Empty: Story = {
  args: {
    linkedData: linkedPage([]),
    linkedIds: new Set<string>(),
  },
  parameters: {
    docs: {
      description: {
        story:
          'Empty state — no linked items yet. Toolbar shows "+ Link" enabled and "Unlink selected" disabled.',
      },
    },
  },
};

export const Loading: Story = {
  args: {
    loading: { linked: true, candidates: false },
    linkedData: linkedPage([]),
  },
  parameters: {
    docs: {
      description: {
        story: 'Loading state on the outer (linked) table while the host fetches.',
      },
    },
  },
};

export const WithLinkedRows: Story = {
  name: 'With linked rows',
  parameters: {
    docs: {
      description: {
        story:
          'Default state with two linked items. The candidate dialog (open via "+") shows the same two rows with a done-icon (already linked) and the rest as selectable candidates.',
      },
    },
  },
};

export const WithCrossPageSelection: Story = {
  name: 'Cross-page selection (semantic note)',
  parameters: {
    docs: {
      description: {
        story:
          '**Selection semantics**: the outer-table checkbox column persists selection by id across paginate/sort/filter. If the user selects row "p1" on page 1, navigates to page 2 (the host fetches new `linkedData.rows`), and clicks "Unlink selected", the manager emits `unlinkRequested(["p1", ...])` even though "p1" is not visible. Consumers SHOULD confirm with `hlm-alert-dialog` ("You are about to unlink N items, M of them not visible") before mutating.',
      },
    },
  },
};

export const Disabled: Story = {
  args: { disabled: true },
  parameters: {
    docs: {
      description: {
        story:
          'Disabled state blocks the "+ Link" button (cannot open the dialog) and the bulk-unlink button.',
      },
    },
  },
};

export const IntegrationExample: Story = {
  name: 'Integration example (useRelationshipPivot)',
  parameters: {
    docs: {
      description: {
        story: `Canonical integration with the atomized composables:

\`\`\`ts
@Component({
  template: \`
    <au-grid-select-multiple-elements
      [linkedData]="rel.linked.data()"
      [linkedColumns]="permissionColumns"
      [candidatesData]="rel.candidates.data()"
      [candidatesColumns]="permissionColumns"
      [linkedIds]="rel.membership.linkedIds()"
      [loading]="{ linked: rel.linked.loading(), candidates: rel.candidates.loading() }"
      (linkedStateChange)="rel.linked.setSearch($event.search); rel.linked.setSort($event.sorting); rel.linked.setPage($event.pagination)"
      (candidatesStateChange)="rel.candidates.setSearch($event.search); ..."
      (linkRequested)="rel.membership.link($event)"
      (unlinkRequested)="rel.membership.unlink($event)"
    />
  \`,
})
export class RoleDetailComponent {
  private readonly parentId = signal<string>(this.route.snapshot.params['id']);

  protected readonly rel = useRelationshipPivot<IamPermission, IamPermission, IamPermissionRole>({
    linkedListConfig: {
      paginate: input => this.permissionRoleShell.list.paginate(input).pipe(map(toPermissionRow)),
      searchableColumnIds: ['permission.name'],
      constraint: () => ({ where: { roleId: this.parentId() } }),
      relationIncludes: () => [{ association: 'permission' }],
      columns: this.linkedColumns,
    },
    candidatesListConfig: {
      paginate: input => this.permissionShell.list.paginate(input),
      searchableColumnIds: ['name'],
      columns: this.candidateColumns,
    },
    pivotMembershipConfig: {
      pivotConfig: {
        getQuery: getPermissionsRolesQuery,
        getKey: 'objects',
        insertMutation: insertPermissionsRolesMutation,
        deleteMutation: deletePermissionsRolesMutation,
      },
      parentField: 'roleId',
      childField: 'permissionId',
      parentId: () => this.parentId(),
    },
  });

  ngOnInit() { void this.rel.init(); }
}
\`\`\`

The orchestrator triggers \`linked.refetch()\` automatically after \`link\`/\`unlink\` succeeds.`,
      },
    },
  },
};
