// TODO: execAsync(['identify', '-format', '{"w":%w,"h":%h}', imagePath])
// to detect img dimensions

const { Gdk, GdkPixbuf, Gio, GLib, Gtk } = imports.gi;
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { Box, Button, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { fileExists } from '../../.miscutils/files.js';
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import { MarginRevealer } from '../../.widgethacks/advancedrevealers.js';
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
import WaifuService from '../../../services/waifus.js';
import { darkMode } from '../../.miscutils/system.js';

async function getImageViewerApp(preferredApp) {
    Utils.execAsync(['bash', '-c', `command -v ${preferredApp}`])
        .then((output) => {
            if (output != '') return preferredApp;
            else return 'xdg-open';
        })
        .catch(print);
}

const IMAGE_REVEAL_DELAY = 13; // Some wait for inits n other weird stuff
const IMAGE_VIEWER_APP = getImageViewerApp(userOptions.apps.imageViewer); // Gnome's image viewer cuz very comfortable zooming
const USER_CACHE_DIR = GLib.get_user_cache_dir();

// Create cache folder and clear pics from previous session
Utils.exec(`bash -c 'mkdir -p ${USER_CACHE_DIR}/ags/media/waifus'`);
Utils.exec(`bash -c 'rm ${USER_CACHE_DIR}/ags/media/waifus/*'`);

const CommandButton = (command) => Button({
    className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
    onClicked: () => sendMessage(command),
    setup: setupCursorHover,
    label: command,
});

export const waifuTabIcon = Box({
    hpack: 'center',
    children: [
        MaterialIcon('photo', 'norm'),
    ]
});

const WaifuInfo = () => {
    const waifuLogo = Label({
        hpack: 'center',
        className: 'sidebar-chat-welcome-logo',
        label: 'photo',
    })
    return Box({
        vertical: true,
        vexpand: true,
        className: 'spacing-v-15',
        children: [
            waifuLogo,
            Label({
                className: 'txt txt-title-small sidebar-chat-welcome-txt',
                wrap: true,
                justify: Gtk.Justification.CENTER,
                label: 'Waifus',
            }),
            Box({
                className: 'spacing-h-5',
                hpack: 'center',
                children: [
                    Label({
                        className: 'txt-smallie txt-subtext',
                        wrap: true,
                        justify: Gtk.Justification.CENTER,
                        label: getString('Powered by waifu.im + other APIs'),
                    }),
                    Button({
                        className: 'txt-subtext txt-norm icon-material',
                        label: 'info',
                        tooltipText: getString('Type tags for a random pic.\nNSFW content will not be returned unless\nyou explicitly request such a tag.\n\nDisclaimer: Not affiliated with the providers\nnor responsible for any of their content.'),
                        setup: setupCursorHoverInfo,
                    }),
                ]
            }),
        ]
    });
}

const waifuWelcome = Box({
    vexpand: true,
    homogeneous: true,
    child: Box({
        className: 'spacing-v-15 margin-top-15 margin-bottom-15',
        vpack: 'center',
        vertical: true,
        children: [
            WaifuInfo(),
        ]
    })
});

const WaifuImage = (taglist) => {
    const ImageState = (icon, name) => Box({
        className: 'spacing-h-5 txt',
        children: [
            Box({ hexpand: true }),
            Label({
                className: 'sidebar-waifu-txt txt-smallie',
                xalign: 0,
                label: name,
            }),
            MaterialIcon(icon, 'norm'),
        ]
    })
    const ImageAction = ({ name, icon, action }) => Button({
        className: 'sidebar-waifu-image-action txt-norm icon-material',
        tooltipText: name,
        label: icon,
        onClicked: action,
        setup: setupCursorHover,
    })
    const downloadState = Stack({
        homogeneous: false,
        transition: 'slide_up_down',
        transitionDuration: userOptions.animations.durationSmall,
        children: {
            'api': ImageState('api', getString('Calling API')),
            'download': ImageState('downloading', getString('Downloading image')),
            'done': ImageState('done', getString('Finished!')),
            'error': ImageState('error', getString('Error')),
            'notfound': ImageState('error', getString('Not found!')),
        },
    });
    const downloadIndicator = MarginRevealer({
        vpack: 'center',
        transition: 'slide_left',
        revealChild: true,
        child: downloadState,
    });
    const blockHeading = Box({
        hpack: 'fill',
        className: 'spacing-h-5',
        children: [
            ...taglist.map((tag) => CommandButton(tag)),
            Box({ hexpand: true }),
            downloadIndicator,
        ]
    });
    const blockImageActions = Revealer({
        transition: 'crossfade',
        revealChild: false,
        child: Box({
            vertical: true,
            children: [
                Box({
                    className: 'sidebar-waifu-image-actions spacing-h-3',
                    children: [
                        Box({ hexpand: true }),
                        ImageAction({
                            name: getString('Go to source'),
                            icon: 'link',
                            action: () => execAsync(['xdg-open', `${thisBlock.attribute.imageData.source}`]).catch(print),
                        }),
                        ImageAction({
                            name: getString('Hoard'),
                            icon: 'save',
                            action: (self) => {
                                execAsync(['bash', '-c', `mkdir -p $(xdg-user-dir PICTURES)/homework${thisBlock.attribute.isNsfw ? '/🌶️' : ''} && cp ${thisBlock.attribute.imagePath} $(xdg-user-dir PICTURES)/homework${thisBlock.attribute.isNsfw ? '/🌶️/' : ''}`])
                                    .then(() => self.label = 'done')
                                    .catch(print);
                            },
                        }),
                        ImageAction({
                            name: getString('Open externally'),
                            icon: 'open_in_new',
                            action: () => execAsync([IMAGE_VIEWER_APP, `${thisBlock.attribute.imagePath}`]).catch(print),
                        }),
                    ]
                })
            ],
        })
    })
    const blockImage = Widget.DrawingArea({
        className: 'sidebar-waifu-image',
    });
    const blockImageRevealer = Revealer({
        transition: 'slide_down',
        transitionDuration: userOptions.animations.durationLarge,
        revealChild: false,
        child: Box({
            className: 'margin-top-5',
            children: [Overlay({
                child: Box({
                    homogeneous: true,
                    className: 'sidebar-waifu-image',
                    children: [blockImage],
                }),
                overlays: [blockImageActions],
            })]
        }),
    });
    const thisBlock = Box({
        className: 'sidebar-chat-message',
        attribute: {
            'imagePath': '',
            'isNsfw': false,
            'imageData': '',
            'update': (imageData, force = false) => {
                thisBlock.attribute.imageData = imageData;
                const { status, signature, url, extension, source, dominant_color, is_nsfw, width, height, tags } = thisBlock.attribute.imageData;
                thisBlock.attribute.isNsfw = is_nsfw;
                if (status == 404) {
                    downloadState.shown = 'notfound';
                    return;
                }
                if (status != 200) {
                    downloadState.shown = 'error';
                    return;
                }
                thisBlock.attribute.imagePath = `${USER_CACHE_DIR}/ags/media/waifus/${signature}${extension}`;
                downloadState.shown = 'download';
                // Width/height
                const widgetWidth = Math.min(Math.floor(waifuContent.get_allocated_width() * 0.85), width);
                const widgetHeight = Math.ceil(widgetWidth * height / width);
                blockImage.set_size_request(widgetWidth, widgetHeight);
                const showImage = () => {
                    downloadState.shown = 'done';
                    const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(thisBlock.attribute.imagePath, widgetWidth, widgetHeight);
                    // const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false);

                    blockImage.set_size_request(widgetWidth, widgetHeight);
                    blockImage.connect("draw", (widget, cr) => {
                        const borderRadius = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);

                        // Draw a rounded rectangle
                        cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
                        cr.arc(widgetWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
                        cr.arc(widgetWidth - borderRadius, widgetHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
                        cr.arc(borderRadius, widgetHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
                        cr.closePath();
                        cr.clip();

                        // Paint image as bg
                        Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
                        cr.paint();
                    });

                    // Reveal stuff
                    Utils.timeout(IMAGE_REVEAL_DELAY, () => {
                        blockImageRevealer.revealChild = true;
                    })
                    Utils.timeout(IMAGE_REVEAL_DELAY + blockImageRevealer.transitionDuration,
                        () => blockImageActions.revealChild = true
                    );
                    downloadIndicator.attribute.hide();
                }
                // Show
                if (!force && fileExists(thisBlock.attribute.imagePath)) showImage();
                else Utils.execAsync(['bash', '-c', `wget -O '${thisBlock.attribute.imagePath}' '${url}'`])
                    .then(showImage)
                    .catch(print);
                thisBlock.css = `background-color: mix(${darkMode.value ? 'black' : 'white'}, ${dominant_color}, 0.97);`;
            },
        },
        children: [
            Box({
                vertical: true,
                children: [
                    blockHeading,
                    Box({
                        vertical: true,
                        hpack: 'start',
                        children: [blockImageRevealer],
                    })
                ]
            })
        ],
    });
    return thisBlock;
}

const waifuContent = Box({
    className: 'spacing-v-15',
    vertical: true,
    attribute: {
        'map': new Map(),
    },
    setup: (self) => self
        .hook(WaifuService, (box, id) => {
            if (id === undefined) return;
            const newImageBlock = WaifuImage(WaifuService.queries[id]);
            box.add(newImageBlock);
            box.show_all();
            box.attribute.map.set(id, newImageBlock);
        }, 'newResponse')
        .hook(WaifuService, (box, id) => {
            if (id === undefined) return;
            const data = WaifuService.responses[id];
            if (!data) return;
            const imageBlock = box.attribute.map.get(id);
            imageBlock?.attribute.update(data);
        }, 'updateResponse')
    ,
});

export const WaifuView = (chatEntry) => Scrollable({
    className: 'sidebar-chat-viewport',
    vexpand: true,
    child: Box({
        vertical: true,
        children: [
            waifuWelcome,
            waifuContent,
        ]
    }),
    setup: (scrolledWindow) => {
        // Show scrollbar
        scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
        const vScrollbar = scrolledWindow.get_vscrollbar();
        vScrollbar.get_style_context().add_class('sidebar-scrollbar');
        // Avoid click-to-scroll-widget-to-view behavior
        Utils.timeout(1, () => {
            const viewport = scrolledWindow.child;
            viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
        })
        // Always scroll to bottom with new content
        const adjustment = scrolledWindow.get_vadjustment();
        adjustment.connect("changed", () => {
            if (!chatEntry.hasFocus) return;
            adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
        })
    }
});

const waifuTags = Revealer({
    revealChild: false,
    transition: 'crossfade',
    transitionDuration: userOptions.animations.durationLarge,
    child: Box({
        className: 'spacing-h-5',
        children: [
            Scrollable({
                vscroll: 'never',
                hscroll: 'automatic',
                hexpand: true,
                child: Box({
                    className: 'spacing-h-5',
                    children: [
                        CommandButton('waifu'),
                        CommandButton('maid'),
                        CommandButton('uniform'),
                        CommandButton('oppai'),
                        CommandButton('selfies'),
                        CommandButton('marin-kitagawa'),
                        CommandButton('raiden-shogun'),
                        CommandButton('mori-calliope'),
                    ]
                })
            }),
            Box({ className: 'separator-line' }),
        ]
    })
});

export const waifuCommands = Box({
    className: 'spacing-h-5',
    setup: (self) => {
        self.pack_end(CommandButton('/clear'), false, false, 0);
        self.pack_start(Button({
            className: 'sidebar-chat-chip-toggle',
            setup: setupCursorHover,
            label: getString('Tags →'),
            onClicked: () => {
                waifuTags.revealChild = !waifuTags.revealChild;
            }
        }), false, false, 0);
        self.pack_start(waifuTags, true, true, 0);
    }
});

const clearChat = () => { // destroy!!
    waifuContent.attribute.map.forEach((value, key, map) => {
        value.destroy();
        value = null;
    });
}

function newSimpleImageCall(name, url, width, height, dominantColor = '#9392A6') {
    const timeSinceEpoch = Date.now();
    const newImage = WaifuImage([`/${name}`]);
    waifuContent.add(newImage);
    waifuContent.attribute.map.set(timeSinceEpoch, newImage);
    Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage?.attribute.update({
        status: 200,
        url: url,
        extension: '',
        signature: timeSinceEpoch,
        source: url,
        dominant_color: dominantColor,
        is_nsfw: false,
        width: width,
        height: height,
        tags: [`/${name}`],
    }, true));
}

export const sendMessage = (text) => {
    // Commands
    if (text.startsWith('/')) {
        if (text.startsWith('/clear')) clearChat();
        else if (text.startsWith('/test'))
            newSimpleImageCall('test', 'https://picsum.photos/600/400', 300, 200);
        else if (text.startsWith('/chino'))
            newSimpleImageCall('chino', 'https://chino.pages.dev/chino', 300, 400, '#B2AEF3');
        else if (text.startsWith('/place'))
            newSimpleImageCall('place', 'https://placewaifu.com/image/400/600', 400, 600, '#F0A235');

    }
    else WaifuService.fetch(text);
}
