import gsap from 'gsap';
import { Flip } from 'gsap/Flip';
import { ActualiteHubFilterState } from '@core/components/ActualiteHubFilterState';

gsap.registerPlugin(Flip);

/**
 * Client-side tag filter for the Actualité Hub grid.
 *
 * Reads server-rendered `data-tags` (JSON array of tag slugs) on each card and the filter
 * pill bar's clicks; toggles card visibility purely via DOM class manipulation. There is
 * intentionally NO fetch/XMLHttpRequest call anywhere in this module — filtering must be
 * instantaneous and must never round-trip to the server (see
 * spec-driven-development/2026-07-11-actualite-hub-page-template.md).
 *
 * Active-tag-set logic lives in ActualiteHubFilterState (multi-tag OR semantics) so that
 * swapping to single-select exclusivity later only requires changing that one class.
 */
export class ActualiteHubFilter {
    private readonly state = new ActualiteHubFilterState();
    private readonly cards: HTMLElement[];

    public constructor(
        private readonly grid: HTMLElement,
        private readonly filterBar: HTMLElement,
        private readonly emptyState: HTMLElement | null,
    ) {
        this.cards = Array.from(this.grid.querySelectorAll<HTMLElement>('[data-tags]'));
    }

    public init(): void {
        this.filterBar.addEventListener('click', (event: MouseEvent) => {
            const target = event.target;
            if (!(target instanceof HTMLElement)) {
                return;
            }

            const resetButton = target.closest<HTMLElement>('[data-filter-reset]');
            if (resetButton) {
                this.state.reset();
                this.render();
                return;
            }

            const pill = target.closest<HTMLElement>('[data-filter-tag]');
            if (pill) {
                const tagSlug = pill.getAttribute('data-filter-tag');
                if (tagSlug) {
                    this.state.toggle(tagSlug);
                    this.render();
                }
            }
        });

        this.render();
    }

    private render(): void {
        this.updatePillStates();

        const state = Flip.getState(this.cards);
        const visibleCount = this.updateCardVisibility();
        this.toggleEmptyState(visibleCount === 0);

        Flip.from(state, {
            duration: 0.5,
            ease: 'power2.inOut',
            absoluteOnLeave: true,
            stagger: 0.03,
            onEnter: (elements) => gsap.fromTo(elements,
                { opacity: 0, scale: 0.9 },
                { opacity: 1, scale: 1, duration: 0.4, ease: 'power2.out' },
            ),
            onLeave: (elements) => gsap.to(elements, {
                opacity: 0,
                scale: 0.9,
                duration: 0.3,
                ease: 'power2.in',
            }),
        });
    }

    private updatePillStates(): void {
        const resetButton = this.filterBar.querySelector<HTMLElement>('[data-filter-reset]');
        resetButton?.classList.toggle('is-active', this.state.isEmpty());

        const pills = this.filterBar.querySelectorAll<HTMLElement>('[data-filter-tag]');
        pills.forEach(pill => {
            const tagSlug = pill.getAttribute('data-filter-tag');
            pill.classList.toggle('is-active', tagSlug !== null && this.state.isActive(tagSlug));
        });
    }

    private updateCardVisibility(): number {
        let visibleCount = 0;

        this.cards.forEach(card => {
            const cardTagSlugs = this.parseCardTags(card);
            const visible = this.state.matches(cardTagSlugs);
            card.classList.toggle('is-hidden', !visible);
            if (visible) {
                visibleCount += 1;
            }
        });

        return visibleCount;
    }

    private toggleEmptyState(isEmpty: boolean): void {
        this.emptyState?.classList.toggle('is-hidden', !isEmpty);
    }

    private parseCardTags(card: HTMLElement): string[] {
        const raw = card.getAttribute('data-tags');
        if (!raw) {
            return [];
        }

        try {
            const parsed: unknown = JSON.parse(raw);
            if (Array.isArray(parsed) && parsed.every(item => typeof item === 'string')) {
                return parsed;
            }
        } catch {
            // Malformed data-tags: treat card as untagged rather than throwing.
        }

        return [];
    }
}
