const fs = require('fs');
const exifr = require('exifr');
const piexif = require('piexifjs');

/**
 * Parses GPS coordinates from an image file using exifr
 * @param {string} filePath - Path to the image file
 * @returns {Promise<{lat: number, lng: number}|null>} GPS coordinates or null if not found
 */
const getGPSCoordinates = async (filePath) => {
    try {
        const gps = await exifr.gps(filePath);
        if (gps && typeof gps.latitude === 'number' && typeof gps.longitude === 'number') {
            return {
                lat: gps.latitude,
                lng: gps.longitude
            };
        }
        return null;
    } catch (error) {
        console.warn(`Could not read EXIF GPS from ${filePath}: ${error.message}`);
        return null;
    }
};

/**
 * Converts decimal degrees to Degrees, Minutes, Seconds rational format for piexifjs
 * @param {number} deg - Decimal degrees coordinate
 * @returns {Array} Array of rational numbers [[D, 1], [M, 1], [S * 100, 100]]
 */
const degToDmsRational = (deg) => {
    const absolute = Math.abs(deg);
    const d = Math.floor(absolute);
    const m = Math.floor((absolute - d) * 60);
    const s = Math.round((absolute - d - m / 60) * 3600 * 100);
    return [
        [d, 1],
        [m, 1],
        [s, 100]
    ];
};

/**
 * Injects GPS coordinates into a JPEG image file using piexifjs
 * @param {string} filePath - Path to the JPEG file
 * @param {number} lat - Latitude
 * @param {number} lng - Longitude
 */
const injectGPSCoordinates = async (filePath, lat, lng) => {
    try {
        const data = fs.readFileSync(filePath);
        
        // Kiểm tra marker SOI (Start of Image) trực tiếp trên Buffer
        if (data[0] !== 0xFF || data[1] !== 0xD8) {
            throw new Error("Tệp tin không phải là định dạng JPEG hợp lệ (thiếu SOI marker).");
        }

        const jpegBinary = data.toString('binary');
        let exifObj;
        try {
            exifObj = piexif.load(jpegBinary);
        } catch (e) {
            // Nếu không có EXIF hoặc lỗi khi nạp, khởi tạo đối tượng sạch
            exifObj = { "0th": {}, "Exif": {}, "GPS": {} };
        }

        // Đảm bảo các cấu trúc IFD tồn tại trước khi ghi đè
        exifObj["GPS"] = exifObj["GPS"] || {};

        const latRef = lat >= 0 ? 'N' : 'S';
        const lngRef = lng >= 0 ? 'E' : 'W';

        // Thêm Version ID (Bắt buộc để một số trình đọc nhận diện khối GPS)
        exifObj["GPS"][piexif.GPSIFD.GPSVersionID] = [2, 2, 0, 0];
        exifObj["GPS"][piexif.GPSIFD.GPSLatitudeRef] = latRef;
        exifObj["GPS"][piexif.GPSIFD.GPSLatitude] = degToDmsRational(lat);
        exifObj["GPS"][piexif.GPSIFD.GPSLongitudeRef] = lngRef;
        exifObj["GPS"][piexif.GPSIFD.GPSLongitude] = degToDmsRational(lng);

        // Chỉ đóng gói các IFD cần thiết để tránh lỗi 'pack' từ dữ liệu rác
        const exifBytes = piexif.dump({
            "0th": exifObj["0th"] || {},
            "Exif": exifObj["Exif"] || {},
            "GPS": exifObj["GPS"] || {}
        });
        
        const newJpegBinary = piexif.insert(exifBytes, jpegBinary);
        
        fs.writeFileSync(filePath, Buffer.from(newJpegBinary, 'binary'));
    } catch (error) {
        throw new Error(`Failed to inject EXIF GPS: ${error.message}`);
    }
};

module.exports = {
    getGPSCoordinates,
    injectGPSCoordinates
};
