/*
 * Copyright (C) 2025-2026  Tim Zimmermann <tim@aglais.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.aglais.zms.ui.tab.settings;

import org.aglais.zms.AglaisZMS;
import org.aglais.zms.db.core.BackupRestoreResult;
import org.aglais.zms.db.core.IBackupListener;
import org.aglais.zms.db.core.model.AncestryData;
import org.aglais.zms.i18n.InterpolatingResourceBundle;
import org.aglais.zms.ui.component.DangerButton;
import org.aglais.zms.ui.dialog.FileChooser;
import org.aglais.zms.ui.tab.SettingsTab;
import org.aglais.zms.ui.tab.SubTab;
import org.aglais.zms.util.Log;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;

import static org.aglais.zms.api.core.APIConstants.PROJECT_NAME_NO_SPACES;

/**
 * Tab for managing backup/restore
 */
public class SettingsBackupTab extends SubTab<SettingsTab> {

    private static final String LOG_TAG = SettingsBackupTab.class.getSimpleName();

    private JPanel contentPane;
    private JButton backupButton;
    private DangerButton restoreButton;

    /**
     * Constructs a new {@link SettingsBackupTab}
     *
     * @param zms    The main application instance
     * @param parent The parent {@link SettingsTab} containing this sub-tab
     */
    public SettingsBackupTab(AglaisZMS zms, SettingsTab parent) {
        super(zms, parent);

        InterpolatingResourceBundle uiBundle = (InterpolatingResourceBundle) ResourceBundle.getBundle("ui");

        backupButton.addActionListener((e) -> {
            FileChooser fileChooser = new FileChooser();

            // Set default backup filename
            String filename = String.format("%s-%tF.bak", PROJECT_NAME_NO_SPACES, new Date());
            fileChooser.setSelectedFile(new File(filename));

            int result = fileChooser.showSaveDialog(contentPane);
            if (result == JFileChooser.APPROVE_OPTION) {
                File file = fileChooser.getSelectedFile();

                try (FileOutputStream out = new FileOutputStream(file)) {
                    zms.getClient().backup(out);
                    JOptionPane.showMessageDialog(contentPane, uiBundle.getString("backup_finished"),
                            uiBundle.getString("info"), JOptionPane.INFORMATION_MESSAGE);
                } catch (IOException | NoSuchAlgorithmException ex) {
                    Log.e(LOG_TAG, "Failed to create backup: " + file, ex);
                    JOptionPane.showMessageDialog(contentPane, uiBundle.getString("backup_failed"),
                            uiBundle.getString("error"), JOptionPane.ERROR_MESSAGE);
                }
            }
        });

        restoreButton.addActionListener((e) -> {
            FileChooser fileChooser = new FileChooser();

            int result = fileChooser.showOpenDialog(contentPane);
            if (result == JFileChooser.APPROVE_OPTION) {
                File file = fileChooser.getSelectedFile();

                try (FileInputStream in = new FileInputStream(file)) {
                    BackupRestoreResult restoreResult = zms.getClient().restore(in,
                            backupInfo -> showBackupInfo(file, backupInfo, uiBundle));
                    switch (restoreResult) {
                        case OK:
                            JOptionPane.showMessageDialog(contentPane, uiBundle.getString("restore_finished"),
                                    uiBundle.getString("info"), JOptionPane.INFORMATION_MESSAGE);
                            zms.restart();
                            break;
                        case ERROR_UNSUPPORTED_VERSION:
                            JOptionPane.showMessageDialog(contentPane,
                                    uiBundle.getString("restore_unsupported_version"),
                                    uiBundle.getString("error"), JOptionPane.ERROR_MESSAGE);
                            break;
                        case ERROR_CANCELLED:
                            JOptionPane.showMessageDialog(contentPane, uiBundle.getString("restore_cancelled"),
                                    uiBundle.getString("info"), JOptionPane.INFORMATION_MESSAGE);
                            break;
                        case ERROR_HASH_MISMATCH:
                            JOptionPane.showMessageDialog(contentPane, uiBundle.getString("restore_hash_mismatch"),
                                    uiBundle.getString("error"), JOptionPane.ERROR_MESSAGE);
                            break;
                        default:
                            JOptionPane.showMessageDialog(contentPane, uiBundle.getString("restore_failed"),
                                    uiBundle.getString("error"), JOptionPane.ERROR_MESSAGE);
                            break;
                    }
                } catch (IOException | ClassNotFoundException | NoSuchAlgorithmException ex) {
                    Log.e(LOG_TAG, "Failed to restore backup: " + file, ex);
                    JOptionPane.showMessageDialog(contentPane, uiBundle.getString("restore_failed"),
                            uiBundle.getString("error"), JOptionPane.ERROR_MESSAGE);
                }
            }
        });
    }

    /**
     * Show information about a backup that's going to be restored to a user.
     *
     * @param file       The backup file that is being restored
     * @param backupInfo The {@link IBackupListener.BackupInfo} loaded from the file
     * @param uiBundle   {@link InterpolatingResourceBundle} for loading translations
     * @return Always true, to continue loading the backup
     */
    private boolean showBackupInfo(File file, IBackupListener.BackupInfo backupInfo,
                                   InterpolatingResourceBundle uiBundle) {
        int ancestryDataCount = 0;
        for (List<AncestryData> ancestryData : backupInfo.ancestryMap().values())
            ancestryDataCount += ancestryData.size();

        String creationTime;
        if (backupInfo.timestamp() != 0)
            creationTime = String.format("%tF %tT", backupInfo.timestamp(), backupInfo.timestamp());
        else
            creationTime = uiBundle.getString("unknown");

        return JOptionPane.showOptionDialog(contentPane, uiBundle.getString("confirm_restore",
                        file.getName(), backupInfo.version(), creationTime, backupInfo.speciesList().size(),
                        ancestryDataCount, backupInfo.userList().size()),
                uiBundle.getString("dialog_confirm_restore_title"),
                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
                new String[]{uiBundle.getString("yes"), uiBundle.getString("no")},
                uiBundle.getString("no")) == JOptionPane.YES_OPTION;
    }

    @Override
    public void onShow() {
        contentPane.getRootPane().setDefaultButton(backupButton);
    }

    @Override
    public @NotNull JComponent getContentPane() {
        return contentPane;
    }
}
