/**
 * TagInput — reusable chip-based tag input with autocomplete.
 *
 * Usage:
 *   import { TagInput } from '../core/tag-input.ts';
 *
 *   const tagInput = new TagInput(document.getElementById('my-container'));
 *   tagInput.setValue(['bedroom', 'gaming']);
 *   tagInput.getValue();  // ['bedroom', 'gaming']
 *   tagInput.destroy();
 *
 * The component fetches available tags from GET /api/v1/tags for autocomplete.
 * Tags are stored lowercase, trimmed, deduplicated.
 */

import { fetchWithAuth } from './api.ts';

let _allTagsCache: string[] | null = null;
let _allTagsFetchPromise: Promise<string[]> | null = null;

/** Fetch all tags from API (cached). Call invalidateTagsCache() after mutations. */
export async function fetchAllTags(): Promise<string[]> {
    if (_allTagsCache) return _allTagsCache;
    if (_allTagsFetchPromise) return _allTagsFetchPromise;
    _allTagsFetchPromise = fetchWithAuth('/tags')
        .then(r => r.json())
        .then(data => {
            _allTagsCache = data.tags || [];
            _allTagsFetchPromise = null;
            return _allTagsCache!;
        })
        .catch(() => {
            _allTagsFetchPromise = null;
            return [];
        });
    return _allTagsFetchPromise;
}

/** Call after create/update to refresh autocomplete suggestions. */
export function invalidateTagsCache() {
    _allTagsCache = null;
}

/**
 * Render tag chips HTML for display on cards.
 * @param {string[]} tags
 * @returns {string} HTML string
 */
export function renderTagChips(tags: string[]): string {
    if (!tags || !tags.length) return '';
    return `<div class="card-tags">${tags.map(tag =>
        `<span class="card-tag">${_escapeHtml(tag)}</span>`
    ).join('')}</div>`;
}

function _escapeHtml(str: string): string {
    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

export class TagInput {
    _container: HTMLElement;
    _tags: string[];
    _placeholder: string;
    _dropdownVisible: boolean;
    _selectedIdx: number;
    _chipsEl!: HTMLElement;
    _inputEl!: HTMLInputElement;
    _dropdownEl!: HTMLElement;

    /**
     * @param {HTMLElement} container  Element to render the tag input into
     * @param {object} [opts]
     * @param {string} [opts.placeholder]  Placeholder text for input
     */
    constructor(container: HTMLElement | null, opts: any = {}) {
        this._container = container!;
        this._tags = [];
        this._placeholder = opts.placeholder || 'Add tag...';
        this._dropdownVisible = false;
        this._selectedIdx = -1;

        this._render();
        this._bindEvents();
    }

    getValue(): string[] {
        return [...this._tags];
    }

    setValue(tags: string[]) {
        this._tags = (tags || []).map(t => t.toLowerCase().trim()).filter(Boolean);
        this._tags = [...new Set(this._tags)];
        this._renderChips();
    }

    destroy() {
        this._container.innerHTML = '';
        this._hideDropdown();
    }

    // ── private ──

    _render() {
        this._container.innerHTML = `
            <div class="tag-input-wrap">
                <div class="tag-input-chips"></div>
                <input type="text" class="tag-input-field" placeholder="${_escapeHtml(this._placeholder)}" autocomplete="off" spellcheck="false">
                <div class="tag-input-dropdown"></div>
            </div>
        `;
        this._chipsEl = this._container.querySelector('.tag-input-chips') as HTMLElement;
        this._inputEl = this._container.querySelector('.tag-input-field') as HTMLInputElement;
        this._dropdownEl = this._container.querySelector('.tag-input-dropdown') as HTMLElement;
    }

    _renderChips() {
        this._chipsEl.innerHTML = this._tags.map((tag, i) =>
            `<span class="tag-chip">${_escapeHtml(tag)}<button type="button" class="tag-chip-remove" data-idx="${i}">&times;</button></span>`
        ).join('');
    }

    _bindEvents() {
        // Chip remove buttons
        this._chipsEl.addEventListener('click', (e) => {
            const btn = (e.target as HTMLElement).closest('.tag-chip-remove') as HTMLElement | null;
            if (!btn) return;
            const idx = parseInt((btn as HTMLElement).dataset.idx!, 10);
            this._tags.splice(idx, 1);
            this._renderChips();
        });

        // Input keydown
        this._inputEl.addEventListener('keydown', (e) => {
            if (e.key === 'Enter' || e.key === ',' || e.key === 'Tab') {
                if (this._dropdownVisible && this._selectedIdx >= 0) {
                    // Select from dropdown
                    e.preventDefault();
                    const items = this._dropdownEl.querySelectorAll('.tag-dropdown-item');
                    if (items[this._selectedIdx]) {
                        this._addTag((items[this._selectedIdx] as HTMLElement).dataset.tag!);
                    }
                } else if (this._inputEl.value.trim()) {
                    e.preventDefault();
                    this._addTag(this._inputEl.value);
                }
            } else if (e.key === 'Backspace' && !this._inputEl.value && this._tags.length) {
                this._tags.pop();
                this._renderChips();
            } else if (e.key === 'ArrowDown' && this._dropdownVisible) {
                e.preventDefault();
                this._moveSelection(1);
            } else if (e.key === 'ArrowUp' && this._dropdownVisible) {
                e.preventDefault();
                this._moveSelection(-1);
            } else if (e.key === 'Escape') {
                this._hideDropdown();
            }
        });

        // Input typing → autocomplete
        this._inputEl.addEventListener('input', () => {
            this._updateDropdown();
        });

        // Focus → show dropdown
        this._inputEl.addEventListener('focus', () => {
            this._updateDropdown();
        });

        // Blur → hide (with delay so clicks register)
        this._inputEl.addEventListener('blur', () => {
            setTimeout(() => this._hideDropdown(), 200);
        });

        // Dropdown click
        this._dropdownEl.addEventListener('mousedown', (e) => {
            e.preventDefault(); // prevent blur
            const item = (e.target as HTMLElement).closest('.tag-dropdown-item') as HTMLElement | null;
            if (item) this._addTag(item.dataset.tag!);
        });
    }

    _addTag(raw: string) {
        const tag = raw.toLowerCase().trim().replace(/,/g, '');
        if (!tag || this._tags.includes(tag)) {
            this._inputEl.value = '';
            this._hideDropdown();
            return;
        }
        this._tags.push(tag);
        this._renderChips();
        this._inputEl.value = '';
        this._hideDropdown();
        invalidateTagsCache();
    }

    async _updateDropdown() {
        const query = this._inputEl.value.toLowerCase().trim();
        const allTags = await fetchAllTags();

        // Filter: exclude already-selected tags, match query
        const suggestions = allTags
            .filter(t => !this._tags.includes(t))
            .filter(t => !query || t.includes(query))
            .slice(0, 8);

        if (!suggestions.length) {
            this._hideDropdown();
            return;
        }

        this._dropdownEl.innerHTML = suggestions.map((tag, i) =>
            `<div class="tag-dropdown-item${i === 0 ? ' tag-dropdown-active' : ''}" data-tag="${_escapeHtml(tag)}">${_escapeHtml(tag)}</div>`
        ).join('');
        this._dropdownEl.classList.add('open');
        this._dropdownVisible = true;
        this._selectedIdx = 0;
    }

    _hideDropdown() {
        this._dropdownEl.classList.remove('open');
        this._dropdownVisible = false;
        this._selectedIdx = -1;
    }

    _moveSelection(delta: number) {
        const items = this._dropdownEl.querySelectorAll('.tag-dropdown-item');
        if (!items.length) return;
        items[this._selectedIdx]?.classList.remove('tag-dropdown-active');
        this._selectedIdx = Math.max(0, Math.min(items.length - 1, this._selectedIdx + delta));
        items[this._selectedIdx]?.classList.add('tag-dropdown-active');
        items[this._selectedIdx]?.scrollIntoView({ block: 'nearest' });
    }
}
