import { ApiClient } from '../../core/ApiClient';
import type { Media, MediaFolder, MediaListResponse } from '../../types/api/MediaApi';
import { THEME_CHANGE_EVENT } from '../../core/components/ThemeToggle';
import { ImageFrameEditor } from './ImageFrameEditor';
import { applyFrame } from './imageFrame';

export class MediaSelectorModal {
    private modal: HTMLElement | null = null;
    private apiClient = new ApiClient();
    private currentField: HTMLElement | null = null;
    private onChangeCallback: () => void;
    private currentFolderId: string | null = null;
    private folderTree: MediaFolder[] = [];
    private frameEditor: ImageFrameEditor;

    constructor(onChangeCallback: () => void) {
        this.onChangeCallback = onChangeCallback;
        this.frameEditor = new ImageFrameEditor(() => this.onChangeCallback());
        this.initialize();
    }

    private initialize(): void {
        this.modal = document.getElementById('media-selector-modal');
        if (!this.modal) return;

        const closeBtn = document.getElementById('close-media-selector');
        closeBtn?.addEventListener('click', () => this.close());

        this.modal.addEventListener('click', (e) => {
            if (e.target === this.modal) {
                this.close();
            }
        });

        document.querySelectorAll<HTMLElement>('[data-editable-image="true"]').forEach(imageField => {
            this.registerField(imageField);
        });

        // The tooltip label depends on the active theme (Été / Hiver variant being edited).
        document.addEventListener(THEME_CHANGE_EVENT, () => {
            document.querySelectorAll<HTMLElement>('[data-editable-image="true"]').forEach(imageField => {
                const button = imageField.querySelector<HTMLButtonElement>('.editable-image__tooltip-btn');
                if (button) this.updateTooltipLabel(imageField, button);
            });
        });

        this.initUploadZone();
    }

    private initUploadZone(): void {
        const zone = document.getElementById('media-upload-zone');
        const input = document.getElementById('media-upload-input') as HTMLInputElement | null;
        if (!zone || !input) return;

        zone.addEventListener('click', () => input.click());

        zone.addEventListener('dragover', (e) => {
            e.preventDefault();
            zone.style.background = '#f0f0f0';
        });
        zone.addEventListener('dragleave', () => {
            zone.style.background = '';
        });
        zone.addEventListener('drop', (e) => {
            e.preventDefault();
            zone.style.background = '';
            const files = e.dataTransfer?.files;
            if (files && files.length > 0) this.uploadFiles(Array.from(files));
        });

        input.addEventListener('change', () => {
            const files = input.files;
            if (files && files.length > 0) this.uploadFiles(Array.from(files));
            input.value = '';
        });
    }

    /**
     * Uploads one or more files sequentially, then selects the last one uploaded
     * (the whole batch lands in the current folder and stays browsable afterwards).
     */
    private async uploadFiles(files: File[]): Promise<void> {
        const label = document.getElementById('media-upload-label');
        const originalLabel = label?.textContent ?? '';

        let lastUploaded: Media | null = null;
        const errors: string[] = [];

        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            if (!file) continue;
            if (label) label.textContent = `Import en cours... (${i + 1}/${files.length})`;

            const formData = new FormData();
            formData.append('file', file);
            if (this.currentFolderId) {
                formData.append('folderId', this.currentFolderId);
            }

            const response = await this.apiClient.postFormData<Media>('/api/admin/media', formData);

            if (!response.success || !response.data) {
                errors.push(`${file.name}: ${response.error || 'échec'}`);
                continue;
            }

            lastUploaded = response.data;
        }

        if (label) label.textContent = originalLabel;

        if (errors.length > 0) {
            alert("Échec de l'import pour: " + errors.join(', '));
        }

        if (lastUploaded) {
            await this.loadFolderTree();
            await this.loadMediaList();
            this.selectMedia(lastUploaded);
        }
    }

    /**
     * Binds the media-picker flow to an image field that was inserted into the DOM
     * after bootstrap (e.g. a dynamically added Actualité block's `[data-editable-image="true"]`
     * element). Reuses the exact same open/select flow as bootstrap-time fields — no second
     * modal or duplicated logic.
     *
     * The field itself is no longer clickable: a hover tooltip with an explicit
     * "add"/"edit" button is injected instead, so a stray click on the image doesn't
     * accidentally reopen the picker.
     */
    public registerField(imageField: HTMLElement): void {
        const button = this.ensureTooltipButton(imageField);

        button.addEventListener('click', async (e) => {
            e.stopPropagation();
            this.currentField = imageField;
            await this.open();
        });
    }

    private ensureTooltipButton(imageField: HTMLElement): HTMLButtonElement {
        let tooltip = imageField.querySelector<HTMLElement>('.editable-image__tooltip');
        let button: HTMLButtonElement;

        if (!tooltip) {
            tooltip = document.createElement('div');
            tooltip.className = 'editable-image__tooltip';

            button = document.createElement('button');
            button.type = 'button';
            button.className = 'editable-image__tooltip-btn';

            tooltip.appendChild(button);
            imageField.appendChild(tooltip);
        } else {
            button = tooltip.querySelector('.editable-image__tooltip-btn') as HTMLButtonElement;
        }

        if (this.isFramable(imageField) && !tooltip.querySelector('.editable-image__tooltip-frame')) {
            tooltip.appendChild(this.createFrameButton(imageField));
        }

        if (this.isThemable(imageField) && !tooltip.querySelector('.editable-image__tooltip-remove')) {
            tooltip.appendChild(this.createRemoveWinterButton(imageField));
        }

        this.updateTooltipLabel(imageField, button);

        return button;
    }

    private updateTooltipLabel(imageField: HTMLElement, button: HTMLButtonElement): void {
        const remove = imageField.querySelector<HTMLElement>('.editable-image__tooltip-remove');
        if (remove) remove.hidden = !imageField.dataset.mediaIdDark;

        const frame = imageField.querySelector<HTMLElement>('.editable-image__tooltip-frame');
        if (frame) {
            frame.hidden = !(imageField.dataset.mediaId || imageField.dataset.mediaIdDark);
            frame.textContent = ImageFrameEditor.label(imageField);
        }

        if (!this.isThemable(imageField)) {
            const isEmpty = imageField.classList.contains('is-empty-image');
            button.textContent = isEmpty ? "+ Ajouter l'image" : "✏️ Modifier l'image";
            return;
        }

        const isDark = this.isDarkTheme();
        const season = isDark ? 'Hiver' : 'Été';
        const hasOwn = isDark ? !!imageField.dataset.mediaIdDark : !!imageField.dataset.mediaId;
        const hasAny = !!(imageField.dataset.mediaId || imageField.dataset.mediaIdDark);

        if (hasOwn) {
            button.textContent = `✏️ Modifier l'image ${season}`;
        } else if (hasAny) {
            button.textContent = `+ Image ${season} (actuellement : image ${isDark ? 'Été' : 'Hiver'})`;
        } else {
            button.textContent = `+ Ajouter l'image ${season}`;
        }
    }

    /** A slot can be framed when its template carries `data-image-frame` (see ImageFrameExtension.php). */
    private isFramable(field: HTMLElement): boolean {
        return field.dataset.imageFrame !== undefined && field.dataset.standalone !== 'true';
    }

    private createFrameButton(field: HTMLElement): HTMLButtonElement {
        const button = document.createElement('button');
        button.type = 'button';
        button.className = 'editable-image__tooltip-btn editable-image__tooltip-frame';

        button.addEventListener('click', (e) => {
            e.stopPropagation();
            this.frameEditor.open(field);
        });

        return button;
    }

    /** A slot is themable when its template carries `data-media-id-dark` (standalone fields never do). */
    private isThemable(field: HTMLElement): boolean {
        return field.dataset.mediaIdDark !== undefined && field.dataset.standalone !== 'true';
    }

    private isDarkTheme(): boolean {
        return document.documentElement.dataset.theme === 'dark';
    }

    private createRemoveWinterButton(field: HTMLElement): HTMLButtonElement {
        const button = document.createElement('button');
        button.type = 'button';
        button.className = 'editable-image__tooltip-btn editable-image__tooltip-remove';
        button.textContent = "🗑 Retirer l'image Hiver";
        button.hidden = !field.dataset.mediaIdDark;

        button.addEventListener('click', (e) => {
            e.stopPropagation();
            field.dataset.mediaIdDark = '';
            if (field.dataset.imageFrameDark !== undefined) field.dataset.imageFrameDark = '';
            this.applyThemedSrc(field, null);
            applyFrame(field);
            const main = field.querySelector<HTMLButtonElement>('.editable-image__tooltip-btn:not(.editable-image__tooltip-remove)');
            if (main) this.updateTooltipLabel(field, main);
            this.onChangeCallback();
        });

        return button;
    }

    /**
     * Keeps the slot's <img> in sync with its Été / Hiver ids without a reload, mirroring
     * `components/_themed_img.html.twig`: a missing side falls back to the other one.
     * `newPath` is the just-picked image for the active theme's variant, or null when the
     * Hiver variant was removed.
     */
    private applyThemedSrc(field: HTMLElement, newPath: string | null): void {
        const img = field.querySelector<HTMLImageElement>('img');
        if (!img) return;

        const isDark = this.isDarkTheme();
        const hasLight = !!field.dataset.mediaId;
        const hasDark = !!field.dataset.mediaIdDark;
        const prevLight = img.dataset.srcLight ?? img.getAttribute('src') ?? '';
        const prevDark = img.dataset.srcDark ?? prevLight;

        let light = prevLight;
        let dark: string;
        if (newPath === null) {
            dark = light;
        } else if (isDark) {
            dark = newPath;
            light = hasLight ? prevLight : newPath;
        } else {
            light = newPath;
            dark = hasDark ? prevDark : newPath;
        }

        img.dataset.srcLight = light;
        img.dataset.srcDark = dark;
        img.src = isDark ? dark : light;
    }

    private async open(): Promise<void> {
        if (!this.modal) return;
        this.modal.style.display = 'block';
        document.body.style.overflow = 'hidden';
        this.currentFolderId = null;
        await this.loadFolderTree();
        await this.loadMediaList();
    }

    private close(): void {
        if (!this.modal) return;
        this.modal.style.display = 'none';
        document.body.style.overflow = '';
    }

    private async loadFolderTree(): Promise<void> {
        try {
            const response = await this.apiClient.get<MediaFolder[]>('/api/admin/media/folders');
            if (response.success && response.data) {
                this.folderTree = response.data;
                this.renderFolderBreadcrumb();
                this.renderFolderList();
            }
        } catch (error) {
            console.error('Error loading folder tree:', error);
        }
    }

    private renderFolderBreadcrumb(): void {
        const breadcrumb = document.getElementById('media-folder-breadcrumb');
        if (!breadcrumb) return;

        let html = '<a href="#" data-folder-id="">Racine</a>';

        if (this.currentFolderId) {
            const path = this.getFolderPath(this.currentFolderId);
            path.forEach(folder => {
                html += ` / <a href="#" data-folder-id="${folder.id}">${folder.name}</a>`;
            });
        }

        breadcrumb.innerHTML = html;

        breadcrumb.querySelectorAll('a').forEach(link => {
            link.addEventListener('click', async (e) => {
                e.preventDefault();
                const target = e.target as HTMLAnchorElement;
                this.currentFolderId = target.dataset.folderId || null;
                await this.loadMediaList();
                this.renderFolderBreadcrumb();
                this.renderFolderList();
            });
        });
    }

    private renderFolderList(): void {
        const folderList = document.getElementById('media-folder-list');
        if (!folderList) return;

        const currentFolders = this.currentFolderId
            ? this.findFolderById(this.currentFolderId)?.children || []
            : this.folderTree;

        if (currentFolders.length === 0) {
            folderList.innerHTML = '';
            return;
        }

        let html = '<div style="display: flex; gap: 10px; margin-bottom: 15px; flex-wrap: wrap;">';
        currentFolders.forEach((folder: MediaFolder) => {
            html += `
                <div data-folder-id="${folder.id}" style="padding: 10px 15px; background: #f0f0f0; border-radius: 4px; cursor: pointer; display: flex; align-items: center; gap: 5px;">
                    📁 ${folder.name} (${folder.mediaCount})
                </div>
            `;
        });
        html += '</div>';

        folderList.innerHTML = html;

        folderList.querySelectorAll('[data-folder-id]').forEach(folder => {
            folder.addEventListener('click', async () => {
                const folderId = (folder as HTMLElement).dataset.folderId;
                this.currentFolderId = folderId || null;
                await this.loadMediaList();
                this.renderFolderBreadcrumb();
                this.renderFolderList();
            });
        });
    }

    private getFolderPath(folderId: string): MediaFolder[] {
        const path: MediaFolder[] = [];
        let currentFolder = this.findFolderById(folderId);

        while (currentFolder) {
            path.unshift(currentFolder);
            currentFolder = currentFolder.parent ? this.findFolderById(currentFolder.parent) : null;
        }

        return path;
    }

    private findFolderById(folderId: string, folders: MediaFolder[] = this.folderTree): MediaFolder | null {
        for (const folder of folders) {
            if (folder.id === folderId) {
                return folder;
            }
            if (folder.children && folder.children.length > 0) {
                const found = this.findFolderById(folderId, folder.children);
                if (found) return found;
            }
        }
        return null;
    }

    private async loadMediaList(): Promise<void> {
        const mediaGrid = document.getElementById('media-grid');
        if (!mediaGrid) return;

        mediaGrid.innerHTML = '<p>Chargement...</p>';

        // Build URL with folder filter
        const url = this.currentFolderId
            ? `/api/admin/media?folder=${this.currentFolderId}`
            : '/api/admin/media';

        const response = await this.apiClient.get<MediaListResponse>(url);

        if (!response.success || !response.data || response.data.length === 0) {
            mediaGrid.innerHTML = '<p>Aucune image dans ce dossier.</p>';
            return;
        }

        mediaGrid.innerHTML = '';
        response.data.forEach(media => {
            const item = document.createElement('div');
            item.style.cssText = 'cursor: pointer; border: 2px solid transparent; border-radius: 8px; overflow: hidden; transition: border-color 0.2s;';
            item.innerHTML = `
                <img src="${media.path}" alt="${media.filename}" style="width: 100%; height: 150px; object-fit: cover;">
                <div style="padding: 10px; background: #f5f5f5; font-size: 12px; text-align: center;">
                    ${media.filename}
                </div>
            `;

            item.addEventListener('mouseenter', () => {
                item.style.borderColor = '#4CAF50';
            });
            item.addEventListener('mouseleave', () => {
                item.style.borderColor = 'transparent';
            });

            item.addEventListener('click', () => this.selectMedia(media));

            mediaGrid.appendChild(item);
        });
    }

    private selectMedia(media: Media): void {
        if (!this.currentField) return;
        const field = this.currentField;

        // Themable slots edit the variant of the active theme (Hiver in dark, Été otherwise).
        // A new picture resets that variant's cadrage: the old focal point was set for another photo.
        if (this.isThemable(field)) {
            if (this.isDarkTheme()) {
                field.dataset.mediaIdDark = media.id;
                if (field.dataset.imageFrameDark !== undefined) field.dataset.imageFrameDark = '';
            } else {
                field.dataset.mediaId = media.id;
                if (field.dataset.imageFrame !== undefined) field.dataset.imageFrame = '';
            }
            this.applyThemedSrc(field, media.path);
            if (this.isFramable(field)) applyFrame(field);
        } else {
            const img = field.querySelector('img');
            if (img) {
                img.src = media.path;
            }
            field.dataset.mediaId = media.id;
        }
        field.classList.remove('is-empty-image');

        const button = field.querySelector<HTMLButtonElement>('.editable-image__tooltip-btn');
        if (button) this.updateTooltipLabel(field, button);

        this.close();

        if (field.dataset.standalone === 'true') {
            this.saveStandaloneField(field, media.id);
        } else {
            this.onChangeCallback();
        }
    }

    /**
     * Persists a `data-standalone="true"` field immediately via its own endpoint, instead
     * of relying on the page-level "Save" button (ContentSerializer excludes these fields
     * from page content - see ContentSerializer.ts - since they belong to a different
     * entity, e.g. the burger menu's nav photo which lives on every page but saves to
     * MenuConfig).
     */
    private async saveStandaloneField(field: HTMLElement, mediaId: string): Promise<void> {
        const url = field.dataset.saveUrl;
        const key = field.dataset.saveKey;
        if (!url || !key) return;

        const response = await this.apiClient.post<{ url: string }>(url, { [key]: mediaId });

        if (!response.success) {
            alert(response.error || "Erreur lors de l'enregistrement de l'image");
        }
    }
}
