import {until, By, WebElement} from 'selenium-webdriver'
import fs from 'fs'
import {Cookies, URL, makeWebDriver} from '../helpers'
import {VideoObj} from './uploadVideo'

function getSubstr(str:string, narrow:boolean, len:number){
    if(!narrow){
        return str.substr(0, len);
    }
    else{
        var ret = '';
        for(var i = 0; i < str.length; i++){
            if(str.substr(0, i+1).replace(/[\u4e00-\u9fa5]/g, '**').length<=len){
                ret = str.substr(0, i+1);
            }
            else{
                break;
            }
        }
        return ret;
    }
}

const validateVideoObj = (videoObj : VideoObj) => {
    if (!videoObj.videoPath) throw new Error("VideoObj: missing required property videoPath")
    if (!videoObj.title) throw new Error("VideoObj: missing required property title")
    var titleLen = videoObj.title.replace(/[\u4e00-\u9fa5]/g, '**').length;
    if (titleLen < 16) throw new Error("VideoObj: given title is shorter than min 16 characters");
    if (titleLen > 40){
        console.log("VideoObj: given title is longer than max 40 characters");
        videoObj.title = getSubstr(videoObj.title, true, 40);
    }
    if (videoObj.description.length > 100){
        console.log("VideoObj: given description is longer than max 100 characters");
        videoObj.description = getSubstr(videoObj.description, false, 100);
    } 
    if (!fs.existsSync(videoObj.videoPath)) throw new Error(`VideoObj: given videoPath doesn't exist on disk (${videoObj.videoPath})`)
    if (videoObj.thumbnailPath && !fs.existsSync(videoObj.thumbnailPath)) throw new Error(`VideoObj: given thumbnailPath doesn't exist on disk (${videoObj.thumbnailPath})`)
}

export default async (videoObj : VideoObj, cookies : Cookies, headlessMode = true, onProgress = console.log):Promise<string> => {
    if (!cookies || !cookies.length) throw new Error("Can't upload video: cookies not set.")

    validateVideoObj(videoObj)

    // Fill default values
    videoObj = {
        description: '',
        category:'无',
        ...videoObj
    }

    const driver = await makeWebDriver({headless: headlessMode, fullsize: true})

    const enterEmojiString = async (webElement : WebElement, string : string) => {
        // sendKeys(string) doesn't support emojis (causes a crash)
        // youtube custom input elements don't have "value" property (but webEl.clear() still works)
        // clipboard hack works but not in headless mode
        // also editing innerHTML causes racing conditions with the underlying javascript mechanism
        // solution is to use an obsolete method document.execCommand('insreText')
        await driver.sleep(500)
        webElement.click()
        await driver.sleep(250)
        webElement.clear()
        await driver.sleep(250)
        await driver.executeScript(`
        arguments[0].focus();
        document.execCommand('insertText', false, arguments[1]);
        `, webElement, string)
    }

    const findElements = async (cssSelector : string) => {
        var webEls = await driver.findElements(By.css(cssSelector))
        if (webEls[0]) await driver.executeScript('arguments[0].scrollIntoViewIfNeeded()', webEls[0])
        return webEls
    }

    const findElement = async (cssSelector : string) => {
        var els = await findElements(cssSelector)
        if (els.length === 0) throw new Error(`Element was not found with selector '${cssSelector}'`)
        return els[0]
    }

    const tryFindElement = async (cssSelector : string) => {
        var els = await findElements(cssSelector)
        if (els.length == 0) return false
        return els[0]
    }

    try {
        onProgress('Settings cookies..')

        // Load haokan page to set up cookies
        await driver.get(URL.HAOKAN)

        // Add cookies
        for (let cookie of cookies) await driver.manage().addCookie(cookie)

        await driver.sleep(1000)
        
        //refresh
        await driver.get(URL.HAOKAN)

        await driver.sleep(1000)

        onProgress('redirect to upload page..')

        await (await findElement('.icon-upload')).click()

        var windows = await driver.getAllWindowHandles();
        if(windows.length==2){
            await driver.switchTo().window(windows[1]);
        }

        // Wait for stuff to fully load
        await driver.sleep(1000*5)

        // Check whether user is still in login state (which is the case if cookies are not valid / are expired)
        if (!(await driver.getCurrentUrl()).includes(URL.HAOKAN_UPLOAD)) {
            throw new Error(`Cookies are expired or not valid.`)
        }

        // Wait for file input to appear
        await driver.wait(until.elementsLocated(By.css("input[type=file]")), 1000*10)

        onProgress('Initializing video..');

        // Enter file path
        await (await findElement("input[type=file]")).sendKeys(videoObj.videoPath)

        // Wait for file metadata to upload
        await driver.wait(until.elementsLocated(By.css(".text-success")), 1000*60*5)

        var uploadVideoSucText = await findElement('.text-success');
        if(uploadVideoSucText){
            // Wait for random javascript garbage to load
            await driver.sleep(1000*2);

            onProgress('Initializing title and description..')

            // Enter title and description
            var editBoxes = await findElements('.hk-input');
            var titleBox = editBoxes[0];
            var descriptionBox = editBoxes[1];
            await enterEmojiString(titleBox, videoObj.title);
            await enterEmojiString(descriptionBox, videoObj.description);
            await driver.sleep(1000);

            onProgress('Entering custom thumbnail..')
            
            async function onUploadThumbnail(){
                await (await findElement('.btn-publish')).click();
                await driver.sleep(1000*10);
                if(!(await tryFindElement('.btn-publish'))){
                    await (await findElement('p.product')).click();
                    await driver.sleep(1000 * 5);
                    if (await tryFindElement('.v-title') && (await (await findElement('.v-title')).getText()) == videoObj.title) {
                        await (await findElement('.v-title')).click();
                        var windows = await driver.getAllWindowHandles();
                        if (windows.length == 3) {
                            await driver.switchTo().window(windows[2]);
                            await driver.sleep(1000 * 5);
                            return JSON.stringify({ status: 1, videoUrl: await driver.getCurrentUrl() });
                        }
                        else{
                            console.error('Fail to publish because could not get url!');
                            return JSON.stringify({ status: 1, videoUrl: URL.HAOKAN_PRODUCT });
                        }
                    }
                    else {
                        console.error('Fail to publish because could not open product page!');
                        return JSON.stringify({ status: 1, videoUrl: URL.HAOKAN_PRODUCT });
                    }
                }
                else{
                    throw new Error(`Fail to publish because publish button doesn't disappear!`);
                }
            }   

            // Enter custom thumbnail
            if (videoObj.thumbnailPath) {
                await (await findElement(".click-area")).click();
                await driver.sleep(1000);
                await (await findElements(".modal-header-tab"))[1].click();
                var thumbnailBox = await findElement('.image-uploader-input');
                await thumbnailBox.sendKeys(videoObj.thumbnailPath);
                await driver.wait(until.elementsLocated(By.css(".btn-submit")), 1000*10)
                var thumbFinBtn = await findElement('.btn-submit');
                if(thumbFinBtn){
                    thumbFinBtn.click();
                    await driver.sleep(1000*2);
                    return await onUploadThumbnail();
                }
                else{
                    throw new Error('Fail to upload thumbnail!');
                }
            }
            else{
                return await onUploadThumbnail();
            }
        }
        else{
            throw new Error('Fail to upload video!');
        }
    }
    catch(e){
        return Promise.resolve(e);
    }
    finally {
        await driver.quit();
    }

}
