import fs from "fs";
import path from "path";
import { MutableObject } from "../src";

function convertJsonToBson(json: any): Uint8Array {
    const buffer: number[] = [];

    function serializeValue(value: any): void {
        switch (typeof value) {
            case 'boolean':
                buffer.push(value ? 1 : 0);
                break;
            case 'number':
                const intBuffer = new ArrayBuffer(8);
                const floatView = new Float64Array(intBuffer);
                floatView[0] = value;
                const bytes = new Uint8Array(intBuffer);
                buffer.push(...bytes);
                break;
            case 'string':
                const utf8Encoder = new TextEncoder();
                const utf8Bytes = utf8Encoder.encode(value);
                const lengthBytes = convertNumberToBytes(utf8Bytes.length);
                buffer.push(...lengthBytes, ...utf8Bytes);
                break;
            case 'object':
                if (value === null) {
                    buffer.push(0x00);
                } else if (Array.isArray(value)) {
                    const lengthBytes = convertNumberToBytes(value.length);
                    buffer.push(0x04, ...lengthBytes);
                    for (const element of value) {
                        serializeValue(element);
                    }
                } else {
                    const keys = Object.keys(value);
                    const lengthBytes = convertNumberToBytes(keys.length);
                    buffer.push(0x03, ...lengthBytes);
                    for (const key of keys) {
                        const utf8KeyBytes = new TextEncoder().encode(key);
                        const keyLengthBytes = convertNumberToBytes(utf8KeyBytes.length);
                        buffer.push(...keyLengthBytes, ...utf8KeyBytes);
                        serializeValue(value[key]);
                    }
                }
                break;
            default:
                throw new Error('Unsupported data type. TYPE: ' + typeof value);
        }
    }

    function convertNumberToBytes(value: number): number[] {
        const bytes: number[] = [];
        do {
            bytes.unshift(value & 0xff);
            value = value >> 8;
        } while (value > 0);
        return bytes;
    }

    serializeValue(json);
    return new Uint8Array(buffer);
}


function convertBsonToJson(bson: Uint8Array): any {
    let currentIndex = 0;

    function deserializeValue(bson: Uint8Array): any {
        const type = bson[currentIndex++];
        switch (type) {
            case 0x01: // double
                const doubleBytes = bson.subarray(currentIndex, currentIndex + 8);
                currentIndex += 8;
                const floatView = new Float64Array(doubleBytes.buffer);
                return floatView[0];
            case 0x02: // string
                const strLength = convertBytesToNumber(bson);
                const utf8Bytes = bson.subarray(currentIndex, currentIndex + strLength);
                currentIndex += strLength;
                const utf8Decoder = new TextDecoder();
                return utf8Decoder.decode(utf8Bytes);
            case 0x03: // object
                const objLength = convertBytesToNumber(bson);
                const obj: any = {};
                for (let i = 0; i < objLength; i++) {
                    const keyLength = convertBytesToNumber(bson);
                    const keyBytes = bson.subarray(currentIndex, currentIndex + keyLength);
                    currentIndex += keyLength;
                    const key = new TextDecoder().decode(keyBytes);
                    obj[key] = deserializeValue(bson);
                }
                return obj;
            case 0x04: // array
                const arrLength = convertBytesToNumber(bson);
                const arr: any[] = [];
                for (let i = 0; i < arrLength; i++) {
                    arr.push(deserializeValue(bson));
                }
                return arr;
            case 0x00: // null
                return null;
            case 0x08: // boolean
                return bson[currentIndex++] === 0x01;
            default:
                throw new Error('Unsupported BSON type');
        }
    }

    function convertBytesToNumber(bson: Uint8Array): number {
        let value = 0;
        let shift = 0;
        let byte;
        do {
            byte = bson[currentIndex++];
            value |= (byte & 0x7f) << shift;
            shift += 7;
        } while (byte & 0x80);
        return value;
    }

    return deserializeValue(bson);
}



// // Example usage
const json = {
    name: 'John Doe',
    age: 30,
    hobbies: ['reading', 'coding', 'gaming'],
    address: {
        street: '123 Main St',
        city: 'New York',
        country: 'USA'
    }
};

// const bson = convertJsonToBson(json);
// fs.writeFileSync("./suss.s", bson);
// console.log('BSON:', bson);



// const convertedJson = convertBsonToJson(fs.readFileSync(path.join(__dirname, "./suss.s")));
// console.log('Converted JSON:', convertedJson);

function convertYamlToJson(yamlString: string): string | null {
    try {
        const lines = yamlString.split('\n');
        const result = parseLines(lines);
        return JSON.stringify(result, null, 2);
    } catch (error) {
        console.error('Error converting YAML to JSON:', error);
        return null;
    }
}

function parseLines(lines: string[]): any {
    const result: any = {};
    let currentIndent = -1;

    for (let line of lines) {
        const indent = line.search(/\S/);
        line = line.trim();

        if (line === '') {
            continue; // Skip empty lines
        }

        if (indent > currentIndent) {
            currentIndent = indent;
        } else if (indent < currentIndent) {
            currentIndent = indent;
        }

        const [key, value] = line.split(':');
        const trimmedKey = key.trim();
        const trimmedValue = value ? value.trim() : '';

        if (trimmedValue.includes(':')) {
            const nestedLines: string[] = [];
            let nestedIndent = indent;

            while (lines.length > 0 && lines[0].search(/\S/) === nestedIndent + 2) {
                const nestedLine = lines.shift()!;
                nestedLines.push(nestedLine.trim());
            }

            result[trimmedKey] = parseLines(nestedLines);
        } else {
            result[trimmedKey] = parseValue(trimmedValue);
        }
    }

    return result;
}

function parseValue(value: string): any {
    if (value === 'true') {
        return true;
    }

    if (value === 'false') {
        return false;
    }

    if (!isNaN(Number(value))) {
        return Number(value);
    }

    return value;
}


function convertJsonToYaml(jsonObj: object) {
    try {
        const lines: string[] = [];
        stringifyObject(jsonObj, lines, '');
        return lines.join('\n');
    } catch (error) {
        console.error('Error converting JSON to YAML:', error);
        return null;
    }
}

function stringifyObject(obj: MutableObject<any>, lines: string[], indent: string) {
    if (Array.isArray(obj)) {
        for (let item of obj) {
            if (typeof item === 'object' && !Array.isArray(item)) {
                lines.push(`${indent}-`);
                stringifyObject(item, lines, `${indent}  `);
            } else {
                lines.push(`${indent}- ${stringifyValue(item)}`);
            }
        }   
    } else {
        for (let key in obj) {
            const value = obj[key];
            if (typeof value === 'object' && !Array.isArray(value)) {
                lines.push(`${indent}${key}:`);
                stringifyObject(value, lines, `${indent}  `);
            } else {
                lines.push(`${indent}${key}: ${stringifyValue(value)}`);
            }
        }
    }
}

function stringifyValue(value: any) {
    if (typeof value === 'string') {
        if (/[":,\[\]\{\}]/.test(value)) {
            return `"${escapeString(value)}"`;
        }
        return value;
    } else if (typeof value === 'number' || typeof value === 'boolean' || value === null) {
        return String(value);
    }
    return JSON.stringify(value);
}

function escapeString(value: string) {
    return value.replace(/["\\]/g, '\\$&');
}


const jsonObject = {
    name: 'John Doe',
    age: 30,
    email: 'john@example.com',
    address: {
        street: '123 Main St',
        city: 'Anytown',
        country: 'USA'
    }
};

// const yamlString = convertJsonToYaml(jsonObject);
// console.log(yamlString);


// const jsonString = convertYamlToJson(yamlString!!);
// console.log(jsonString)