import fileManipulator from './index'
import * as fs from 'fs-extra'
import utils from './utils'

//must be not async and return never (ok callvack call just inside then)
beforeEach((ok) => {
    utils.restoreTestFolder().then(ok)
})
afterAll((ok) => {
    utils.restoreTestFolder().then(ok)
})

describe('Copper ', function () {

    describe('files', () => {
        test('from frolder to other folder, save name', async () => {
            const isCopied = await fileManipulator.copy.file({
                from: './test_folder/f1',
                name: 'f1',
                ext: 'txt',
                to: './test_folder/f2',
            })
            const isExist = await utils.checkExist('./test_folder/f2/f1.txt')
            expect(isCopied).toBeTruthy()
            expect(isExist).toBeTruthy()
        })
        test('from frolder to current folder, different name without error', async () => {
            const isCopied = await fileManipulator.copy.file({
                from: './test_folder/f1',
                name: 'f1',
                ext: 'txt',
                to: './test_folder/f1',
                renameTo: 'f2'
            })
            const isExist = await utils.checkExist('./test_folder/f1/f2.txt')
            expect(isCopied).toBeTruthy()
            expect(isExist).toBeTruthy()
        })
        test('from frolder to other folder, change name to same existing with error (withOUT overwrite)', async () => {
            const isCopied = await fileManipulator.copy.file({
                from: './test_folder/f1',
                name: 'f1',
                ext: 'txt',
                to: './test_folder/f2',
                renameTo: 'f2',
                replaceExisting: false
            })
            const isExist = await utils.checkExist('./test_folder/f2/f2.txt')
            expect(isExist).toBeTruthy()
            expect(isCopied).toBeFalsy()
        })
        test('from frolder to other folder, change name to same existing without error (with overwrite)', async () => {
            const isCopied = await fileManipulator.copy.file({
                from: './test_folder/f1',
                name: 'f1',
                ext: 'txt',
                to: './test_folder/f2',
                renameTo: 'f2',
                replaceExisting: true
            })
            const isExist = await utils.checkExist('./test_folder/f2/f2.txt')
            expect(isExist).toBeTruthy()
            expect(isCopied).toBeTruthy()
        })
        test('copy file without ext', async () => {
            const isCopied = await fileManipulator.copy.file({
                from: './test_folder/f1',
                name: 'f1.txt',
                to: './test_folder/f2',
                renameTo: 'f2.txt',
                replaceExisting: true
            })
            const isExist = await utils.checkExist('./test_folder/f2/f2.txt')
            expect(isExist).toBeTruthy()
            expect(isCopied).toBeTruthy()
        })

    })

    describe('folders', () => {
        test('from frolder to other folder, save name', async () => {
            const isCopied = await fileManipulator.copy.folder({
                name: 'f1',
                from: './test_folder',
                to: './test_folder/f2',
            })
            expect(isCopied).toBeTruthy()
        })
        test('from frolder to other folder, change name to existing with error (with overwrite)', async () => {
            const isCopied = await fileManipulator.copy.folder({
                name: 'f1',
                from: './test_folder',
                to: './test_folder',
                renameTo: 'f2',
                replaceExisting: true
            })
            const isExist = await utils.checkExist('./test_folder')
            expect(isCopied).toBeFalsy()
            expect(isExist).toBeTruthy()
        })
        test('from frolder to other folder, change name to existing with error (without overwrite)', async () => {
            const isCopied = await fileManipulator.copy.folder({
                name: 'f1',
                from: './test_folder',
                to: './test_folder',
                renameTo: 'f2',
                replaceExisting: false
            })
            const isExist = await utils.checkExist('./test_folder')
            expect(isCopied).toBeFalsy()
            expect(isExist).toBeTruthy()
        })
    })
})

describe('Mover', function () {
    describe('file', function () {
        test('move file from one folder to other and save name', async () => {
            const isMoved = await fileManipulator.move.file({
                from: './test_folder/f1',
                to: './test_folder/f2',
                name: 'f1',
                ext: 'txt',
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1/f1.txt')
            const isExistAfter = await utils.checkExist('./test_folder/f2/f1.txt')
            expect(isMoved).toBeTruthy()
            expect(isExistBefore).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('move file from one folder to other and change name name', async () => {
            const isMoved = await fileManipulator.move.file({
                from: './test_folder/f1',
                to: './test_folder/f2',
                name: 'f1',
                ext: 'txt',
                renameTo: 'f3'
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1/f1.txt')
            const isExistAfter = await utils.checkExist('./test_folder/f2/f3.txt')
            expect(isMoved).toBeTruthy()
            expect(isExistBefore).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('move file from one folder to other and change name to existing threre withOUT err (with overwriting)', async () => {
            const isMoved = await fileManipulator.move.file({
                from: './test_folder/f1',
                to: './test_folder/f2',
                name: 'f1',
                ext: 'txt',
                renameTo: 'f2',
                replaceExisting: true
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1/f1.txt')
            const isExistAfter = await utils.checkExist('./test_folder/f2/f2.txt')
            expect(isMoved).toBeTruthy()
            expect(isExistBefore).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('move file from one folder to other and change name to existing threre with err (withOUT overwriting)', async () => {
            const isMoved = await fileManipulator.move.file({
                from: './test_folder/f1',
                to: './test_folder/f2',
                name: 'f1',
                ext: 'txt',
                renameTo: 'f2',
                replaceExisting: false
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1/f1.txt')
            const isExistAfter = await utils.checkExist('./test_folder/f2/f2.txt')
            expect(isMoved).toBeFalsy()
            expect(isExistBefore).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('move file without ext', async () => {
            const isMoved = await fileManipulator.move.file({
                from: './test_folder/f1',
                to: './test_folder/f2',
                name: 'f1.txt',
                renameTo: 'f2.txt',
                replaceExisting: true
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1/f1.txt')
            const isExistAfter = await utils.checkExist('./test_folder/f2/f2.txt')
            expect(isMoved).toBeTruthy()
            expect(isExistBefore).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
    })

    describe('folder', function () {
        test('folder move to other folder', async () => {
            const isMoved = await fileManipulator.move.folder({
                from: './test_folder',
                to: './test_folder/f2',
                name: 'f1',
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1')
            const isExistAfter = await utils.checkExist('./test_folder/f2/f1')
            expect(isMoved).toBeTruthy()
            expect(isExistBefore).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('folder move to current folder with existing name withOUT err (overwrite)', async () => {
            const isMoved = await fileManipulator.move.folder({
                from: './test_folder',
                to: './test_folder',
                name: 'f1',
                renameTo: 'f2',
                replaceExisting: true
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1')
            const isExistAfter = await utils.checkExist('./test_folder/f2')
            expect(isMoved).toBeTruthy()
            expect(isExistBefore).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('folder move to current folder with existing name with err (withOUT overwrite)', async () => {
            const isMoved = await fileManipulator.move.folder({
                from: './test_folder',
                to: './test_folder',
                name: 'f1',
                renameTo: 'f2',
                replaceExisting: false
            })
            const isExistBefore = await utils.checkExist('./test_folder/f1')
            const isExistAfter = await utils.checkExist('./test_folder/f2')
            expect(isMoved).toBeFalsy()
            expect(isExistBefore).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
    })
})

describe('Deleter', function () {
    describe('file', function () {
        test('remove exist file', async () => {
            const isDeleted = await fileManipulator.delete.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            expect(isDeleted).toBeTruthy()
            expect(isExistAfter).toBeFalsy()
        })
        test('remove not exist file and not crash and save neighbours files', async () => {
            const isDeleted = await fileManipulator.delete.file({
                name: 'f3',
                ext: 'txt',
                path: './test_folder/f1'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            expect(isDeleted).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('remove file without ext', async () => {
            const isDeleted = await fileManipulator.delete.file({
                name: 'f1.txt',
                path: './test_folder/f1'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            expect(isDeleted).toBeTruthy()
            expect(isExistAfter).toBeFalsy()
        })
    })

    describe('folder', function () {
        test('remove exist folder', async () => {
            const isDeleted = await fileManipulator.delete.folder({
                name: 'f1',
                path: './test_folder'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1')
            expect(isDeleted).toBeTruthy()
            expect(isExistAfter).toBeFalsy()
        })
        test('remove not exist folder and not crash and save neighbours folders', async () => {
            const isDeleted = await fileManipulator.delete.folder({
                name: 'f3',
                path: './test_folder'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1')
            expect(isDeleted).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
    })
})


describe('Creator', function () {
    describe('file', function () {
        test('create new file (new name)', async () => {
            const isCreated = await fileManipulator.create.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f2'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f2/f1.txt')
            expect(isCreated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('create new file and save content from props save existing', async () => {
            const isCreated = await fileManipulator.create.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f2',
                content: 'test string'
            })
            const content = await fs.readFile('./test_folder/f2/f1.txt', {encoding: 'utf8'})
            expect(isCreated).toBeTruthy()
            expect(content).toBe('test string')
        })
        test('create new file and save content from props save existing and custom encoding', async () => {
            const encoding: BufferEncoding = 'binary'
            const isCreated = await fileManipulator.create.file({
                name: 'f3',
                ext: 'txt',
                path: './test_folder/f2',
                content: 'test string',
                encoding
            })
            const content = await fs.readFile('./test_folder/f2/f3.txt', {encoding})
            const contentExpected = Buffer.from('test string').toString(encoding)
            console.log(content, contentExpected)
            expect(isCreated).toBeTruthy()
            expect(content).toBe(contentExpected)
        })
        test('create new file (existing name) withOUT err (with overwrite)', async () => {
            const isCreated = await fileManipulator.create.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                replaceExisting: true
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            expect(isCreated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('create new file (existing name) with err (withOUT overwrite)', async () => {
            const isCreated = await fileManipulator.create.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                replaceExisting: false
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            expect(isCreated).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('create new file (existing name) without ext', async () => {
            const isCreated = await fileManipulator.create.file({
                name: 'f1.txt',
                path: './test_folder/f1',
                replaceExisting: true
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            expect(isCreated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
    })

    describe('folder', function () {
        test('create new folder (new name)', async () => {
            const isCreated = await fileManipulator.create.folder({
                name: 'f1',
                path: './test_folder/f2'
            })
            const isExistAfter = await utils.checkExist('./test_folder/f2/f1')
            expect(isCreated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('create new folder without err (with overwrite)', async () => {
            const isCreated = await fileManipulator.create.folder({
                name: 'f1',
                path: './test_folder',
                replaceExisting: true
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1')
            expect(isCreated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('create new folder with err (without overwrite)', async () => {
            const isCreated = await fileManipulator.create.folder({
                name: 'f1',
                path: './test_folder',
                replaceExisting: false
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1')
            expect(isCreated).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
    })
})

describe('Updater', function () {
    describe('file', function () {
        test('update file name only', async () => {
            const isUpdated = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                renameTo: 'f2',
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f2.txt')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('update file name only if threr exist the same name withOUT error, with overwrite', async () => {
            //create mock file
            const isCreated = await fileManipulator.create.file({
                name: 'f2',
                path: './test_folder/f1',
                ext: 'txt',
                content: 'x'
            })
            const isUpdated = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                renameTo: 'f2',
                replaceExisting: true
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f2.txt')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('update file content full with replace', async () => {
            //create mock file
            const isCreated = await fileManipulator.create.file({
                name: 'f2',
                ext: 'txt',
                path: './test_folder/f1',
                content: 'text',
            })
            const isUpdated = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                renameTo: 'f2',
                replaceExisting: false
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f2.txt')
            expect(isUpdated).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
        test('update file content some', async () => {
            const isUpdated = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                stringFrom: /.*/, //replace all text
                stringTo: 'result',
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            const contentAfter = await utils.readFile('./test_folder/f1/f1.txt')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
            expect(contentAfter).toBe('result')
        })
        test('update file content some col', async () => {
            const startText = `const x = 1;`
            const expectedText = `const y = 2;`

            await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                stringFrom: /.*/, //replace all text
                stringTo: startText,
            })
            const isUpdated = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                lineParams: {
                    line: 0,
                    space: 6,
                    len: 5
                },
                stringTo: 'y = 2',
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            const contentAfter = await utils.readFile('./test_folder/f1/f1.txt')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
            expect(contentAfter).toBe(expectedText)
        })

        test('update file content some row and col', async () => {
            const startText = `
const x = 1;
let y = 2;
    "hello world"
    firstBlock {
        secondBlock {
            value: 1;
            config: true;
        }
    }
`
            const expectedText = `
const x = 1;
let y = 2;
    "hello world"
    firstBlock {
        secondBlock {
            value: 5;
            config: true;
            path: "./key"
        }
    }
`

            //1) set up empty file
            await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                stringFrom: /.*/, //replace all text
                stringTo: startText,
            })
            //2) edit first row
            const isUpdated1 = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                lineParams: {
                    line: 6,
                    space: 19,
                    len: 1
                },
                stringTo: '5',
            })
            //3) edit second row
            const isUpdated2 = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                lineParams: {
                    line: 8,
                    space: 8,
                    len: 1
                },
                stringTo: '    path: "./key"\n        }',
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f1.txt')
            const contentAfter = await utils.readFile('./test_folder/f1/f1.txt')
            expect(isUpdated1).toBeTruthy()
            expect(isUpdated2).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
            expect(contentAfter).toBe(expectedText)
        })

        /**
         * must hard test for 1) renaming 2)replaceing two code blocks
         */
        test('update file content some row and col and rename and replace part of code', async () => {
            const startText = `
const x = 1;
let y = 2;
    "hello world"
    firstBlock {
        secondBlock {
            value: 1;
            config: true;
        }
    }
`
            const expectedText = `
const x = 1;
let y = 2;
    "hello world"
    firstBlock {
        secondBlock {
            value: 5;
            config: true;
            path: "./key"
        }
    }
`

            //1) set up empty file
            await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                stringFrom: /.*/, //replace all text
                stringTo: startText,
            })
            //2) edit first row
            const isUpdated1 = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                lineParams: {
                    line: 6,
                    space: 19,
                    len: 1
                },
                stringTo: '5',
                renameTo: 'f3'
            })
            //3) edit second row
            const isUpdated2 = await fileManipulator.update.file({
                name: 'f3',
                ext: 'txt',
                path: './test_folder/f1',
                lineParams: {
                    line: 8,
                    space: 8,
                    len: 1
                },
                stringTo: '    path: "./key"\n        }',
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f3.txt')
            const contentAfter = await utils.readFile('./test_folder/f1/f3.txt')
            expect(isUpdated1).toBeTruthy()
            expect(isUpdated2).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
            expect(contentAfter).toBe(expectedText)
        })
        test('rewirite if string from empty', async () => {
            const isRewrited = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                stringTo: 'hello'
            })
            const resultFileContent = await utils.readFile('./test_folder/f1/f1.txt')
            expect(isRewrited).toBeTruthy()
            expect(resultFileContent).toBe('hello')
        })
        test('update file without ext', async () => {
            const isUpdated = await fileManipulator.update.file({
                name: 'f1.txt',
                path: './test_folder/f1',
                renameTo: 'f2.txt',
            })
            const isExistAfter = await utils.checkExist('./test_folder/f1/f2.txt')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
    })

    describe('folder', function () {
        test('update folder name only', async () => {
            const isUpdated = await fileManipulator.update.folder({name: 'f1', path: './test_folder', renameTo: 'f3'})
            const isExistAfter = await utils.checkExist('./test_folder/f3')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('update folder name only withOUT error, with overwrite', async () => {
            const isUpdated = await fileManipulator.update.folder({
                name: 'f1',
                path: './test_folder',
                renameTo: 'f2',
                replaceExisting: true
            })
            const isExistAfter = await utils.checkExist('./test_folder/f2')
            expect(isUpdated).toBeTruthy()
            expect(isExistAfter).toBeTruthy()
        })
        test('update folder name only with, error withOUT overwrite', async () => {
            const isUpdated = await fileManipulator.update.folder({
                name: 'f1',
                path: './test_folder',
                renameTo: 'f2',
                replaceExisting: false
            })
            const isExistAfter = await utils.checkExist('./test_folder/f2')
            expect(isUpdated).toBeFalsy()
            expect(isExistAfter).toBeTruthy()
        })
    })
})

describe('Reader', function () {
    describe('file', function () {
        test('read file return file inner text', async () => {
            const isUpdated = await fileManipulator.update.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
                stringFrom: /.*/,
                stringTo: 'test'
            })
            const {readed, result} = await fileManipulator.read.file({
                name: 'f1',
                ext: 'txt',
                path: './test_folder/f1',
            })
            expect(readed).toBeTruthy()
            expect(isUpdated).toBeTruthy()
            expect(result).toBe('test')
        })
        test('not read not exist file', async () => {
            const {readed, result} = await fileManipulator.read.file({
                name: 'f3',
                ext: 'txt',
                path: './test_folder/f1',
            })
            expect(readed).toBeFalsy()
            expect(result).toBe('')
        })
    })
    describe('folder', function () {
        test('read folder inner and return file names with ext', async () => {
            const {readed, result} = await fileManipulator.read.folder({
                name: 'f1',
                path: './test_folder',
            })
            expect(readed).toBeTruthy()
            expect(result).toEqual(['f1.txt'])
        })
        test('read folder inner and return folder names', async () => {
            const {readed, result} = await fileManipulator.read.folder({
                name: 'test_folder',
                path: './',
            })
            const isInclude1 = result?.includes('f1')
            const isInclude2 = result?.includes('f2')
            expect(readed).toBeTruthy()
            expect(isInclude1).toBeTruthy()
            expect(isInclude2).toBeTruthy()
        })
        test('not read not exist folder', async () => {
            const {readed, result} = await fileManipulator.read.folder({
                name: 'f3',
                path: './test_folder',
            })
            expect(readed).toBeFalsy()
            expect(result).toEqual([])
        })
    })
})
