/**
 * Assets — file upload/download, CRUD, cards, modal.
 */

import { _cachedAssets, assetsCache } from '../core/state.ts';
import { fetchWithAuth, escapeHtml, API_BASE, getHeaders } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { Modal } from '../core/modal.ts';
import { showToast, showConfirm } from '../core/ui.ts';
import { ICON_CLONE, ICON_EDIT, ICON_DOWNLOAD, ICON_ASSET, ICON_TRASH, getAssetTypeIcon } from '../core/icons.ts';
import * as P from '../core/icon-paths.ts';
import { wrapCard } from '../core/card-colors.ts';
import { TagInput, renderTagChips } from '../core/tag-input.ts';
import { loadPictureSources } from './streams.ts';
import type { Asset } from '../types.ts';

const _icon = (d: string) => `<svg class="icon" viewBox="0 0 24 24">${d}</svg>`;
const ICON_PLAY_SOUND = _icon(P.play);
const ICON_UPLOAD = _icon(P.fileUp);
const ICON_RESTORE = _icon(P.rotateCcw);

// ── Helpers ──

let _dropzoneInitialized = false;

/** Initialise the drop-zone wiring for the upload modal (once). */
function initUploadDropzone(): void {
    if (_dropzoneInitialized) return;
    _dropzoneInitialized = true;
    const dropzone = document.getElementById('asset-upload-dropzone')!;
    const fileInput = document.getElementById('asset-upload-file') as HTMLInputElement;
    const infoEl = document.getElementById('asset-upload-file-info')!;
    const nameEl = document.getElementById('asset-upload-file-name')!;
    const sizeEl = document.getElementById('asset-upload-file-size')!;
    const removeBtn = document.getElementById('asset-upload-file-remove')!;

    const showFile = (file: File) => {
        nameEl.textContent = file.name;
        sizeEl.textContent = formatFileSize(file.size);
        infoEl.style.display = '';
        dropzone.classList.add('has-file');
        // Hide the prompt text when a file is selected
        const labelEl = dropzone.querySelector('.file-dropzone-label') as HTMLElement | null;
        if (labelEl) labelEl.style.display = 'none';
        const iconEl = dropzone.querySelector('.file-dropzone-icon') as HTMLElement | null;
        if (iconEl) iconEl.style.display = 'none';
    };

    const clearFile = () => {
        fileInput.value = '';
        infoEl.style.display = 'none';
        dropzone.classList.remove('has-file');
        const labelEl = dropzone.querySelector('.file-dropzone-label') as HTMLElement | null;
        if (labelEl) labelEl.style.display = '';
        const iconEl = dropzone.querySelector('.file-dropzone-icon') as HTMLElement | null;
        if (iconEl) iconEl.style.display = '';
    };

    // Click → open file picker
    dropzone.addEventListener('click', (e) => {
        if ((e.target as HTMLElement).closest('.file-dropzone-remove')) return;
        fileInput.click();
    });

    // Keyboard: Enter/Space
    dropzone.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            fileInput.click();
        }
    });

    // File input change
    fileInput.addEventListener('change', () => {
        if (fileInput.files && fileInput.files.length > 0) {
            showFile(fileInput.files[0]);
        } else {
            clearFile();
        }
    });

    // Drag events
    let dragCounter = 0;
    dropzone.addEventListener('dragenter', (e) => {
        e.preventDefault();
        dragCounter++;
        dropzone.classList.add('dragover');
    });
    dropzone.addEventListener('dragleave', () => {
        dragCounter--;
        if (dragCounter <= 0) {
            dragCounter = 0;
            dropzone.classList.remove('dragover');
        }
    });
    dropzone.addEventListener('dragover', (e) => {
        e.preventDefault();
    });
    dropzone.addEventListener('drop', (e) => {
        e.preventDefault();
        dragCounter = 0;
        dropzone.classList.remove('dragover');
        const files = e.dataTransfer?.files;
        if (files && files.length > 0) {
            // Use DataTransfer to safely assign files cross-browser
            const dt = new DataTransfer();
            dt.items.add(files[0]);
            fileInput.files = dt.files;
            showFile(files[0]);
        }
    });

    // Remove button
    removeBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        clearFile();
    });
}

function formatFileSize(bytes: number): string {
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
    return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function getAssetTypeLabel(assetType: string): string {
    const map: Record<string, string> = {
        sound: t('asset.type.sound'),
        image: t('asset.type.image'),
        video: t('asset.type.video'),
        other: t('asset.type.other'),
    };
    return map[assetType] || assetType;
}

// ── Card builder ──

export function createAssetCard(asset: Asset): string {
    const icon = getAssetTypeIcon(asset.asset_type);
    const sizeStr = formatFileSize(asset.size_bytes);
    const prebuiltBadge = asset.prebuilt
        ? `<span class="stream-card-prop" title="${escapeHtml(t('asset.prebuilt'))}">${_icon(P.star)} ${t('asset.prebuilt')}</span>`
        : '';

    let playBtn = '';
    if (asset.asset_type === 'sound') {
        playBtn = `<button class="btn btn-icon btn-secondary" data-action="play" title="${escapeHtml(t('asset.play'))}">${ICON_PLAY_SOUND}</button>`;
    }

    return wrapCard({
        dataAttr: 'data-id',
        id: asset.id,
        removeOnclick: `deleteAsset('${asset.id}')`,
        removeTitle: t('common.delete'),
        content: `
            <div class="card-header">
                <div class="card-title" title="${escapeHtml(asset.name)}">
                    ${icon} <span class="card-title-text">${escapeHtml(asset.name)}</span>
                </div>
            </div>
            <div class="stream-card-props">
                <span class="stream-card-prop">${getAssetTypeIcon(asset.asset_type)} ${escapeHtml(getAssetTypeLabel(asset.asset_type))}</span>
                <span class="stream-card-prop">${_icon(P.fileText)} ${sizeStr}</span>
                ${prebuiltBadge}
            </div>
            ${renderTagChips(asset.tags)}`,
        actions: `
            ${playBtn}
            <button class="btn btn-icon btn-secondary" data-action="download" title="${escapeHtml(t('asset.download'))}">${ICON_DOWNLOAD}</button>
            <button class="btn btn-icon btn-secondary" data-action="edit" title="${escapeHtml(t('common.edit'))}">${ICON_EDIT}</button>`,
    });
}

// ── Sound playback ──

let _currentAudio: HTMLAudioElement | null = null;

async function _playAssetSound(assetId: string) {
    if (_currentAudio) {
        _currentAudio.pause();
        _currentAudio = null;
    }
    try {
        const res = await fetchWithAuth(`/assets/${assetId}/file`);
        const blob = await res.blob();
        const blobUrl = URL.createObjectURL(blob);
        const audio = new Audio(blobUrl);
        audio.addEventListener('ended', () => URL.revokeObjectURL(blobUrl));
        audio.play().catch(() => showToast(t('asset.error.play_failed'), 'error'));
        _currentAudio = audio;
    } catch {
        showToast(t('asset.error.play_failed'), 'error');
    }
}

// ── Modal ──

let _assetTagsInput: TagInput | null = null;
let _uploadTagsInput: TagInput | null = null;

class AssetEditorModal extends Modal {
    constructor() { super('asset-editor-modal'); }

    snapshotValues() {
        return {
            name: (document.getElementById('asset-editor-name') as HTMLInputElement).value,
            description: (document.getElementById('asset-editor-description') as HTMLInputElement).value,
            tags: JSON.stringify(_assetTagsInput ? _assetTagsInput.getValue() : []),
        };
    }

    onForceClose() {
        if (_assetTagsInput) { _assetTagsInput.destroy(); _assetTagsInput = null; }
    }
}
const assetEditorModal = new AssetEditorModal();

class AssetUploadModal extends Modal {
    constructor() { super('asset-upload-modal'); }
    snapshotValues() {
        return {
            name: (document.getElementById('asset-upload-name') as HTMLInputElement).value,
            file: (document.getElementById('asset-upload-file') as HTMLInputElement).value,
            tags: JSON.stringify(_uploadTagsInput ? _uploadTagsInput.getValue() : []),
        };
    }
    onForceClose() {
        if (_uploadTagsInput) { _uploadTagsInput.destroy(); _uploadTagsInput = null; }
    }
}
const assetUploadModal = new AssetUploadModal();

// ── CRUD: Upload ──

export async function showAssetUploadModal(): Promise<void> {
    const titleEl = document.getElementById('asset-upload-title')!;
    titleEl.innerHTML = `${ICON_UPLOAD} ${t('asset.upload')}`;

    (document.getElementById('asset-upload-name') as HTMLInputElement).value = '';
    (document.getElementById('asset-upload-description') as HTMLInputElement).value = '';
    (document.getElementById('asset-upload-file') as HTMLInputElement).value = '';
    document.getElementById('asset-upload-error')!.style.display = 'none';

    // Tags
    if (_uploadTagsInput) { _uploadTagsInput.destroy(); _uploadTagsInput = null; }
    const tagsContainer = document.getElementById('asset-upload-tags-container')!;
    _uploadTagsInput = new TagInput(tagsContainer, { entityType: 'asset' });

    // Reset dropzone visual state
    const dropzone = document.getElementById('asset-upload-dropzone')!;
    dropzone.classList.remove('has-file', 'dragover');
    const dzLabel = dropzone.querySelector('.file-dropzone-label') as HTMLElement | null;
    if (dzLabel) dzLabel.style.display = '';
    const dzIcon = dropzone.querySelector('.file-dropzone-icon') as HTMLElement | null;
    if (dzIcon) dzIcon.style.display = '';
    document.getElementById('asset-upload-file-info')!.style.display = 'none';

    initUploadDropzone();

    assetUploadModal.open();
    assetUploadModal.snapshot();
}

export async function uploadAsset(): Promise<void> {
    const fileInput = document.getElementById('asset-upload-file') as HTMLInputElement;
    const nameInput = document.getElementById('asset-upload-name') as HTMLInputElement;
    const descInput = document.getElementById('asset-upload-description') as HTMLInputElement;
    const errorEl = document.getElementById('asset-upload-error')!;

    if (!fileInput.files || fileInput.files.length === 0) {
        errorEl.textContent = t('asset.error.no_file');
        errorEl.style.display = '';
        return;
    }

    const file = fileInput.files[0];
    const formData = new FormData();
    formData.append('file', file);

    let url = `${API_BASE}/assets`;
    const params = new URLSearchParams();
    const name = nameInput.value.trim();
    if (name) params.set('name', name);
    const desc = descInput.value.trim();
    if (desc) params.set('description', desc);
    if (params.toString()) url += `?${params.toString()}`;

    try {
        // Use raw fetch for multipart — fetchWithAuth forces Content-Type: application/json
        // which breaks the browser's automatic multipart boundary generation
        const headers = getHeaders();
        delete headers['Content-Type'];
        const res = await fetch(url, { method: 'POST', headers, body: formData });
        if (res.status === 401) {
            throw new Error('Session expired');
        }
        if (!res.ok) {
            const err = await res.json();
            throw new Error(err.detail || 'Upload failed');
        }

        // Set tags via metadata update if any were specified
        const tags = _uploadTagsInput ? _uploadTagsInput.getValue() : [];
        const result = await res.json();
        if (tags.length > 0 && result.id) {
            await fetchWithAuth(`/assets/${result.id}`, {
                method: 'PUT',
                body: JSON.stringify({ tags }),
            });
        }

        showToast(t('asset.uploaded'), 'success');
        assetsCache.invalidate();
        assetUploadModal.forceClose();
        await loadPictureSources();
    } catch (e: any) {
        errorEl.textContent = e.message;
        errorEl.style.display = '';
    }
}

export function closeAssetUploadModal() {
    assetUploadModal.close();
}

// ── CRUD: Edit metadata ──

export async function showAssetEditor(editId: string): Promise<void> {
    const titleEl = document.getElementById('asset-editor-title')!;
    const idInput = document.getElementById('asset-editor-id') as HTMLInputElement;
    const nameInput = document.getElementById('asset-editor-name') as HTMLInputElement;
    const descInput = document.getElementById('asset-editor-description') as HTMLInputElement;
    const errorEl = document.getElementById('asset-editor-error')!;

    errorEl.style.display = 'none';
    const assets = await assetsCache.fetch();
    const asset = assets.find(a => a.id === editId);
    if (!asset) return;

    titleEl.innerHTML = `${ICON_ASSET} ${t('asset.edit')}`;
    idInput.value = asset.id;
    nameInput.value = asset.name;
    descInput.value = asset.description || '';

    // Tags
    if (_assetTagsInput) { _assetTagsInput.destroy(); _assetTagsInput = null; }
    const tagsContainer = document.getElementById('asset-editor-tags-container')!;
    _assetTagsInput = new TagInput(tagsContainer, { entityType: 'asset' });
    _assetTagsInput.setValue(asset.tags || []);

    assetEditorModal.open();
    assetEditorModal.snapshot();
}

export async function saveAssetMetadata(): Promise<void> {
    const id = (document.getElementById('asset-editor-id') as HTMLInputElement).value;
    const name = (document.getElementById('asset-editor-name') as HTMLInputElement).value.trim();
    const description = (document.getElementById('asset-editor-description') as HTMLInputElement).value.trim();
    const errorEl = document.getElementById('asset-editor-error')!;

    if (!name) {
        errorEl.textContent = t('asset.error.name_required');
        errorEl.style.display = '';
        return;
    }

    const tags = _assetTagsInput ? _assetTagsInput.getValue() : [];

    try {
        const res = await fetchWithAuth(`/assets/${id}`, {
            method: 'PUT',
            body: JSON.stringify({ name, description: description || null, tags }),
        });
        if (!res.ok) {
            const err = await res.json();
            throw new Error(err.detail);
        }

        showToast(t('asset.updated'), 'success');
        assetsCache.invalidate();
        assetEditorModal.forceClose();
        await loadPictureSources();
    } catch (e: any) {
        if (e.isAuth) return;
        errorEl.textContent = e.message;
        errorEl.style.display = '';
    }
}

export function closeAssetEditorModal() {
    assetEditorModal.close();
}

// ── CRUD: Delete ──

export async function deleteAsset(assetId: string): Promise<void> {
    const ok = await showConfirm(t('asset.confirm_delete'));
    if (!ok) return;

    try {
        await fetchWithAuth(`/assets/${assetId}`, { method: 'DELETE' });
        showToast(t('asset.deleted'), 'success');
        assetsCache.invalidate();
        await loadPictureSources();
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message || t('asset.error.delete_failed'), 'error');
    }
}

// ── Restore prebuilt ──

export async function restorePrebuiltAssets(): Promise<void> {
    try {
        const res = await fetchWithAuth('/assets/restore-prebuilt', { method: 'POST' });
        if (!res.ok) {
            const err = await res.json();
            throw new Error(err.detail);
        }
        const data = await res.json();
        if (data.restored_count > 0) {
            showToast(t('asset.prebuilt_restored').replace('{count}', String(data.restored_count)), 'success');
        } else {
            showToast(t('asset.prebuilt_none_to_restore'), 'info');
        }
        assetsCache.invalidate();
        await loadPictureSources();
    } catch (e: any) {
        if (e.isAuth) return;
        showToast(e.message, 'error');
    }
}

// ── Download ──

async function _downloadAsset(assetId: string) {
    const asset = _cachedAssets.find(a => a.id === assetId);
    const filename = asset ? asset.filename : 'download';
    try {
        const res = await fetchWithAuth(`/assets/${assetId}/file`);
        const blob = await res.blob();
        const blobUrl = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = blobUrl;
        a.download = filename;
        a.click();
        URL.revokeObjectURL(blobUrl);
    } catch {
        showToast(t('asset.error.download_failed'), 'error');
    }
}

// ── Event delegation ──

export function initAssetDelegation(container: HTMLElement): void {
    container.addEventListener('click', (e: Event) => {
        const btn = (e.target as HTMLElement).closest('[data-action]') as HTMLElement | null;
        if (!btn) return;
        const card = btn.closest('[data-id]') as HTMLElement | null;
        if (!card || !card.closest('#stream-tab-assets')) return;

        const action = btn.dataset.action;
        const id = card.getAttribute('data-id');
        if (!action || !id) return;

        e.stopPropagation();
        if (action === 'edit') showAssetEditor(id);
        else if (action === 'delete') deleteAsset(id);
        else if (action === 'download') _downloadAsset(id);
        else if (action === 'play') _playAssetSound(id);
    });
}

// ── Expose to global scope for HTML template onclick handlers ──

window.showAssetUploadModal = showAssetUploadModal;
window.closeAssetUploadModal = closeAssetUploadModal;
window.uploadAsset = uploadAsset;
window.showAssetEditor = showAssetEditor;
window.closeAssetEditorModal = closeAssetEditorModal;
window.saveAssetMetadata = saveAssetMetadata;
window.deleteAsset = deleteAsset;
window.restorePrebuiltAssets = restorePrebuiltAssets;
