package com.dynamicwallpaper.gui;

import com.dynamicwallpaper.core.NativeHelper;
import com.dynamicwallpaper.core.WallpaperBuilder;
import com.dynamicwallpaper.core.WallpaperInstaller;
import com.dynamicwallpaper.model.WallpaperImage;
import com.dynamicwallpaper.model.WallpaperMode;
import com.dynamicwallpaper.util.ImageValidation;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.io.File;
import java.util.List;

/** Top-level application window: mode switch, image editors, and the generate/install controls. */
public final class MainWindow extends JFrame {

    private final NativeHelper nativeHelper = new NativeHelper();
    private final WallpaperBuilder wallpaperBuilder = new WallpaperBuilder(nativeHelper);

    private final JComboBox<WallpaperMode> modeSelector = new JComboBox<>(WallpaperMode.values());
    private final CardLayout cardLayout = new CardLayout();
    private final JPanel modePanel = new JPanel(cardLayout);
    private final AppearancePanel appearancePanel = new AppearancePanel();
    private final SolarPanel solarPanel = new SolarPanel();

    private final JTextField outputPathField = new JTextField();
    private final JSpinner qualitySpinner = new JSpinner(new SpinnerNumberModel(0.9, 0.1, 1.0, 0.05));
    private final JButton generateButton = new JButton("Generate Wallpaper");
    private final JButton setWallpaperButton = new JButton("Set as Desktop Picture");
    private final JProgressBar progressBar = new JProgressBar();
    private final JTextArea logArea = new JTextArea(8, 60);

    private File lastGeneratedFile;

    public MainWindow() {
        super("Dynamic Wallpaper Creator");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        add(buildHeader(), BorderLayout.NORTH);
        add(modePanel, BorderLayout.CENTER);
        add(buildFooter(), BorderLayout.SOUTH);

        modePanel.add(appearancePanel, WallpaperMode.APPEARANCE.name());
        modePanel.add(solarPanel, WallpaperMode.SOLAR.name());
        modeSelector.addActionListener(e -> cardLayout.show(modePanel, currentMode().name()));

        setWallpaperButton.setEnabled(false);
        setWallpaperButton.addActionListener(e -> setAsDesktopPicture());
        generateButton.addActionListener(e -> generate());

        setMinimumSize(new Dimension(760, 640));
        pack();
        setLocationRelativeTo(null);


        this.setSize(new Dimension(1200,800));
        this.setLocationRelativeTo(null); // Center on screen
    }

    private Component buildHeader() {
        JPanel header = new JPanel(new BorderLayout(8, 8));
        header.setBorder(BorderFactory.createEmptyBorder(12, 16, 0, 16));

        JLabel title = new JLabel("Choose a wallpaper type:");
        JPanel left = new JPanel(new FlowLayout(FlowLayout.LEFT));
        left.add(title);
        left.add(modeSelector);

        JLabel modeDescription = new JLabel(currentMode().description());
        modeSelector.addActionListener(e -> modeDescription.setText(currentMode().description()));
        modeDescription.setEnabled(false);

        header.add(left, BorderLayout.NORTH);
        header.add(modeDescription, BorderLayout.SOUTH);
        return header;
    }

    private Component buildFooter() {
        JPanel footer = new JPanel();
        footer.setLayout(new BorderLayout(8, 8));
        footer.setBorder(BorderFactory.createEmptyBorder(8, 16, 16, 16));

        JPanel outputRow = new JPanel(new BorderLayout(8, 8));
        outputRow.add(new JLabel("Output file:"), BorderLayout.WEST);
        outputPathField.setText(defaultOutputPath());
        outputRow.add(outputPathField, BorderLayout.CENTER);
        JButton browseButton = new JButton("Choose…");
        browseButton.addActionListener(e -> chooseOutputPath());
        outputRow.add(browseButton, BorderLayout.EAST);

        JPanel controlsRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
        controlsRow.add(new JLabel("Quality:"));
        qualitySpinner.setPreferredSize(new Dimension(70, qualitySpinner.getPreferredSize().height));
        controlsRow.add(qualitySpinner);
        controlsRow.add(generateButton);
        controlsRow.add(setWallpaperButton);

        JPanel top = new JPanel(new GridLayout(2, 1));
        top.add(outputRow);
        top.add(controlsRow);

        logArea.setEditable(false);
        logArea.setFont(new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 12));

        footer.add(top, BorderLayout.NORTH);
        footer.add(progressBar, BorderLayout.CENTER);
        footer.add(new JScrollPane(logArea), BorderLayout.SOUTH);
        return footer;
    }

    private WallpaperMode currentMode() {
        return (WallpaperMode) modeSelector.getSelectedItem();
    }

    private String defaultOutputPath() {
        String home = System.getProperty("user.home");
        return new File(home, "Desktop/MyDynamicWallpaper.heic").getAbsolutePath();
    }

    private void chooseOutputPath() {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle("Save Dynamic Wallpaper As");
        chooser.setFileFilter(new FileNameExtensionFilter("HEIC Image", "heic"));
        chooser.setSelectedFile(new File(outputPathField.getText()));
        if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            File selected = chooser.getSelectedFile();
            if (!selected.getName().toLowerCase().endsWith(".heic")) {
                selected = new File(selected.getParentFile(), selected.getName() + ".heic");
            }
            outputPathField.setText(selected.getAbsolutePath());
        }
    }

    private void generate() {
        File output = new File(outputPathField.getText().trim());
        if (output.getParentFile() == null || !output.getParentFile().isDirectory()) {
            JOptionPane.showMessageDialog(this, "Choose a valid output location first.",
                    "Missing output location", JOptionPane.WARNING_MESSAGE);
            return;
        }

        WallpaperMode mode = currentMode();
        double quality = (Double) qualitySpinner.getValue();

        if (mode == WallpaperMode.APPEARANCE) {
            File light = appearancePanel.lightImage();
            File dark = appearancePanel.darkImage();
            if (light == null || dark == null) {
                JOptionPane.showMessageDialog(this, "Choose both a light image and a dark image first.",
                        "Missing images", JOptionPane.WARNING_MESSAGE);
                return;
            }
            String warning = ImageValidation.checkConsistentDimensions(List.of(light, dark));
            if (warning != null && !confirmProceedDespiteWarning(warning)) {
                return;
            }
            runGenerateJob(output, listener -> wallpaperBuilder.buildAppearance(light, dark, output, quality, listener));
        } else {
            List<WallpaperImage> images = solarPanel.images();
            if (images.size() < 2) {
                JOptionPane.showMessageDialog(this, "Add at least two images for a sun-position wallpaper.",
                        "Not enough images", JOptionPane.WARNING_MESSAGE);
                return;
            }
            String warning = ImageValidation.checkConsistentDimensions(images.stream().map(WallpaperImage::file).toList());
            if (warning != null && !confirmProceedDespiteWarning(warning)) {
                return;
            }
            runGenerateJob(output, listener -> wallpaperBuilder.buildSolar(images, output, quality, listener));
        }
    }

    private boolean confirmProceedDespiteWarning(String warning) {
        int choice = JOptionPane.showConfirmDialog(this, warning + "\n\nContinue anyway?",
                "Image sizes differ", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
        return choice == JOptionPane.YES_OPTION;
    }

    @FunctionalInterface
    private interface GenerateJob {
        void run(WallpaperBuilder.ProgressListener listener) throws WallpaperBuilder.BuildException;
    }

    private void runGenerateJob(File output, GenerateJob job) {
        setBusy(true);
        logArea.setText("");
        setWallpaperButton.setEnabled(false);

        SwingWorker<Void, String> worker = new SwingWorker<>() {
            @Override
            protected Void doInBackground() throws Exception {
                job.run(line -> publish(line));
                return null;
            }

            @Override
            protected void process(List<String> chunks) {
                for (String line : chunks) {
                    logArea.append(line);
                    logArea.append("\n");
                }
            }

            @Override
            protected void done() {
                setBusy(false);
                try {
                    get();
                    logArea.append("Done. Saved to " + output.getAbsolutePath() + "\n");
                    lastGeneratedFile = output;
                    setWallpaperButton.setEnabled(true);
                } catch (Exception e) {
                    Throwable cause = e.getCause() != null ? e.getCause() : e;
                    logArea.append("Failed: " + cause.getMessage() + "\n");
                    JOptionPane.showMessageDialog(MainWindow.this, cause.getMessage(),
                            "Could not generate wallpaper", JOptionPane.ERROR_MESSAGE);
                }
            }
        };
        worker.execute();
    }

    private void setBusy(boolean busy) {
        generateButton.setEnabled(!busy);
        progressBar.setIndeterminate(busy);
    }

    private void setAsDesktopPicture() {
        if (lastGeneratedFile == null) {
            return;
        }
        SwingWorker<Void, Void> worker = new SwingWorker<>() {
            @Override
            protected Void doInBackground() throws Exception {
                WallpaperInstaller.setAsDesktopPicture(lastGeneratedFile);
                return null;
            }

            @Override
            protected void done() {
                try {
                    get();
                    logArea.append("Set as desktop picture.\n");
                } catch (Exception e) {
                    Throwable cause = e.getCause() != null ? e.getCause() : e;
                    JOptionPane.showMessageDialog(MainWindow.this, cause.getMessage(),
                            "Could not set desktop picture", JOptionPane.ERROR_MESSAGE);
                }
            }
        };
        worker.execute();
    }

}
