const { cleanLayers } = require('../psapi')
const psapi = require('../psapi')
const io = require('./io')
const Enum = require('../enum')

const { base64ToBase64Url } = require('./general')
const html_manip = require('./html_manip')
const layer_util = require('./layer')

const SessionState = {
    Active: 'active',
    Inactive: 'inactive',
}
const GarbageCollectionState = {
    Accept: 'accept', // accept all generated images
    Discard: 'discard', //discard all generated images
    DiscardSelected: 'discard_selected',
    AcceptSelected: 'accept_selected', //accept_selected only chosen images
}

class GenerationSession {
    constructor() {
        //this should be unique session id and it also should act as the total number of sessions been created in the project
        this.id = 0
        this.state = SessionState['Inactive']
        this.mode = 'txt2img'
        this.selectionInfo = null
        this.isFirstGeneration = true // only before the first generation is requested should this be true
        this.outputGroup
        this.prevOutputGroup
        this.isLoadingActive = false
        this.base64OutputImages = {} //image_id/path => base64_image
        this.base64initImages = {} //init_image_path => base64
        this.base64maskImage = []
        this.base64maskExpansionImage
        this.activeBase64InitImage
        this.activeBase64MaskImage
        this.image_paths_to_layers = {}
        this.progress_layer
        this.last_settings //the last settings been used for generation
        this.controlNetImage = [] // base64 images (one for each control net)
        this.controlNetMask = [] // base64 images (one for each control net)
        this.request_status = Enum.RequestStateEnum['Finished'] //finish or ideal state
        this.is_control_net = false
        this.control_net_selection_info
        this.control_net_preview_selection_info
    }
    isActive() {
        return this.state === SessionState['Active']
    }
    isInactive() {
        return this.state === SessionState['Inactive']
    }
    activate() {
        this.state = SessionState['Active']
    }
    deactivate() {
        this.state = SessionState['Inactive']
    }
    name() {
        return `session - ${this.id}`
    }

    deleteInitImageLayers() {}
    async closePreviousOutputGroup() {
        try {
            //close the previous output folder

            if (this.prevOutputGroup) {
                // const is_visible = await this.prevOutputGroup.visible
                await layer_util.collapseFolderExe(
                    [this.prevOutputGroup],
                    false
                ) // close the folder group
                // and reselect the current output folder for clarity
                await psapi.selectLayersExe([this.outputGroup])
                // this.prevOutputGroup.visible = is_visible
            }
        } catch (e) {
            console.warn(e)
        }
    }
    isSameMode(selected_mode) {
        if (this.mode === selected_mode) {
            return true
        }
        return false
    }
    loadLastSession() {
        //load the last session from the server
    }
    saveCurrentSession() {
        //all session info will be saved in a json file in the project folder
    }
    async moveToTopOfOutputGroup(layer) {
        const output_group_id = await this.outputGroup.id
        let group_index = await psapi.getLayerIndex(output_group_id)
        const indexOffset = 1 //1 for background, 0 if no background exist
        await executeAsModal(async () => {
            await psapi.selectLayersExe([layer]) //the move command is selection selection sensitive
            await psapi.moveToGroupCommand(group_index - indexOffset, layer.id)
        })
    }

    async deleteProgressLayer() {
        try {
            await layer_util.deleteLayers([this.progress_layer]) // delete the old progress layer
        } catch (e) {
            console.warn(e)
        }
    }
    deleteProgressImageHtml() {
        try {
            // await layer_util.deleteLayers([this.progress_layer]) // delete the old progress layer
            // document.getElementById('progressImage').style.width = '0px'
            // document.getElementById('progressImage').style.height = '0px'

            document.getElementById(
                'divProgressImageViewerContainer'
            ).style.height = '0px'
        } catch (e) {
            console.warn(e)
        }
    }
    async deleteProgressImage() {
        preview.store.updateProperty('image', null)
        this.deleteProgressImageHtml()
        await this.deleteProgressLayer()
    }
    async setControlNetImageHelper() {
        const width = html_manip.getWidth()
        const height = html_manip.getHeight()

        //get the selection from the canvas as base64 png, make sure to resize to the width and height slider
        const selectionInfo = await psapi.getSelectionInfoExe()
        this.control_net_selection_info = selectionInfo
        this.control_net_preview_selection_info = selectionInfo
        // const base64_image = await io.IO.getSelectionFromCanvasAsBase64Silent(
        //     selectionInfo,
        //     true,
        //     width,
        //     height
        // )

        const use_silent_mode = html_manip.getUseSilentMode()
        let layer = null
        if (!use_silent_mode) {
            await psapi.snapshot_layerExe()
            const snapshotLayer = await app.activeDocument.activeLayers[0]
            layer = snapshotLayer
        }
        const base64_image =
            await io.IO.getSelectionFromCanvasAsBase64Interface(
                width,
                height,
                layer,
                selectionInfo,
                true,
                use_silent_mode
            )

        await layer_util.deleteLayers([layer]) //delete the snapshot layer if it exists
        return base64_image
    }
    async setControlNetImage(control_net_index = 0, base64_image) {
        //check if the selection area is active
        //convert layer to base64
        //the width and height of the exported image
        // const base64_image = this.setControlNetImageHelper()
        this.controlNetImage[control_net_index] = base64_image
        html_manip.setControlImageSrc(
            base64ToBase64Url(base64_image),
            control_net_index
        )
        // console.log('base64_img:', base64_image)
        // await io.IO.base64ToLayer(base64_image)
    }
}

//REFACTOR: move to generation_settings.js
async function getSettings(session_data) {
    let payload = {}

    try {
        const extension_type = settings_tab_ts.store.data.extension_type // get the extension type
        payload['selection_info'] = session_data?.selectionInfo
        const numberOfBatchSize = parseInt(sd_tab_store.data.batch_size)

        const prompt = multiPrompts.getPrompt().positive
        const negative_prompt = multiPrompts.getPrompt().negative
        const hi_res_fix = sd_tab_store.data.enable_hr

        const upscaler = sd_tab_store.data.hr_upscaler
        const cfg_scale = sd_tab_store.data.cfg

        function calculateSeed(init_seed, batch_index, batch_size) {
            if (init_seed === -1) return -1
            // const seed = init_seed + batch_index * batch_size
            const seed = init_seed + BigInt(batch_index) * BigInt(batch_size)
            return seed
        }

        // const init_seed = parseInt(sd_tab_store.data.seed)
        const init_seed = BigInt(sd_tab_store.data.seed)

        const seed = calculateSeed(
            init_seed,
            g_current_batch_index,
            numberOfBatchSize
        )

        const use_sharp_mask = settings_tab_ts.store.data.extension_type
        const mask_blur = settings_tab_ts.store.data.use_sharp_mask
            ? 0
            : sd_tab_store.data.mask_blur
        const mask_expansion = sd_tab_store.data.mask_expansion

        const inpaint_full_res_padding =
            sd_tab_store.data.inpaint_full_res_padding

        // console.dir(numberOfImages)
        const bUsePromptShortcut =
            settings_tab_ts.store.data.use_prompt_shortcut
        let prompt_shortcut_ui_dict = {}
        try {
            let prompt_shortcut_string =
                document.getElementById('taPromptShortcut').value
            prompt_shortcut_ui_dict = JSON.parse(prompt_shortcut_string)
        } catch (e) {
            console.warn(
                `warning prompt_shortcut_ui_dict is not valid Json obj: ${e}`
            )
            prompt_shortcut_ui_dict = {}
        }

        // const slider_width = document.getElementById("slWidth").value
        // gWidth = getWidthFromSlider(slider_width)
        const original_width = sd_tab_store.data.width
        const original_height = sd_tab_store.data.height

        const width = general.nearestMultiple(original_width, 64)
        const height = general.nearestMultiple(original_height, 64)

        //const hScale = html_manip.getSliderSdValue_Old('hrScale',1)

        const uniqueDocumentId = await getUniqueDocumentId()
        const h_denoising_strength = sd_tab_store.data.hr_denoising_strength

        //Note: store the sampler names in json file if auto is offline or auto api is unmounted

        const sampler_name = sd_tab_store.data.sampler_name

        const mode = session_data?.rb_mode || sd_tab_store.data.rb_mode // Use the 'rb_mode' from the session, if not available, fallback to the 'rb_mode' from the interface.

        const b_restore_faces = sd_tab_store.data.restore_faces

        let denoising_strength = h_denoising_strength
        if (mode == 'inpaint' || mode == 'outpaint') {
            payload['inpaint_full_res'] = sd_tab_store.data.inpaint_full_res

            payload['inpaint_full_res_padding'] = inpaint_full_res_padding

            payload['inpainting_fill'] = sd_tab_store.data.inpainting_fill
            payload['mask_expansion'] = mask_expansion

            payload['mask'] = session_data?.mask
            payload['expanded_mask'] = session_data?.mask
            if (
                use_sharp_mask === false &&
                payload['mask'] &&
                mask_expansion > 0
            ) {
                //only if mask is available and sharp_mask is off
                // use blurry and expanded mask

                const expanded_mask = await py_re.maskExpansionRequest(
                    payload['mask'],
                    payload['mask_expansion'],
                    mask_blur
                )
                if (expanded_mask) {
                    payload['expanded_mask'] = expanded_mask
                    payload['mask'] = expanded_mask
                    session_store.data.expanded_mask = expanded_mask
                }
            }
            // viewer.store.mask = payload['mask'] // make sure
        }

        if (mode == 'img2img' || mode == 'inpaint' || mode == 'outpaint') {
            denoising_strength = sd_tab_store.data.denoising_strength
            payload['denoising_strength'] = denoising_strength

            payload['init_images'] = [session_data?.init_image]

            if (settings_tab_ts.store.data.use_image_cfg_scale_slider) {
                payload['image_cfg_scale'] = sd_tab_store.data.image_cfg_scale // we may need to check if model is pix2pix
            }

            if (
                scripts.script_store.isInstalled() &&
                scripts.script_store.is_active &&
                scripts.script_store.selected_script_name !== 'None' &&
                scripts.script_store.is_selected_script_available
            ) {
                payload['script_args'] = scripts.script_store.orderedValues()

                payload['script_name'] =
                    scripts.script_store.selected_script_name //'Ultimate SD upscale'
            }
        }

        function setAlwaysOnScripts() {
            const data = after_detailer_script.store.toJsFunc().data
            // console.log('setAlwaysOnScripts=> data:', data)
            let ad_controlnet_module = null
            if (data.controlnet_model?.includes('inpaint')) {
                ad_controlnet_module = 'inpaint_global_harmonious'
            }
            const alwayson_scripts = {
                ADetailer: {
                    args: [
                        data.is_enabled,
                        {
                            ad_model: data.ad_model,
                            ad_prompt: data.prompt,
                            ad_negative_prompt: data.negativePrompt,
                            ad_confidence: data.ad_conf,
                            ad_mask_min_ratio: 0.0,
                            ad_mask_max_ratio: 1.0,
                            ad_dilate_erode: 32,
                            ad_x_offset: 0,
                            ad_y_offset: 0,
                            ad_mask_merge_invert: 'None',
                            ad_mask_blur: 4,
                            ad_denoising_strength: 0.4,
                            ad_inpaint_only_masked: true,
                            ad_inpaint_only_masked_padding: 0,
                            ad_use_inpaint_width_height: false,
                            ad_inpaint_width: 512,
                            ad_inpaint_height: 512,
                            ad_use_steps: true,
                            ad_steps: 28,
                            ad_use_cfg_scale: false,
                            ad_cfg_scale: 7.0,
                            ad_use_sampler: false,
                            ad_sampler: sampler_name, //use the current sd sampler
                            ad_use_noise_multiplier: false,
                            ad_noise_multiplier: 1.0,
                            ad_restore_face: false,

                            ad_controlnet_model: data.controlnet_model,
                            // ad_controlnet_module: data.controlnet_module,
                            ad_controlnet_module: ad_controlnet_module,
                            ad_controlnet_weight: data.controlNetWeight,
                            ad_controlnet_guidance_start: 0.0,
                            ad_controlnet_guidance_end: 1.0,
                        },
                    ],
                },
            }
            if (!data?.is_installed) {
                delete alwayson_scripts['ADetailer']
            }
            return alwayson_scripts
        }

        const alwyason_scripts = setAlwaysOnScripts()
        payload['alwayson_scripts'] = {
            ...(payload['alwayson_scripts'] || {}),
            ...alwyason_scripts,
        }

        if (hi_res_fix && width >= 512 && height >= 512) {
            payload['enable_hr'] = hi_res_fix
            payload['hr_scale'] = sd_tab_store.data.hr_scale // Scale
            payload['hr_upscaler'] = upscaler // Upscaler
            payload['hr_second_pass_steps'] =
                sd_tab_store.data.hr_second_pass_steps
        }

        const backend_type = html_manip.getBackendType()
        if (backend_type === backendTypeEnum['Auto1111HordeExtension']) {
            payload['script_name'] = script_horde.script_name
            payload['script_args'] = script_horde.getScriptArgs()
        } else if (
            payload['script_name'] === script_horde.script_name &&
            backend_type !== backendTypeEnum['Auto1111HordeExtension']
        ) {
            delete payload['script_name']
            delete payload['script_args']
        }

        if (bUsePromptShortcut) {
            //replace the prompt with the prompt shortcut equivalent
            const [new_prompt, new_negative_prompt] =
                py_re.replacePromptsWithShortcuts(
                    prompt,
                    negative_prompt,
                    prompt_shortcut_ui_dict
                )

            //used in generation
            payload['prompt'] = new_prompt
            payload['negative_prompt'] = new_negative_prompt

            //used to when resote settings from metadata
            payload['original_prompt'] = prompt
            payload['original_negative_prompt'] = negative_prompt
        } else {
            //use the same prompt as in the prompt textarea
            payload['prompt'] = prompt
            payload['negative_prompt'] = negative_prompt

            payload['original_prompt'] = prompt
            payload['original_negative_prompt'] = negative_prompt
        }

        payload = {
            ...payload,
            // prompt: prompt,
            // negative_prompt: negative_prompt,
            steps: sd_tab_store.data.steps,
            // n_iter: numberOfImages,
            sampler_index: sampler_name,
            width: width,
            height: height,
            denoising_strength: denoising_strength,
            batch_size: numberOfBatchSize,
            cfg_scale: cfg_scale,
            seed: seed.toString(),
            mask_blur: 4, //mask_blur, // don't use auto1111 blur, instead use Auto-Photoshop-SD blur
            use_sharp_mask: use_sharp_mask,
            use_prompt_shortcut: bUsePromptShortcut,
            prompt_shortcut_ui_dict: prompt_shortcut_ui_dict,
            uniqueDocumentId: uniqueDocumentId,
            mode: mode,
            restore_faces: b_restore_faces,
            tiling: sd_tab_store.data.tiling,
            // script_args: script_args,
            // script_name:"Run on Stable Horde"
        }
    } catch (e) {
        console.error(e)
    }
    return payload
}

module.exports = {
    GenerationSession,
    GarbageCollectionState,
    SessionState,
    getSettings,
}
