package dev.photosync.ui.screen;

import dev.photosync.core.upload.QueuedUpload;
import dev.photosync.mcapi.render.RenderBridge;
import dev.photosync.mcapi.screen.ScreenModel;
import dev.photosync.ui.Chrome;
import dev.photosync.ui.PhotoSyncUi;
import dev.photosync.ui.Rect;
import dev.photosync.ui.Theme;
import dev.photosync.ui.widget.Button;
import dev.photosync.ui.widget.WidgetList;

import java.util.List;

/**
 * The screen that stands between an in-flight upload and a closed game.
 *
 * <p>Quitting mid-upload is the one moment where the mod has to interrupt the
 * player, so it is worth being exact about what it offers. Waiting is the
 * default and needs no click: the dialog watches the queue and lets the game go
 * as soon as it empties. "Quit anyway" is always available and never destroys
 * anything -- the queue is on disk and resumes next launch -- so the button says
 * what happens next rather than warning about consequences that do not exist.
 *
 * <p>Escape does nothing here. It is the one modal in the mod: the game is
 * already on its way out, and a dialog that can be dismissed without answering
 * would leave the player in a session they have asked to end.
 */
public final class QuitDialog implements ScreenModel {

    private static final int PANEL_WIDTH = 260;
    private static final int PANEL_HEIGHT = 116;

    private final PhotoSyncUi ui;
    private final Chrome chrome;
    private final WidgetList widgets = new WidgetList();

    /** How many were outstanding when the dialog opened, so progress has a denominator. */
    private final int initialCount;

    private Rect panel = Rect.EMPTY;
    private Rect body = Rect.EMPTY;
    private boolean released;

    public QuitDialog(PhotoSyncUi ui) {
        this.ui = ui;
        this.chrome = ui.chrome();
        this.initialCount = Math.max(1, ui.core().queue().activeCount());
    }

    private Theme theme() {
        return chrome.theme();
    }

    @Override
    public String title() {
        return chrome.translate("photosync.quit.title");
    }

    @Override
    public void layout(int width, int height) {
        panel = new Rect((width - PANEL_WIDTH) / 2, (height - PANEL_HEIGHT) / 2, PANEL_WIDTH, PANEL_HEIGHT);
        body = panel.inset(theme().padding());

        widgets.clear();
        Rect actions = body.bottom(theme().controlHeight());
        int half = (actions.width() - theme().gap()) / 2;

        Button stay = widgets.add(new Button(chrome, chrome.translate("photosync.quit.keep_playing"), this::dismiss));
        stay.bounds(actions.left(half));

        Button quit = widgets.add(new Button(chrome, chrome.translate("photosync.quit.anyway"), this::release));
        quit.emphasized(Button.Emphasis.DANGER).bounds(actions.right(half));
    }

    @Override
    public void render(RenderBridge render, int mouseX, int mouseY) {
        chrome.scrim(render);
        chrome.panel(render, panel);

        int line = render.lineHeight();
        int y = body.y();
        chrome.label(render, title(), body.x(), y, theme().text());

        List<QueuedUpload> active = ui.core().queue().snapshot().stream()
                .filter(entry -> entry.job().state().isActive())
                .toList();
        y += line + 6;
        chrome.label(render, chrome.translate("photosync.quit.remaining", active.size()),
                body.x(), y, theme().textMuted());

        y += line + 4;
        Rect bar = new Rect(body.x(), y, body.width(), 4);
        // Overall progress, measured in files rather than bytes: the count is the
        // number the player was just shown, and mixing the two units would make
        // the bar disagree with the line above it.
        double done = (initialCount - active.size()) / (double) initialCount;
        chrome.progressBar(render, bar, done, theme().accent());

        y += 10;
        String current = active.isEmpty()
                ? chrome.translate("photosync.quit.finishing")
                : chrome.elide(render, active.get(0).job().fileName(), body.width());
        chrome.label(render, current, body.x(), y, theme().textFaint());

        y += line + 2;
        chrome.label(render, chrome.translate("photosync.quit.hint"), body.x(), y, theme().textFaint());

        widgets.render(render, mouseX, mouseY);
    }

    /**
     * Polled rather than driven by an upload event, because the interesting
     * transition is "the queue became empty" and a tick is the cheapest place to
     * notice it that is already on the render thread.
     */
    @Override
    public void tick() {
        widgets.tick();
        if (!ui.core().isBusy()) {
            release();
        }
    }

    /** Lets the shutdown proceed, exactly once. */
    private void release() {
        if (released) {
            return;
        }
        released = true;
        ui.quitNow();
    }

    private void dismiss() {
        ui.close();
    }

    @Override
    public boolean mouseClicked(double mouseX, double mouseY, int button) {
        return widgets.mouseClicked(mouseX, mouseY, button);
    }

    @Override
    public boolean mouseReleased(double mouseX, double mouseY, int button) {
        return widgets.mouseReleased(mouseX, mouseY, button);
    }

    @Override
    public boolean closeOnEscape() {
        return false;
    }
}
