import { describe, it, expect, beforeEach } from 'vitest';
import { droppableCollection } from './droppable-collection.js';
import { mountHost, connectTrait, spyEvent, resetDOM } from '../test-helpers.js';

// Minimal smoke coverage. The droppable-collection trait coordinates
// child droppables of the apps/tasks/ DnD model;
// the cases below verify only the trait's own surface (schema, attribute
// lifecycle, child-drop re-emission) so the verify:traits gate stays
// green. End-to-end multi-column behavior belongs in the playground tests.

describe('droppable-collection', () => {
  beforeEach(resetDOM);

  it('schema shape is defined', () => {
    expect(droppableCollection.schema.name).toBe('droppable-collection');
    expect(droppableCollection.schema.category).toBe('input-interaction');
    expect(droppableCollection.schema.description.length).toBeGreaterThanOrEqual(11);
    expect(droppableCollection.schema.events).toContain('dnd-collection-drop');
  });

  it('connect sets data-droppable-collection-active', () => {
    const host = mountHost('div');
    connectTrait(droppableCollection, host);
    expect(host.hasAttribute('data-droppable-collection-active')).toBe(true);
  });

  it('disconnect clears its attributes', () => {
    const host = mountHost('div');
    const inst = droppableCollection();
    inst.connect(host);
    host.setAttribute('data-droppable-collection-dragging', '');
    inst.disconnect(host);
    expect(host.hasAttribute('data-droppable-collection-active')).toBe(false);
    expect(host.hasAttribute('data-droppable-collection-dragging')).toBe(false);
  });

  it('re-emits a child dnd-drop-receive as dnd-collection-drop on the host', () => {
    // The collection listens for the TARGET-side `dnd-drop-receive` (emitted
    // by the [droppable] trait on its host), NOT the source-side `dnd-drop`
    // (emitted by the lifter on document). The two events are deliberately
    // distinct names so that the target re-emit doesn't bubble back into
    // the lifter's document-level listener and recurse.
    const host = mountHost('div');
    const child = document.createElement('div');
    host.appendChild(child);
    connectTrait(droppableCollection, host);

    const spy = spyEvent(host, 'dnd-collection-drop');
    child.dispatchEvent(new CustomEvent('dnd-drop-receive', {
      bubbles: true,
      composed: false,
      detail: {
        source_id: 't-1',
        source_index: 0,
        source_container_id: 'col-a',
        target_container_id: 'col-b',
        target_index: 2,
      },
    }));

    expect(spy.count).toBe(1);
    expect(spy.last).toMatchObject({
      source_id: 't-1',
      target_container_id: 'col-b',
      target_index: 2,
    });
    spy.cleanup();
  });

  it('does NOT react to the source-side dnd-drop (would recurse)', () => {
    // Regression guard: a source-side `dnd-drop` bubbling up from a child
    // must NOT trigger `dnd-collection-drop`. Only the target-side
    // `dnd-drop-receive` is the trigger.
    const host = mountHost('div');
    const child = document.createElement('div');
    host.appendChild(child);
    connectTrait(droppableCollection, host);

    const spy = spyEvent(host, 'dnd-collection-drop');
    child.dispatchEvent(new CustomEvent('dnd-drop', {
      bubbles: true,
      composed: false,
      detail: { source_id: 't-1', target_container_id: 'col-b', target_index: 0 },
    }));
    expect(spy.count).toBe(0);
    spy.cleanup();
  });

  it('reflects dragging state from dnd-lift / dnd-drop-cancel', () => {
    const host = mountHost('div');
    connectTrait(droppableCollection, host);

    host.dispatchEvent(new CustomEvent('dnd-lift', { bubbles: true, detail: {} }));
    expect(host.hasAttribute('data-droppable-collection-dragging')).toBe(true);

    host.dispatchEvent(new CustomEvent('dnd-drop-cancel', { bubbles: true, detail: {} }));
    expect(host.hasAttribute('data-droppable-collection-dragging')).toBe(false);
  });
});
