import { Languages } from '@/global/mimetype';

const icons = new Map([
    ['.zip', 'p-file-zip'],
    ['.gz', 'p-file-zip'],
    ['.tar.bz2', 'p-file-zip'],
    ['.bz2', 'p-file-zip'],
    ['.xz', 'p-file-zip'],
    ['.tar.xz', 'p-file-zip'],
    ['.tar', 'p-file-zip'],
    ['.tar.gz', 'p-file-zip'],
    ['.war', 'p-file-zip'],
    ['.tgz', 'p-file-zip'],
    ['.7z', 'p-file-zip'],
    ['.rar', 'p-file-zip'],
    ['.mp3', 'p-file-mp3'],
    ['.svg', 'p-file-svg'],
    ['.txt', 'p-file-txt'],
    ['.html', 'p-file-html'],
    ['.word', 'p-file-word'],
    ['.ppt', 'p-file-ppt'],
    ['.jpg', 'p-file-jpg'],
    ['.jpeg', 'p-file-jpg'],
    ['.png', 'p-file-png'],
    ['.xlsx', 'p-file-excel'],
    ['.doc', 'p-file-word'],
    ['.xls', 'p-file-excel'],
    ['.docx', 'p-file-word'],
    ['.pdf', 'p-file-pdf'],
    ['.bmp', 'p-file-png'],
    ['.gif', 'p-file-png'],
    ['.tiff', 'p-file-png'],
    ['.ico', 'p-file-png'],
    ['.webp', 'p-file-png'],
    ['.mp4', 'p-file-video'],
    ['.webm', 'p-file-video'],
    ['.mov', 'p-file-video'],
    ['.wmv', 'p-file-video'],
    ['.mkv', 'p-file-video'],
    ['.avi', 'p-file-video'],
    ['.wma', 'p-file-video'],
    ['.flv', 'p-file-video'],
    ['.wav', 'p-file-mp3'],
    ['.wma', 'p-file-mp3'],
    ['.ape', 'p-file-mp3'],
    ['.acc', 'p-file-mp3'],
    ['.ogg', 'p-file-mp3'],
    ['.flac', 'p-file-mp3'],
]);

const fileTypes = {
    image: ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.ico', '.svg', '.webp'],
    compress: ['.zip', '.rar', '.gz', '.war', '.tgz', '.7z', '.tar.gz', '.tar', '.bz2', '.xz', '.tar.bz2', '.tar.xz'],
    video: ['.mp4', '.webm', '.mov', '.wmv', '.mkv', '.avi', '.wma', '.flv'],
    audio: ['.mp3', '.wav', '.wma', '.ape', '.acc', '.ogg', '.flac'],
    pdf: ['.pdf'],
    word: ['.doc', '.docx'],
    excel: ['.xls', '.xlsx'],
    text: ['.iso', '.tiff', '.exe', '.so', '.bz', '.dmg', '.apk', '.pptx', '.ppt', '.xlsb'],
};

export function getIcon(extension: string): string {
    if (icons.get(extension) != undefined) {
        return String(icons.get(extension));
    }
    return 'p-file-normal';
}

export const getFileType = (extension: string) => {
    let type = 'text';
    Object.entries(fileTypes).forEach(([key, extensions]) => {
        if (extensions.includes(extension.toLowerCase())) {
            type = key;
        }
    });
    return type;
};

const convertTypes = ['image', 'video', 'audio'] as const;
type ConvertType = (typeof convertTypes)[number];

export function isConvertible(extension: string, mimeType: string): boolean {
    return convertTypes.includes(getFileType(extension) as ConvertType) && /^(image|audio|video)\//.test(mimeType);
}

export const isSensitiveLinuxPath = (path: string) => {
    const sensitivePath = [
        '/',
        '/bin',
        '/sbin',
        '/usr/bin',
        '/usr/sbin',
        '/usr/local/bin',
        '/etc',
        '/lib',
        '/lib64',
        '/usr/lib',
        '/home',
        '/tmp',
        '/var',
        '/dev',
        '/proc',
        '/sys',
    ];
    return sensitivePath.indexOf(path) !== -1;
};

function buildFileDownloadUrl(filePath: string, currentNode: string): string {
    const base = `${import.meta.env.VITE_API_URL as string}/files/download?operateNode=${currentNode}&`;
    return base + 'path=' + encodeURIComponent(filePath);
}

export function buildFileSharePageUrl(code: string, currentNode: string): string {
    const shareUrl = new URL(`/s/${encodeURIComponent(code)}`, window.location.origin);
    shareUrl.searchParams.set('operateNode', currentNode);
    return shareUrl.toString();
}

export function buildFileShareDownloadUrl(code: string, currentNode: string, password?: string): string {
    const apiBase = import.meta.env.VITE_API_URL as string;
    const normalizedBase = apiBase.replace(/\/$/, '');
    const shareUrl = /^https?:\/\//i.test(normalizedBase)
        ? new URL(`${normalizedBase}/files/share/download`)
        : new URL(`${normalizedBase}/files/share/download`, window.location.origin);
    shareUrl.searchParams.set('operateNode', currentNode);
    shareUrl.searchParams.set('code', code);
    if (password && password.length > 0) {
        shareUrl.searchParams.set('password', password);
    }
    return shareUrl.toString();
}

export function buildFileShareQrCodeUrl(code: string, currentNode: string): string {
    const apiBase = import.meta.env.VITE_API_URL as string;
    const normalizedBase = apiBase.replace(/\/$/, '');
    const shareUrl = /^https?:\/\//i.test(normalizedBase)
        ? new URL(`${normalizedBase}/files/share/qrcode`)
        : new URL(`${normalizedBase}/files/share/qrcode`, window.location.origin);
    shareUrl.searchParams.set('operateNode', currentNode);
    shareUrl.searchParams.set('code', code);
    return shareUrl.toString();
}

export function downloadFile(filePath: string, currentNode: string) {
    window.open(buildFileDownloadUrl(filePath, currentNode), '_blank');
}

export function downloadWithContent(content: string, fileName: string) {
    const downloadUrl = window.URL.createObjectURL(new Blob([content]));
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = downloadUrl;
    a.download = fileName;
    const event = new MouseEvent('click');
    a.dispatchEvent(event);
}

const editorLanguages = new Map(
    Languages.flatMap((language) => language.value.map((ext) => [ext.toLowerCase(), language.label] as const)),
);

const specialEditorFileNames = ['dockerfile'];

const normalizeEditorExtension = (value: string) => {
    const trimmed = value.trim().toLowerCase();
    if (!trimmed) {
        return '';
    }
    return trimmed.startsWith('.') ? trimmed.slice(1) : trimmed;
};

const isSpecialEditorFileName = (value: string) => {
    const normalized = value.trim().toLowerCase();
    if (!normalized) {
        return false;
    }
    return specialEditorFileNames.some((filename) => normalized === filename);
};

export const resolveEditorLanguage = (path: string, extension = '', name = '') => {
    if (isSpecialEditorFileName(name)) {
        return 'dockerfile';
    }

    const candidates = [extension, path.split('/').pop() || '']
        .map((value) => normalizeEditorExtension(value))
        .filter(Boolean);

    for (const ext of candidates) {
        const language = editorLanguages.get(ext);
        if (language) {
            return language;
        }
    }

    const fileName = path.split('/').pop() || '';
    if (isSpecialEditorFileName(fileName)) {
        return 'dockerfile';
    }

    return 'yaml';
};

const normalizeUrlFilename = (value: string) => {
    let decoded = value.trim().replace(/^['"]|['"]$/g, '');
    try {
        decoded = decodeURIComponent(decoded);
    } catch {
        // Keep the original value when it contains an incomplete escape sequence.
    }
    return (
        decoded
            .replace(/[\u0000-\u001f\u007f]/g, '')
            .split(/[\\/]/)
            .pop()
            ?.trim() || ''
    );
};

const getFilenameFromContentDisposition = (contentDisposition: string) => {
    const extendedMatch = contentDisposition.match(/(?:^|;)\s*filename\*\s*=\s*(?:"([^"]*)"|([^;]*))/i);
    const extendedValue = extendedMatch?.[1] || extendedMatch?.[2]?.trim();
    if (extendedValue) {
        const encodedValue = extendedValue.match(/^[^']*'[^']*'(.*)$/)?.[1] || extendedValue;
        return normalizeUrlFilename(encodedValue);
    }

    const filenameMatch = contentDisposition.match(/(?:^|;)\s*filename\s*=\s*(?:"([^"]*)"|([^;]*))/i);
    return normalizeUrlFilename(filenameMatch?.[1] || filenameMatch?.[2] || '');
};

export const getFilenameFromUrl = (value: string) => {
    const normalizedValue = value.trim();
    try {
        const url = new URL(normalizedValue);
        const dispositionKeys = ['response-content-disposition', 'rscd', 'content-disposition'];
        for (const key of dispositionKeys) {
            const disposition = Array.from(url.searchParams.entries()).find(
                ([paramKey]) => paramKey.toLowerCase() === key,
            )?.[1];
            if (disposition) {
                const filename = getFilenameFromContentDisposition(disposition);
                if (filename) {
                    return filename;
                }
            }
        }
        return normalizeUrlFilename(url.pathname.slice(url.pathname.lastIndexOf('/') + 1));
    } catch {
        const urlWithoutParams = normalizedValue.replace(/[?#].*$/, '');
        return normalizeUrlFilename(urlWithoutParams.slice(urlWithoutParams.lastIndexOf('/') + 1));
    }
};

const FILE_SHARE_PASSWORD_KEY = 'k';

export const withFileSharePassword = (value: string, password: string) => {
    const shareUrl = new URL(value);
    const hashParams = new URLSearchParams(shareUrl.hash.slice(1));
    const normalizedPassword = password.trim();
    if (normalizedPassword) {
        hashParams.set(FILE_SHARE_PASSWORD_KEY, normalizedPassword);
    } else {
        hashParams.delete(FILE_SHARE_PASSWORD_KEY);
    }
    shareUrl.hash = hashParams.toString();
    return shareUrl.toString();
};

export const getFileSharePasswordFromHash = (hash: string) => {
    const hashParams = new URLSearchParams(hash.replace(/^#/, ''));
    return hashParams.get(FILE_SHARE_PASSWORD_KEY)?.trim() || '';
};

export const removeFileSharePasswordFromHash = (hash: string) => {
    const hashParams = new URLSearchParams(hash.replace(/^#/, ''));
    hashParams.delete(FILE_SHARE_PASSWORD_KEY);
    const remainingHash = hashParams.toString();
    return remainingHash ? `#${remainingHash}` : '';
};
