import { ApiClient } from '../../core/ApiClient';
import { EditorCore } from './EditorCore';
import type { FindOrCreateTagRequest, Tag, TagListResponse, TagScope, UpdatePageTagsResponseData } from '../../types/api/TagApi';

/**
 * Page-level multi-select tag picker, mounted at `#tag-picker` — shared by Actualité
 * (templates/pages/actualite.html.twig, via components/_page_hero.html.twig) and
 * ProjectDetail (templates/projects/detail.html.twig's hero) rather than duplicated per
 * template (mutualized system, see the ProjectDetail hero tags decision).
 *
 * `data-tag-scope` (`Tag::SCOPE_ACTUALITE` / `SCOPE_PROJECT` / `SCOPE_BOTH`) filters the
 * catalog fetched from `GET /api/admin/tags?scope=...` to tags usable in this context —
 * see `TagRepository::findAllOrderedByName()` (a `SCOPE_BOTH` tag is always included).
 *
 * Primary flow: pick an existing tag from that filtered catalog as a chip. A secondary
 * "+ Nouveau tag" input lets the admin create one on the fly when nothing fits — it calls
 * `POST /api/admin/tags/find-or-create` (`TagManagementService::findOrCreateByName()`),
 * which reuses a same-name tag (case-insensitive) instead of erroring, then selects it.
 * New tags get this picker's scope.
 *
 * Tags are an entity relation (`Page` <-> `Tag`), not part of the template's `content`
 * JSON, so selection is persisted via a dedicated endpoint (`PATCH /api/admin/pages/{id}/tags`,
 * see `PageApiController::updateTags()` / `PageTagAssignmentService`) rather than through
 * `EditorApiController::saveContent()`. This class registers that persistence as an
 * "additional save handler" on the shared EditorCore, so a single "Enregistrer" click
 * commits both content and tags together.
 */
export class TagPicker {
    private container: HTMLElement;
    private pageId: string;
    private scope: TagScope;
    private apiClient: ApiClient;
    private editorCore: EditorCore;

    private allTags: Tag[] = [];
    private selectedTagIds: Set<string> = new Set();

    private chipsContainer!: HTMLElement;
    private addSelect!: HTMLSelectElement;
    private createInput!: HTMLInputElement;
    private createButton!: HTMLButtonElement;

    constructor(container: HTMLElement, editorCore: EditorCore) {
        this.container = container;
        this.pageId = container.dataset.pageId || '';
        this.scope = (container.dataset.tagScope as TagScope) || 'both';
        this.apiClient = new ApiClient();
        this.editorCore = editorCore;

        const initialIds = (container.dataset.selectedTagIds || '')
            .split(',')
            .map(id => id.trim())
            .filter(id => id.length > 0);
        this.selectedTagIds = new Set(initialIds);
    }

    public async initialize(): Promise<void> {
        this.renderShell();

        await this.loadTags();
        this.render();

        this.editorCore.registerAdditionalSaveHandler(() => this.save());
    }

    private async loadTags(): Promise<void> {
        const response = await this.apiClient.get<TagListResponse>(
            `/api/admin/tags?scope=${encodeURIComponent(this.scope)}`
        );
        if (response.success && response.data) {
            this.allTags = response.data;
        } else {
            console.error('[TagPicker] Failed to load tags:', response.error);
        }
    }

    private renderShell(): void {
        this.container.classList.add('tag-picker');

        this.chipsContainer = document.createElement('div');
        this.chipsContainer.className = 'tag-picker__chips';

        this.addSelect = document.createElement('select');
        this.addSelect.className = 'tag-picker__add-select';

        this.createInput = document.createElement('input');
        this.createInput.type = 'text';
        this.createInput.className = 'tag-picker__create-input';
        this.createInput.placeholder = 'Nouveau tag…';

        this.createButton = document.createElement('button');
        this.createButton.type = 'button';
        this.createButton.className = 'tag-picker__create-button';
        this.createButton.textContent = '+ Créer';

        this.container.appendChild(this.chipsContainer);
        this.container.appendChild(this.addSelect);
        this.container.appendChild(this.createInput);
        this.container.appendChild(this.createButton);

        this.addSelect.addEventListener('change', () => {
            const tagId = this.addSelect.value;
            if (tagId) {
                this.selectTag(tagId);
            }
        });

        this.createButton.addEventListener('click', () => this.createTag());
        this.createInput.addEventListener('keydown', event => {
            if (event.key === 'Enter') {
                event.preventDefault();
                this.createTag();
            }
        });
    }

    private render(): void {
        this.renderChips();
        this.renderAddSelect();
    }

    private renderChips(): void {
        this.chipsContainer.innerHTML = '';

        this.selectedTagIds.forEach(tagId => {
            const tag = this.allTags.find(t => t.id === tagId);
            const label = tag ? tag.name : tagId;

            const chip = document.createElement('span');
            chip.className = 'tag-picker__chip';
            chip.dataset.tagId = tagId;

            const labelSpan = document.createElement('span');
            labelSpan.className = 'tag-picker__chip-label';
            labelSpan.textContent = label;

            const removeButton = document.createElement('button');
            removeButton.type = 'button';
            removeButton.className = 'tag-picker__chip-remove';
            removeButton.setAttribute('aria-label', `Retirer le tag ${label}`);
            removeButton.textContent = '×';
            removeButton.addEventListener('click', () => this.deselectTag(tagId));

            chip.appendChild(labelSpan);
            chip.appendChild(removeButton);
            this.chipsContainer.appendChild(chip);
        });
    }

    private renderAddSelect(): void {
        this.addSelect.innerHTML = '';

        const placeholderOption = document.createElement('option');
        placeholderOption.value = '';
        placeholderOption.textContent = '+ Ajouter un tag';
        this.addSelect.appendChild(placeholderOption);

        const availableTags = this.allTags.filter(tag => !this.selectedTagIds.has(tag.id));

        availableTags.forEach(tag => {
            const option = document.createElement('option');
            option.value = tag.id;
            option.textContent = tag.name;
            this.addSelect.appendChild(option);
        });

        this.addSelect.disabled = availableTags.length === 0;
    }

    private selectTag(tagId: string): void {
        this.selectedTagIds.add(tagId);
        this.render();
        this.editorCore.getChangeTracker().markChanged();
    }

    private deselectTag(tagId: string): void {
        this.selectedTagIds.delete(tagId);
        this.render();
        this.editorCore.getChangeTracker().markChanged();
    }

    private async createTag(): Promise<void> {
        const name = this.createInput.value.trim();
        if (!name) {
            return;
        }

        this.createButton.disabled = true;

        const response = await this.apiClient.post<Tag>('/api/admin/tags/find-or-create', {
            name,
            scope: this.scope,
        } satisfies FindOrCreateTagRequest);

        this.createButton.disabled = false;

        if (!response.success || !response.data) {
            console.error('[TagPicker] Failed to create tag:', response.error);
            return;
        }

        const tag = response.data;
        if (!this.allTags.some(existing => existing.id === tag.id)) {
            this.allTags.push(tag);
        }

        this.createInput.value = '';
        this.selectTag(tag.id);
    }

    private async save(): Promise<boolean> {
        if (!this.pageId) {
            console.error('[TagPicker] Missing page id, cannot save tags');
            return false;
        }

        const response = await this.apiClient.patch<UpdatePageTagsResponseData>(
            `/api/admin/pages/${this.pageId}/tags`,
            { tagIds: Array.from(this.selectedTagIds) }
        );

        return response.success;
    }
}
