/**
 * Файловий кеш повнорозмірних зображень превʼю (main process).
 * Ключ — SHA1-хеш файла з /files/-URL + розширення; тека <profile>/cache/images.
 */

const fs        = require('fs');
const stream    = require('stream');
const urlParse  = require('url');
const parsePath = require('path');
const Profile   = require('../profile');

let cacheRoot = null;
let dirReady  = false;

function setCacheRoot(rootPath){ // тестовий хук; у застосунку тека береться з профілю
    cacheRoot = rootPath;
    dirReady  = false;
}

function getCacheDir(){
    if (!cacheRoot){
        cacheRoot = Profile.getProfilePath() + '/cache/images';
    }

    if (!dirReady){
        fs.mkdirSync(cacheRoot, { recursive: true });

        dirReady = true;
    }

    return cacheRoot;
}

function parseCacheKey(url){
    const pathname = urlParse.parse(url).pathname || "";
    const segments = pathname.split('/');

    let fileName = segments.pop() || "";
    const hash   = segments.pop() || "";

    try { fileName = decodeURI(fileName); } catch (e) { /* битий URI — лишаємо як є */ }

    const ext = parsePath.extname(fileName).toLowerCase();

    if (!/^[a-f0-9]{40}$/i.test(hash) || !ext){
        return null;
    }

    return hash + ext;
}

const inFlight = {}; // <hash>.<ext> → Promise активного download (дедуплікація паралельних кліків)

function cacheFileExists(localPath){
    // НЕ fs.existsSync: adm-zip (util/fattr.js) глобально підміняє його на fs.statSync,
    // який на відсутньому файлі кидає ENOENT замість повернути false.
    try {
        return fs.statSync(localPath).isFile();
    } catch (e) {
        return false;
    }
}

function getCachedPath(url){
    const key = parseCacheKey(url);

    if (!key){
        return null;
    }

    try {
        const localPath = getCacheDir() + '/' + key;

        return cacheFileExists(localPath) ? localPath : null;
    } catch (fsErr) {
        return null; // тека кешу недоступна (mkdirSync упав) — рахуємо як промах
    }
}

function downloadToCache(url, localPath){
    return new Promise(function (resolve, reject) {
        const isHttps = url.indexOf('https://') === 0;
        const http    = require(isHttps ? 'https' : 'http');
        const tmpPath = localPath + '.tmp';
        const options = urlParse.parse(url);

        let settled     = false; // guard: request.on('error') і pipeline-callback можуть спрацювати обидва
        let hasPipeline = false; // після старту pipeline cleanup робить тільки pipeline-callback (файл уже закритий)

        if (isHttps){
            options.rejectUnauthorized = false; // самопідписаний сертифікат сервера; тільки цей запит, без глобального env
        }

        function cleanupAndReject(streamErr){
            if (settled){
                return;
            }

            settled = true;

            fs.unlink(tmpPath, function () { // помилку unlink ігноруємо: .tmp може ще/вже не існувати
                reject(streamErr);
            });
        }

        const request = http.get(options, function (response) {
            if (response.statusCode !== 200){
                response.resume();
                cleanupAndReject(new Error('imageCache: HTTP ' + response.statusCode));

                return;
            }

            hasPipeline = true;

            const fileStream = fs.createWriteStream(tmpPath);

            // pipeline: помилка/обрив будь-якого стріму → destroy обох, файл закрито, потім callback.
            // Недокачаний response (premature close) — теж помилка: битий .tmp у кеш не потрапить,
            // unlink іде після закриття файла (безпечно на Windows).
            stream.pipeline(response, fileStream, function (streamErr) {
                if (streamErr){
                    cleanupAndReject(streamErr);

                    return;
                }

                fs.rename(tmpPath, localPath, function (renameErr) {
                    if (renameErr){
                        cleanupAndReject(renameErr);
                    } else {
                        settled = true;

                        resolve(localPath);
                    }
                });
            });
        });

        request.on('error', function (requestErr) {
            // Після отримання response pipeline сам закриє потоки й викличе cleanup після close.
            if (!hasPipeline){
                cleanupAndReject(requestErr);
            }
        });
    });
}

function ensureCached(url){
    const key = parseCacheKey(url);

    if (!key){
        return Promise.reject(new Error('imageCache: не /files/-URL, кеш не застосовний'));
    }

    let localPath = null;

    try {
        localPath = getCacheDir() + '/' + key;

        if (cacheFileExists(localPath)){
            return Promise.resolve(localPath);
        }
    } catch (fsErr) {
        return Promise.reject(fsErr); // mkdirSync упав (тека недоступна) — reject, а не синхронний виняток
    }

    if (inFlight[key]){
        return inFlight[key];
    }

    inFlight[key] = downloadToCache(url, localPath)
        .then(function (resultPath) {
            delete inFlight[key];

            return resultPath;
        })
        .catch(function (downloadErr) {
            delete inFlight[key];

            throw downloadErr;
        });

    return inFlight[key];
}

module.exports = {
    parseCacheKey: parseCacheKey,
    getCachedPath: getCachedPath,
    ensureCached : ensureCached,
    setCacheRoot : setCacheRoot
};
