/**
 * Atlas Image Resolver
 * Normalizes atlas image references and resolves them to public paths based on the
 * bundled image directory structure used by backend imports and tests.
 */
const fs = require('fs');
const path = require('path');

const PUBLIC_IMAGES_PREFIX = '/public/images';
const SPECIAL_SUBDIRECTORIES = ['paintings', 'chybejici'];

function toPosixPath(value) {
    return value.replace(/\\/g, '/');
}

function normalizeAtlasImageReference(reference) {
    if (typeof reference !== 'string') {
        return '';
    }

    let normalized = reference.trim();

    if (!normalized) {
        return '';
    }

    normalized = normalized.replace(/^https?:\/\/[^/]+/i, '');
    normalized = toPosixPath(normalized);

    const publicPrefixPattern = /^\/?public\/images\//i;
    const hadPublicPrefix = publicPrefixPattern.test(normalized);
    normalized = normalized.replace(publicPrefixPattern, '');
    normalized = normalized.replace(/^\/+/, '');

    const segments = normalized.split('/').filter(Boolean);
    if (hadPublicPrefix && segments.length > 1) {
        normalized = segments.slice(1).join('/');
    } else {
        normalized = segments[0] || '';
        if (segments.length > 1) {
            normalized = segments.join('/');
        }
    }

    for (const directory of SPECIAL_SUBDIRECTORIES) {
        const jammedPrefix = new RegExp(`^${directory}(?=[^/])`, 'i');
        if (jammedPrefix.test(normalized)) {
            normalized = normalized.replace(jammedPrefix, `${directory}/`);
        }
    }

    return normalized.replace(/^\/+/, '');
}

function collectImageCandidates(imagesDir, currentDir = imagesDir) {
    const directoryEntries = fs.readdirSync(currentDir, { withFileTypes: true });
    const files = [];

    for (const entry of directoryEntries) {
        const absolutePath = path.join(currentDir, entry.name);

        if (entry.isDirectory()) {
            files.push(...collectImageCandidates(imagesDir, absolutePath));
            continue;
        }

        const relativePath = toPosixPath(path.relative(imagesDir, absolutePath));
        const publicPath = `${PUBLIC_IMAGES_PREFIX}/${relativePath}`;
        const atlasReference = relativePath.split('/').slice(1).join('/') || path.posix.basename(relativePath);

        files.push({
            absolutePath,
            relativePath,
            relativePathLower: relativePath.toLowerCase(),
            publicPath,
            publicPathLower: publicPath.toLowerCase(),
            atlasReference,
            atlasReferenceLower: atlasReference.toLowerCase(),
            basenameLower: path.posix.basename(relativePath).toLowerCase(),
        });
    }

    return files.sort((left, right) => {
        const leftIsSpecial = SPECIAL_SUBDIRECTORIES.some(directory => left.relativePathLower.includes(`/${directory}/`));
        const rightIsSpecial = SPECIAL_SUBDIRECTORIES.some(directory => right.relativePathLower.includes(`/${directory}/`));

        if (leftIsSpecial !== rightIsSpecial) {
            return leftIsSpecial ? 1 : -1;
        }

        return left.relativePathLower.localeCompare(right.relativePathLower);
    });
}

function createAtlasImageResolver(imagesDir) {
    const candidates = collectImageCandidates(imagesDir);
    const byBasename = new Map();

    for (const candidate of candidates) {
        if (!byBasename.has(candidate.basenameLower)) {
            byBasename.set(candidate.basenameLower, []);
        }

        byBasename.get(candidate.basenameLower).push(candidate);
    }

    function resolveAtlasImage(reference) {
        if (!reference) {
            return reference;
        }

        if (/^(https?:|blob:|data:)/i.test(reference)) {
            return reference;
        }

        const normalizedReference = normalizeAtlasImageReference(reference);
        if (!normalizedReference) {
            return reference;
        }

        const publicReference = `${PUBLIC_IMAGES_PREFIX}/${normalizedReference}`.toLowerCase();
        const directPublicMatch = candidates.find(candidate => candidate.publicPathLower === publicReference);
        if (directPublicMatch) {
            return directPublicMatch.publicPath;
        }

        const directReferenceMatch = candidates.find(candidate => candidate.atlasReferenceLower === normalizedReference.toLowerCase());
        if (directReferenceMatch) {
            return directReferenceMatch.publicPath;
        }

        if (normalizedReference.includes('/')) {
            const normalizedSlashReference = normalizedReference.toLowerCase();
            const suffix = `/${normalizedSlashReference}`;
            const suffixMatches = candidates.filter(candidate =>
                candidate.relativePathLower === normalizedSlashReference || candidate.relativePathLower.endsWith(suffix)
            );

            if (suffixMatches.length === 1) {
                return suffixMatches[0].publicPath;
            }
        }

        const basenameMatches = byBasename.get(path.posix.basename(normalizedReference).toLowerCase()) || [];
        if (basenameMatches.length >= 1) {
            return basenameMatches[0].publicPath;
        }

        throw new Error(`Atlas image reference could not be resolved: ${reference}`);
    }

    resolveAtlasImage.toAtlasReference = (reference) => {
        if (!reference) {
            return reference;
        }

        const normalizedReference = normalizeAtlasImageReference(reference);
        if (!normalizedReference) {
            return reference;
        }

        const resolvedPublicPath = resolveAtlasImage(reference);
        const resolvedCandidate = candidates.find(candidate => candidate.publicPath === resolvedPublicPath);
        return resolvedCandidate ? resolvedCandidate.atlasReference : normalizedReference;
    };

    return resolveAtlasImage;
}

module.exports = {
    PUBLIC_IMAGES_PREFIX,
    SPECIAL_SUBDIRECTORIES,
    collectImageCandidates,
    createAtlasImageResolver,
    normalizeAtlasImageReference,
};