// #region StorageService [C:3] [TYPE Service] [SEMANTICS storage, service, api, file, upload]
// @BRIEF Frontend API client for file storage management including list, upload, download, and delete operations.
// @LAYER Service
// @RELATION DEPENDS_ON -> [ApiModule]

// #region storageService:Module [TYPE Function]
// @PURPOSE: Default purpose
// @RELATION USES -> [EXT:frontend:App]
/**
 * @purpose Frontend API client for file storage management.
 * @layer   Service
 * @relation DEPENDS_ON -> [EXT:path:backend.api.storage]
 * @SEMANTICS: storage, api, client
 */

interface StoredFile {
  name: string;
  path: string;
  size?: number;
  type?: string;
  [key: string]: unknown;
}

interface UploadResponse {
  name?: string;
  path?: string;
  [key: string]: unknown;
}

interface ErrorDetail {
  detail?: string;
  [key: string]: unknown;
}

const API_BASE = '/api/storage';

// #region getStorageAuthHeaders:Function [TYPE Function]
/**
 * @purpose Returns headers with Authorization for storage API calls.
 * @returns Headers object with Authorization if token exists.
 * @NOTE    Unlike api.js getAuthHeaders, this doesn't set Content-Type
 *          to allow FormData to set its own multipart boundary.
 */
function getStorageAuthHeaders(): Record<string, string> {
    const headers: Record<string, string> = {};
    if (typeof window !== 'undefined') {
        const token = localStorage.getItem('auth_token');
        if (token) {
            headers['Authorization'] = `Bearer ${token}`;
        }
    }
    return headers;
}
// #endregion getStorageAuthHeaders:Function

// #region encodeStoragePath:Function [TYPE Function]
/**
 * @purpose Encodes a storage-relative path preserving slash separators.
 * @param   path - Relative storage path.
 * @returns Encoded path safe for URL segments.
 */
function encodeStoragePath(path: string): string {
    return String(path || '')
        .split('/')
        .filter((part) => part.length > 0)
        .map((part) => encodeURIComponent(part))
        .join('/');
}
// #endregion encodeStoragePath:Function

// #region listFiles:Function [TYPE Function]
/**
 * @purpose Fetches the list of files for a given category and subpath.
 * @param   category - Optional category filter.
 * @param   path - Optional subpath filter.
 * @returns Promise resolving to an array of StoredFile objects.
 * @PRE     category and path should be valid strings if provided.
 * @POST    Returns a promise resolving to an array of StoredFile objects.
 */
export async function listFiles(category?: string, path?: string): Promise<StoredFile[]> {
    const params = new URLSearchParams();
    if (category) {
        params.append('category', category);
    }
    if (path) {
        params.append('path', path);
    }
    const response = await fetch(`${API_BASE}/files?${params.toString()}`, {
        headers: getStorageAuthHeaders()
    });
    if (!response.ok) {
        throw new Error(`Failed to fetch files: ${response.statusText}`);
    }
    return await response.json();
}
// #endregion listFiles:Function

// #region uploadFile:Function [TYPE Function]
/**
 * @purpose Uploads a file to the storage system.
 * @param   file - The file to upload.
 * @param   category - Target category.
 * @param   path - Target subpath.
 * @returns Promise resolving to the metadata of the uploaded file.
 * @PRE     file must be a valid File object; category must be specified.
 * @POST    Returns a promise resolving to the metadata of the uploaded file.
 */
export async function uploadFile(file: File, category: string, path?: string): Promise<UploadResponse> {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('category', category);
    if (path) {
        formData.append('path', path);
    }

    const response = await fetch(`${API_BASE}/upload`, {
        method: 'POST',
        headers: getStorageAuthHeaders(),
        body: formData
    });

    if (!response.ok) {
        const errorData: ErrorDetail = await response.json().catch(() => ({}));
        throw new Error(errorData.detail || `Failed to upload file: ${response.statusText}`);
    }
    return await response.json();
}
// #endregion uploadFile:Function

// #region deleteFile:Function [TYPE Function]
/**
 * @purpose Deletes a file or directory from storage.
 * @param   category - File category.
 * @param   path - Relative path of the item.
 * @returns Promise that resolves when deletion is complete.
 * @PRE     category and path must identify an existing file or directory.
 * @POST    The specified file or directory is removed from storage.
 */
export async function deleteFile(category: string, path: string): Promise<void> {
    const response = await fetch(`${API_BASE}/files/${category}/${path}`, {
        method: 'DELETE',
        headers: getStorageAuthHeaders()
    });

    if (!response.ok) {
        const errorData: ErrorDetail = await response.json().catch(() => ({}));
        throw new Error(errorData.detail || `Failed to delete: ${response.statusText}`);
    }
}
// #endregion deleteFile:Function

// #region downloadFileUrl:Function [TYPE Function]
/**
 * @purpose Returns the URL for downloading a file.
 * @param   category - File category.
 * @param   path - Relative path of the file.
 * @returns A valid API URL for file download.
 * @PRE     category and path must identify an existing file.
 * @POST    Returns a valid API URL for file download.
 * @NOTE    Downloads use browser navigation, so auth is handled via cookies
 *          or the backend must allow unauthenticated downloads for valid paths.
 */
export function downloadFileUrl(category: string, path: string): string {
    const safeCategory = encodeURIComponent(String(category || ''));
    const safePath = encodeStoragePath(path);
    return `${API_BASE}/download/${safeCategory}/${safePath}`;
}
// #endregion downloadFileUrl:Function

// #region downloadFile:Function [TYPE Function]
/**
 * @purpose Downloads a file using authenticated fetch and saves it in browser.
 * @param   category - File category.
 * @param   path - Relative path of the file.
 * @param   filename - Optional preferred filename.
 * @returns Promise that resolves when download is triggered.
 * @PRE     category/path identify an existing file and user has READ permission.
 * @POST    Browser download is triggered or an Error is thrown.
 */
export async function downloadFile(category: string, path: string, filename?: string): Promise<void> {
    const response = await fetch(downloadFileUrl(category, path), {
        headers: getStorageAuthHeaders(),
    });

    if (!response.ok) {
        const errorData: ErrorDetail = await response.json().catch(() => ({}));
        throw new Error(errorData.detail || `Failed to download file: ${response.statusText}`);
    }

    const blob = await response.blob();
    const objectUrl = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = objectUrl;
    link.download = filename || String(path || '').split('/').pop() || 'download';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    window.URL.revokeObjectURL(objectUrl);
}
// #endregion downloadFile:Function

export default {
    listFiles,
    uploadFile,
    deleteFile,
    downloadFileUrl,
    downloadFile
};

// #endregion storageService:Module
// #endregion StorageService
