//! Attachment uploader component
//!
//! Allows users to upload files and attach them to tasks.
//! Supports multiple file uploads with progress tracking.

use crate::types::TaskAttachmentDto;

/// Generate HTML for the attachment uploader component
///
/// # Arguments
///
/// * `task_id` - ID of the task to attach files to
/// * `attachments` - Existing attachments for the task
/// * `auth_token` - Authentication token for API calls (optional)
///
/// # Returns
///
/// HTML string for the attachment uploader component
pub fn attachment_uploader_html(
    task_id: &str,
    attachments: &[TaskAttachmentDto],
    auth_token: Option<&str>,
) -> String {
    let attachments_html = if attachments.is_empty() {
        r#"<p class="text-sm text-gray-500 dark:text-gray-400 italic">No attachments yet</p>"#
            .to_string()
    } else {
        attachments
            .iter()
            .map(|att| {
                format!(
                    r#"<div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded-md">
    <div class="flex items-center space-x-3">
        <span class="text-2xl">{}</span>
        <div>
            <p class="text-sm font-medium text-gray-900 dark:text-white">{}</p>
            <p class="text-xs text-gray-500 dark:text-gray-400">{} • {}</p>
        </div>
    </div>
    <div class="flex items-center space-x-2">
        <a
            href="/api/tasks/{}/attachments/{}/download"
            class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 text-sm"
            download>
            Download
        </a>
        <button
            class="text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300 delete-attachment-btn"
            data-attachment-id="{}"
            title="Delete attachment">
            ✕
        </button>
    </div>
</div>"#,
                    get_file_icon(&att.mime_type),
                    html_escape(&att.filename),
                    format_file_size(att.size),
                    format_datetime(&att.uploaded_at),
                    task_id,
                    att.id,
                    att.id
                )
            })
            .collect::<Vec<_>>()
            .join("\n")
    };

    let auth_header = auth_token
        .map(|token| format!(r#"headers.set('Authorization', 'Bearer {}');"#, token))
        .unwrap_or_default();

    format!(
        r#"<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
    <h3 class="text-lg font-bold text-gray-900 dark:text-white mb-4">Attachments</h3>

    <!-- Upload Area -->
    <div class="mb-4">
        <label class="block">
            <div class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-6 text-center hover:border-blue-500 dark:hover:border-blue-400 transition-colors cursor-pointer">
                <input
                    type="file"
                    id="attachment-input-{}"
                    class="hidden"
                    multiple
                    accept="*/*"
                />
                <div>
                    <svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true">
                        <path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
                    </svg>
                    <p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
                        Click to upload or drag and drop
                    </p>
                    <p class="text-xs text-gray-500 dark:text-gray-500">
                        Any file type, max 10MB
                    </p>
                </div>
            </div>
        </label>
    </div>

    <!-- Upload Progress -->
    <div id="upload-progress-{}" class="hidden mb-4">
        <div class="bg-gray-200 dark:bg-gray-700 rounded-full h-2">
            <div id="upload-progress-bar-{}" class="bg-blue-600 h-2 rounded-full" style="width: 0%"></div>
        </div>
        <p id="upload-status-{}" class="text-sm text-gray-600 dark:text-gray-400 mt-2">Uploading...</p>
    </div>

    <!-- Attachments List -->
    <div class="space-y-2" id="attachments-list-{}">
        {}
    </div>
</div>

<script>
    (function() {{
        const taskId = '{}';
        const fileInput = document.getElementById('attachment-input-' + taskId);
        const progressDiv = document.getElementById('upload-progress-' + taskId);
        const progressBar = document.getElementById('upload-progress-bar-' + taskId);
        const statusText = document.getElementById('upload-status-' + taskId);

        // Handle file selection
        fileInput?.addEventListener('change', async function(e) {{
            const files = Array.from(e.target.files || []);
            if (files.length === 0) return;

            // Validate file sizes
            const maxSize = 10 * 1024 * 1024; // 10MB
            for (const file of files) {{
                if (file.size > maxSize) {{
                    alert(`File "${{file.name}}" is too large. Maximum size is 10MB.`);
                    return;
                }}
            }}

            // Show progress
            progressDiv?.classList.remove('hidden');
            progressBar.style.width = '0%';
            statusText.textContent = 'Uploading...';

            // Upload files
            for (let i = 0; i < files.length; i++) {{
                const file = files[i];
                const formData = new FormData();
                formData.append('file', file);

                try {{
                    const headers = new Headers();
                    {}

                    const response = await fetch(`/api/tasks/${{taskId}}/attachments`, {{
                        method: 'POST',
                        headers: headers,
                        body: formData
                    }});

                    if (response.ok) {{
                        const progress = ((i + 1) / files.length) * 100;
                        progressBar.style.width = progress + '%';
                        statusText.textContent = `Uploaded ${{i + 1}} of ${{files.length}} files`;
                    }} else {{
                        const error = await response.json();
                        alert(`Failed to upload "${{file.name}}": ` + (error.error || 'Unknown error'));
                        break;
                    }}
                }} catch (err) {{
                    console.error('Upload error:', err);
                    alert(`Network error uploading "${{file.name}}". Please try again.`);
                    break;
                }}
            }}

            // Hide progress and reload
            setTimeout(() => {{
                progressDiv?.classList.add('hidden');
                window.location.reload();
            }}, 1000);
        }});

        // Handle delete
        document.querySelectorAll('.delete-attachment-btn').forEach(btn => {{
            btn.addEventListener('click', async function() {{
                if (!confirm('Delete this attachment?')) {{
                    return;
                }}

                const attachmentId = this.getAttribute('data-attachment-id');

                try {{
                    const headers = new Headers();
                    {}

                    const response = await fetch(`/api/tasks/${{taskId}}/attachments/${{attachmentId}}`, {{
                        method: 'DELETE',
                        headers: headers
                    }});

                    if (response.ok) {{
                        window.location.reload();
                    }} else {{
                        alert('Failed to delete attachment');
                    }}
                }} catch (err) {{
                    console.error('Delete error:', err);
                    alert('Network error. Please try again.');
                }}
            }});
        }});

        // Drag and drop support
        const dropzone = fileInput?.parentElement;
        if (dropzone) {{
            ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {{
                dropzone.addEventListener(eventName, preventDefaults, false);
            }});

            function preventDefaults(e) {{
                e.preventDefault();
                e.stopPropagation();
            }}

            ['dragenter', 'dragover'].forEach(eventName => {{
                dropzone.addEventListener(eventName, () => {{
                    dropzone.classList.add('border-blue-500', 'dark:border-blue-400');
                }});
            }});

            ['dragleave', 'drop'].forEach(eventName => {{
                dropzone.addEventListener(eventName, () => {{
                    dropzone.classList.remove('border-blue-500', 'dark:border-blue-400');
                }});
            }});

            dropzone.addEventListener('drop', (e) => {{
                const files = e.dataTransfer?.files;
                if (files && fileInput) {{
                    fileInput.files = files;
                    fileInput.dispatchEvent(new Event('change'));
                }}
            }});
        }}
    }})();
</script>"#,
        task_id,
        task_id,
        task_id,
        task_id,
        task_id,
        attachments_html,
        task_id,
        auth_header,
        auth_header
    )
}

/// Get file icon based on MIME type
fn get_file_icon(mime_type: &str) -> &'static str {
    if mime_type.starts_with("image/") {
        "🖼️"
    } else if mime_type.starts_with("video/") {
        "🎥"
    } else if mime_type.starts_with("audio/") {
        "🎵"
    } else if mime_type.contains("pdf") {
        "📄"
    } else if mime_type.contains("zip") || mime_type.contains("archive") {
        "📦"
    } else if mime_type.contains("text") {
        "📝"
    } else {
        "📎"
    }
}

/// Format file size for display
fn format_file_size(bytes: u64) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;

    if bytes >= GB {
        format!("{:.2} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.2} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.2} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} bytes", bytes)
    }
}

/// Escape HTML special characters
fn html_escape(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&#x27;")
}

/// Format datetime string for display
fn format_datetime(dt: &str) -> String {
    if let Some(date_part) = dt.split('T').next() {
        date_part.to_string()
    } else {
        dt.to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_file_icon() {
        assert_eq!(get_file_icon("image/png"), "🖼️");
        assert_eq!(get_file_icon("video/mp4"), "🎥");
        assert_eq!(get_file_icon("audio/mpeg"), "🎵");
        assert_eq!(get_file_icon("application/pdf"), "📄");
        assert_eq!(get_file_icon("application/zip"), "📦");
        assert_eq!(get_file_icon("text/plain"), "📝");
        assert_eq!(get_file_icon("application/octet-stream"), "📎");
    }

    #[test]
    fn test_format_file_size() {
        assert_eq!(format_file_size(500), "500 bytes");
        assert_eq!(format_file_size(1024), "1.00 KB");
        assert_eq!(format_file_size(1536), "1.50 KB");
        assert_eq!(format_file_size(1048576), "1.00 MB");
        assert_eq!(format_file_size(2097152), "2.00 MB");
        assert_eq!(format_file_size(1073741824), "1.00 GB");
    }

    #[test]
    fn test_html_escape() {
        assert_eq!(html_escape("test.txt"), "test.txt");
        assert_eq!(html_escape("<script>"), "&lt;script&gt;");
        assert_eq!(html_escape("A & B"), "A &amp; B");
    }

    #[test]
    fn test_attachment_uploader_html_empty() {
        let html = attachment_uploader_html("task-123", &[], None);
        assert!(html.contains("task-123"));
        assert!(html.contains("No attachments yet"));
        assert!(html.contains("Click to upload"));
    }

    #[test]
    fn test_attachment_uploader_html_with_attachments() {
        let attachments = vec![TaskAttachmentDto {
            id: "att-1".to_string(),
            task_id: "task-123".to_string(),
            filename: "document.pdf".to_string(),
            size: 1048576, // 1MB
            mime_type: "application/pdf".to_string(),
            uploaded_at: "2023-11-14T10:00:00Z".to_string(),
            uploaded_by: "user-1".to_string(),
        }];

        let html = attachment_uploader_html("task-123", &attachments, Some("test-token"));
        assert!(html.contains("document.pdf"));
        assert!(html.contains("1.00 MB"));
        assert!(html.contains("📄"));
        assert!(html.contains("Authorization"));
    }
}
