/**
 * Product Encoding/Decoding Helper
 * 
 * Provides functions to encode and decode product configurations into compact
 * hex-based URL strings. Uses bit-packing for efficient, stable encoding.
 * 
 * Format: v-T-C-M-HHHH-HHHH-HHHH
 * See /planning/ENCODING_SYSTEM.md for detailed documentation
 */

// ============================================================================
// LOOKUP TABLES
// ============================================================================

const ENCODING_VERSION = 0;

// Main Product Attributes (single hex character, 0-15)
const PRODUCT_TYPE = {
    'PATIO_DOOR': 1,
    'STORM_DOOR': 3,
    'STORM_WINDOW': 5,
    'PRIMARY_WINDOW': 7
};

const PRODUCT_TYPE_REVERSE = {
    1: 'PATIO_DOOR',
    3: 'STORM_DOOR',
    5: 'STORM_WINDOW',
    7: 'PRIMARY_WINDOW'
};

const PRODUCT_COLOR = {
    'BLACK': 1,
    'BRONZE': 2,
    'SANDSTONE': 3,
    'WHITE': 4,
    'TAN': 5,
    'MILL': 6
};

const PRODUCT_COLOR_REVERSE = {
    1: 'BLACK',
    2: 'BRONZE',
    3: 'SANDSTONE',
    4: 'WHITE',
    5: 'TAN',
    6: 'MILL'
};

const PRODUCT_MATERIAL = {
    'ALUMINUM': 1,
    'VINYL': 2
};

const PRODUCT_MATERIAL_REVERSE = {
    1: 'ALUMINUM',
    2: 'VINYL'
};

// Hardware Fields (5 bits each, 0-31)
const HARDWARE_TYPE = {
    'NONE': 0,
    'LEVER': 1,
    'PULL': 2,
    'PULL_HANDLE': 3,
    'DEADBOLT': 4,
    'HINGE': 5
};

const HARDWARE_TYPE_REVERSE = {
    0: 'NONE',
    1: 'LEVER',
    2: 'PULL',
    3: 'PULL_HANDLE',
    4: 'DEADBOLT',
    5: 'HINGE'
};

const HARDWARE_STYLE = {
    'NONE': 0,
    'STANDARD': 1,
    'PUSH': 2,
    'ALTERNATIVE': 3,
    'CONTEMPORARY': 4,
    'TRADITIONAL': 5
};

const HARDWARE_STYLE_REVERSE = {
    0: 'NONE',
    1: 'STANDARD',
    2: 'PUSH',
    3: 'ALTERNATIVE',
    4: 'CONTEMPORARY',
    5: 'TRADITIONAL'
};

const HARDWARE_COLOR = {
    'NONE': 0,
    'BRASS': 1,
    'WHITE': 2,
    'BLACK': 3,
    'SATIN': 4,
    'NICKEL': 5,
    'BRONZE': 6
};

const HARDWARE_COLOR_REVERSE = {
    0: 'NONE',
    1: 'BRASS',
    2: 'WHITE',
    3: 'BLACK',
    4: 'SATIN',
    5: 'NICKEL',
    6: 'BRONZE'
};

// ============================================================================
// ENCODING FUNCTIONS
// ============================================================================

/**
 * Encode a hardware configuration into a 4-character hex string
 * Uses 5 bits per field (32 values each)
 * 
 * @param {number} type - Hardware type (0-31)
 * @param {number} style - Hardware style (0-31)
 * @param {number} color - Hardware color (0-31)
 * @returns {string} 4-character hex string (e.g., "0421")
 */
function encodeHardware(type, style, color) {
    // Validate inputs
    if (type < 0 || type > 31) throw new Error(`Invalid hardware type: ${type} (must be 0-31)`);
    if (style < 0 || style > 31) throw new Error(`Invalid hardware style: ${style} (must be 0-31)`);
    if (color < 0 || color > 31) throw new Error(`Invalid hardware color: ${color} (must be 0-31)`);
    
    // Pack into 16 bits: [reserved(1)][type(5)][style(5)][color(5)]
    const value = ((type & 0x1F) << 10) |    // Bits 10-14
                  ((style & 0x1F) << 5) |     // Bits 5-9
                  (color & 0x1F);             // Bits 0-4
    
    return value.toString(16).toUpperCase().padStart(4, '0');
}

/**
 * Encode a complete product configuration into a URL string
 * 
 * @param {Object} config - Product configuration
 * @param {number} config.type - Product type (0-15)
 * @param {number} config.color - Product color (0-15)
 * @param {number} config.material - Product material (0-15)
 * @param {Array<Object>} config.hardware - Array of hardware items (optional)
 * @param {number} config.hardware[].type - Hardware type (0-31)
 * @param {number} config.hardware[].style - Hardware style (0-31)
 * @param {number} config.hardware[].color - Hardware color (0-31)
 * @returns {string} Encoded URL string (e.g., "0-1-4-1-0421")
 */
function encodeProduct(config) {
    // Validate main attributes
    if (config.type < 0 || config.type > 15) {
        throw new Error(`Invalid product type: ${config.type} (must be 0-15)`);
    }
    if (config.color < 0 || config.color > 15) {
        throw new Error(`Invalid product color: ${config.color} (must be 0-15)`);
    }
    if (config.material < 0 || config.material > 15) {
        throw new Error(`Invalid product material: ${config.material} (must be 0-15)`);
    }
    
    // Build base string: version-type-color-material
    const parts = [
        ENCODING_VERSION.toString(16).toUpperCase(),
        config.type.toString(16).toUpperCase(),
        config.color.toString(16).toUpperCase(),
        config.material.toString(16).toUpperCase()
    ];
    
    // Add hardware items if present
    if (config.hardware && config.hardware.length > 0) {
        config.hardware.forEach(hw => {
            parts.push(encodeHardware(hw.type, hw.style, hw.color));
        });
    }
    
    return parts.join('-');
}

/**
 * Encode using named constants (convenience function)
 * 
 * @param {Object} config - Product configuration with named values
 * @param {string} config.type - Product type name (e.g., 'PATIO_DOOR')
 * @param {string} config.color - Color name (e.g., 'WHITE')
 * @param {string} config.material - Material name (e.g., 'ALUMINUM')
 * @param {Array<Object>} config.hardware - Array of hardware items (optional)
 * @returns {string} Encoded URL string
 */
function encodeProductByName(config) {
    const numericConfig = {
        type: PRODUCT_TYPE[config.type],
        color: PRODUCT_COLOR[config.color],
        material: PRODUCT_MATERIAL[config.material],
        hardware: []
    };
    
    if (config.hardware && config.hardware.length > 0) {
        numericConfig.hardware = config.hardware.map(hw => ({
            type: HARDWARE_TYPE[hw.type],
            style: HARDWARE_STYLE[hw.style],
            color: HARDWARE_COLOR[hw.color]
        }));
    }
    
    return encodeProduct(numericConfig);
}

// ============================================================================
// DECODING FUNCTIONS
// ============================================================================

/**
 * Decode a hardware hex string back to its components
 * 
 * @param {string} hex - 4-character hex string (e.g., "0421")
 * @returns {Object} Decoded hardware configuration
 */
function decodeHardware(hex) {
    if (typeof hex !== 'string' || hex.length !== 4) {
        throw new Error(`Invalid hardware hex string: ${hex} (must be 4 characters)`);
    }
    
    const value = parseInt(hex, 16);
    
    if (isNaN(value)) {
        throw new Error(`Invalid hex value: ${hex}`);
    }
    
    return {
        type: (value >> 10) & 0x1F,     // Extract bits 10-14
        style: (value >> 5) & 0x1F,     // Extract bits 5-9
        color: value & 0x1F             // Extract bits 0-4
    };
}

/**
 * Decode a complete product URL string
 * 
 * @param {string} encodedString - Encoded URL string (e.g., "0-1-4-1-0421")
 * @returns {Object} Decoded product configuration
 */
function decodeProduct(encodedString) {
    if (typeof encodedString !== 'string') {
        throw new Error('Invalid encoded string: must be a string');
    }
    
    const parts = encodedString.split('-');
    
    if (parts.length < 4) {
        throw new Error(`Invalid encoded string: ${encodedString} (must have at least 4 parts)`);
    }
    
    // Parse version
    const version = parseInt(parts[0], 16);
    if (version !== ENCODING_VERSION) {
        throw new Error(`Unsupported encoding version: ${version} (current: ${ENCODING_VERSION})`);
    }
    
    // Parse main attributes
    const config = {
        version: version,
        type: parseInt(parts[1], 16),
        color: parseInt(parts[2], 16),
        material: parseInt(parts[3], 16),
        hardware: []
    };
    
    // Parse hardware items (if present)
    for (let i = 4; i < parts.length; i++) {
        config.hardware.push(decodeHardware(parts[i]));
    }
    
    return config;
}

/**
 * Decode and return named values (convenience function)
 * 
 * @param {string} encodedString - Encoded URL string
 * @returns {Object} Decoded product with named values
 */
function decodeProductToNames(encodedString) {
    const numeric = decodeProduct(encodedString);
    
    return {
        version: numeric.version,
        type: PRODUCT_TYPE_REVERSE[numeric.type] || `UNKNOWN_${numeric.type}`,
        color: PRODUCT_COLOR_REVERSE[numeric.color] || `UNKNOWN_${numeric.color}`,
        material: PRODUCT_MATERIAL_REVERSE[numeric.material] || `UNKNOWN_${numeric.material}`,
        hardware: numeric.hardware.map(hw => ({
            type: HARDWARE_TYPE_REVERSE[hw.type] || `UNKNOWN_${hw.type}`,
            style: HARDWARE_STYLE_REVERSE[hw.style] || `UNKNOWN_${hw.style}`,
            color: HARDWARE_COLOR_REVERSE[hw.color] || `UNKNOWN_${hw.color}`
        }))
    };
}

// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================

/**
 * Get the binary representation of a hex string (for debugging)
 * 
 * @param {string} hex - Hex string
 * @returns {string} Binary string with spaces every 4 bits
 */
function hexToBinary(hex) {
    const value = parseInt(hex, 16);
    const binary = value.toString(2).padStart(16, '0');
    return binary.match(/.{1,4}/g).join(' ');
}

/**
 * Validate that a configuration uses only defined values
 * 
 * @param {Object} config - Product configuration
 * @returns {Object} Validation result with errors array
 */
function validateConfiguration(config) {
    const errors = [];
    
    if (!PRODUCT_TYPE_REVERSE[config.type]) {
        errors.push(`Invalid product type: ${config.type}`);
    }
    if (!PRODUCT_COLOR_REVERSE[config.color]) {
        errors.push(`Invalid product color: ${config.color}`);
    }
    if (!PRODUCT_MATERIAL_REVERSE[config.material]) {
        errors.push(`Invalid product material: ${config.material}`);
    }
    
    if (config.hardware) {
        config.hardware.forEach((hw, index) => {
            if (!HARDWARE_TYPE_REVERSE[hw.type]) {
                errors.push(`Invalid hardware ${index + 1} type: ${hw.type}`);
            }
            if (!HARDWARE_STYLE_REVERSE[hw.style]) {
                errors.push(`Invalid hardware ${index + 1} style: ${hw.style}`);
            }
            if (!HARDWARE_COLOR_REVERSE[hw.color]) {
                errors.push(`Invalid hardware ${index + 1} color: ${hw.color}`);
            }
        });
    }
    
    return {
        valid: errors.length === 0,
        errors: errors
    };
}

// ============================================================================
// EXPORTS (for Node.js / Module usage)
// ============================================================================

if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
        // Constants
        ENCODING_VERSION,
        PRODUCT_TYPE,
        PRODUCT_COLOR,
        PRODUCT_MATERIAL,
        HARDWARE_TYPE,
        HARDWARE_STYLE,
        HARDWARE_COLOR,
        
        // Encoding functions
        encodeHardware,
        encodeProduct,
        encodeProductByName,
        
        // Decoding functions
        decodeHardware,
        decodeProduct,
        decodeProductToNames,
        
        // Utilities
        hexToBinary,
        validateConfiguration
    };
}

// ============================================================================
// EXAMPLE USAGE
// ============================================================================

/*
// Example 1: Encode by numeric values
const encoded1 = encodeProduct({
    type: 1,        // Patio Door
    color: 4,       // White
    material: 1,    // Aluminum
    hardware: [
        { type: 1, style: 1, color: 1 }  // Lever, Standard, Brass
    ]
});
console.log(encoded1); // "0-1-4-1-0421"

// Example 2: Encode by named values
const encoded2 = encodeProductByName({
    type: 'STORM_DOOR',
    color: 'WHITE',
    material: 'ALUMINUM',
    hardware: [
        { type: 'LEVER', style: 'STANDARD', color: 'BRASS' },
        { type: 'PULL_HANDLE', style: 'ALTERNATIVE', color: 'SATIN' }
    ]
});
console.log(encoded2); // "0-3-4-1-0421-0C64"

// Example 3: Decode back to numeric values
const decoded1 = decodeProduct("0-1-4-1-0421");
console.log(decoded1);
// { version: 0, type: 1, color: 4, material: 1, hardware: [{ type: 1, style: 1, color: 1 }] }

// Example 4: Decode to named values
const decoded2 = decodeProductToNames("0-3-4-1-0421-0C64");
console.log(decoded2);
// {
//   version: 0,
//   type: 'STORM_DOOR',
//   color: 'WHITE',
//   material: 'ALUMINUM',
//   hardware: [
//     { type: 'LEVER', style: 'STANDARD', color: 'BRASS' },
//     { type: 'PULL_HANDLE', style: 'ALTERNATIVE', color: 'SATIN' }
//   ]
// }

// Example 5: Debug hardware encoding
const hwHex = encodeHardware(3, 12, 20);
console.log(hwHex);                    // "0D94"
console.log(hexToBinary(hwHex));       // "0000 1101 1001 0100"
console.log(decodeHardware(hwHex));    // { type: 3, style: 12, color: 20 }

// Example 6: Validate configuration
const validation = validateConfiguration({
    type: 1, color: 4, material: 1,
    hardware: [{ type: 1, style: 1, color: 1 }]
});
console.log(validation); // { valid: true, errors: [] }
*/
