package cz.kamma.kfmanager.ui;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

public class PropertiesDialog extends JDialog {
    private final File file;
    private final Path path;
    
    // Attributes Tab components
    private JCheckBox[][] permChecks; // [Owner, Group, Other][Read, Write, Execute]
    private JTextField octalField;
    private JTextField symbolicField;
    private JCheckBox suidCheck, sgidCheck, stickyCheck;
    private JCheckBox recursiveCheck;
    private JTextField ownerField, groupField;

    public PropertiesDialog(Window owner, File file) {
        super(owner, "Properties", ModalityType.APPLICATION_MODAL);
        this.file = file;
        this.path = file.toPath();
        
        initComponents();
        
        pack();
        setMinimumSize(new Dimension(500, 550));
        setLocationRelativeTo(owner);
    }

    private void initComponents() {
        setLayout(new BorderLayout());

        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.addTab("Properties", buildPropertiesPanel());
        tabbedPane.addTab("Attributes", buildAttributesPanel());

        add(tabbedPane, BorderLayout.CENTER);

        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        JButton okButton = new JButton("OK");
        JButton cancelButton = new JButton("Cancel");

        okButton.addActionListener(e -> {
            if (applyChanges()) {
                dispose();
            }
        });
        cancelButton.addActionListener(e -> dispose());

        buttonPanel.add(okButton);
        buttonPanel.add(cancelButton);
        add(buttonPanel, BorderLayout.SOUTH);
    }

    private JPanel buildPropertiesPanel() {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBorder(new EmptyBorder(20, 20, 20, 20));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 5, 5, 5);

        int row = 0;
        addInfoRow(panel, gbc, row++, "Name:", file.getName());
        addInfoRow(panel, gbc, row++, "Path:", file.getAbsolutePath());
        
        String sizeStr = file.isDirectory() ? "Directory" : formatSize(file.length());
        addInfoRow(panel, gbc, row++, "Size:", sizeStr);
        
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        addInfoRow(panel, gbc, row++, "Modified:", sdf.format(new Date(file.lastModified())));

        return panel;
    }

    private JPanel buildAttributesPanel() {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBorder(new EmptyBorder(15, 15, 15, 15));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 5, 5, 5);

        int row = 0;

        // File name display
        JLabel nameLabel = new JLabel("File name");
        gbc.gridx = 0; gbc.gridy = row; gbc.weightx = 0;
        panel.add(nameLabel, gbc);
        
        JLabel fileNameValue = new JLabel(file.getName());
        gbc.gridx = 1; gbc.gridy = row; gbc.weightx = 1; gbc.gridwidth = 3;
        panel.add(fileNameValue, gbc);
        gbc.gridwidth = 1;
        row++;

        panel.add(new JSeparator(), new GridBagConstraints(0, row++, 4, 1, 1.0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 10, 0), 0, 0));

        // Header: Read, Write, Execute
        gbc.gridy = row;
        gbc.weightx = 0;
        String[] titles = {"", "Read", "Write", "Execute"};
        for (int i = 1; i < titles.length; i++) {
            gbc.gridx = i;
            panel.add(new JLabel(titles[i], SwingConstants.CENTER), gbc);
        }
        row++;

        // Rows: Owner, Group, Other
        String[] labels = {"Owner", "Group", "Other"};
        permChecks = new JCheckBox[3][3];
        
        Set<PosixFilePermission> perms = new HashSet<>();
        String ownerName = "";
        String groupName = "";
        try {
            PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class);
            perms = attrs.permissions();
            ownerName = attrs.owner().getName();
            groupName = attrs.group().getName();
        } catch (Exception ignore) {
            // Fallback for non-posix systems or permission issues
            if (file.canRead()) perms.add(PosixFilePermission.OWNER_READ);
            if (file.canWrite()) perms.add(PosixFilePermission.OWNER_WRITE);
            if (file.canExecute()) perms.add(PosixFilePermission.OWNER_EXECUTE);
        }

        for (int i = 0; i < 3; i++) {
            gbc.gridx = 0; gbc.gridy = row;
            panel.add(new JLabel(labels[i]), gbc);
            
            for (int j = 0; j < 3; j++) {
                permChecks[i][j] = new JCheckBox();
                permChecks[i][j].setHorizontalAlignment(SwingConstants.CENTER);
                gbc.gridx = j + 1;
                panel.add(permChecks[i][j], gbc);
                
                // Set initial state
                permChecks[i][j].setSelected(hasPermission(perms, i, j));
                permChecks[i][j].addActionListener(e -> updateOctalAndSymbolic());
            }
            row++;
        }

        panel.add(new JSeparator(), new GridBagConstraints(0, row++, 4, 1, 1.0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 10, 0), 0, 0));

        // Special Bits
        JPanel bitsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 0));
        bitsPanel.add(new JLabel("Bits:"));
        suidCheck = new JCheckBox("SUID");
        sgidCheck = new JCheckBox("SGID");
        stickyCheck = new JCheckBox("Sticky");
        suidCheck.setEnabled(false); // Advanced bits usually require more than just Java basic API or are rare
        sgidCheck.setEnabled(false);
        stickyCheck.setEnabled(false);
        bitsPanel.add(suidCheck);
        bitsPanel.add(sgidCheck);
        bitsPanel.add(stickyCheck);
        
        gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 4;
        panel.add(bitsPanel, gbc);
        gbc.gridwidth = 1;

        // Octal and Symbolic
        gbc.gridy = row;
        gbc.gridx = 0;
        panel.add(new JLabel("Octal:"), gbc);
        
        octalField = new JTextField(5);
        gbc.gridx = 1;
        panel.add(octalField, gbc);
        
        gbc.gridx = 2;
        panel.add(new JLabel("Text:"), gbc);
        
        symbolicField = new JTextField(12);
        symbolicField.setEditable(false);
        gbc.gridx = 3;
        panel.add(symbolicField, gbc);
        row++;

        // Owner/Group Section
        JPanel ownerGroupPanel = new JPanel(new GridBagLayout());
        ownerGroupPanel.setBorder(BorderFactory.createTitledBorder("Owner"));
        GridBagConstraints ogbc = new GridBagConstraints();
        ogbc.fill = GridBagConstraints.HORIZONTAL;
        ogbc.insets = new Insets(2, 5, 2, 5);
        
        ogbc.gridx = 0; ogbc.gridy = 0;
        ownerGroupPanel.add(new JLabel("Owner"), ogbc);
        ownerField = new JTextField(ownerName);
        ownerField.setEditable(false);
        ogbc.gridx = 1; ogbc.weightx = 1.0;
        ownerGroupPanel.add(ownerField, ogbc);
        
        ogbc.gridx = 0; ogbc.gridy = 1;
        ownerGroupPanel.add(new JLabel("Group"), ogbc);
        groupField = new JTextField(groupName);
        groupField.setEditable(false);
        ogbc.gridx = 1;
        ownerGroupPanel.add(groupField, ogbc);
        
        gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 4;
        panel.add(ownerGroupPanel, gbc);
        gbc.gridwidth = 1;

        // Recursive
        recursiveCheck = new JCheckBox("Recursive");
        recursiveCheck.setEnabled(file.isDirectory());
        gbc.gridx = 0; gbc.gridy = row++; gbc.gridwidth = 4;
        panel.add(recursiveCheck, gbc);

        updateOctalAndSymbolic();
        
        // Listener for octal field to update checkboxes
        octalField.addActionListener(e -> updateFromOctal());

        return panel;
    }

    private boolean hasPermission(Set<PosixFilePermission> perms, int userIdx, int permIdx) {
        PosixFilePermission p = getPermission(userIdx, permIdx);
        return p != null && perms.contains(p);
    }

    private PosixFilePermission getPermission(int userIdx, int permIdx) {
        if (userIdx == 0) { // Owner
            if (permIdx == 0) return PosixFilePermission.OWNER_READ;
            if (permIdx == 1) return PosixFilePermission.OWNER_WRITE;
            if (permIdx == 2) return PosixFilePermission.OWNER_EXECUTE;
        } else if (userIdx == 1) { // Group
            if (permIdx == 0) return PosixFilePermission.GROUP_READ;
            if (permIdx == 1) return PosixFilePermission.GROUP_WRITE;
            if (permIdx == 2) return PosixFilePermission.GROUP_EXECUTE;
        } else if (userIdx == 2) { // Other
            if (permIdx == 0) return PosixFilePermission.OTHERS_READ;
            if (permIdx == 1) return PosixFilePermission.OTHERS_WRITE;
            if (permIdx == 2) return PosixFilePermission.OTHERS_EXECUTE;
        }
        return null;
    }

    private void updateOctalAndSymbolic() {
        int octal = 0;
        if (permChecks[0][0].isSelected()) octal += 0400;
        if (permChecks[0][1].isSelected()) octal += 0200;
        if (permChecks[0][2].isSelected()) octal += 0100;
        if (permChecks[1][0].isSelected()) octal += 0040;
        if (permChecks[1][1].isSelected()) octal += 0020;
        if (permChecks[1][2].isSelected()) octal += 0010;
        if (permChecks[2][0].isSelected()) octal += 0004;
        if (permChecks[2][1].isSelected()) octal += 0002;
        if (permChecks[2][2].isSelected()) octal += 0001;

        octalField.setText(String.format("%03o", octal));
        
        StringBuilder sb = new StringBuilder();
        sb.append(file.isDirectory() ? 'd' : '-');
        sb.append(permChecks[0][0].isSelected() ? 'r' : '-');
        sb.append(permChecks[0][1].isSelected() ? 'w' : '-');
        sb.append(permChecks[0][2].isSelected() ? 'x' : '-');
        sb.append(permChecks[1][0].isSelected() ? 'r' : '-');
        sb.append(permChecks[1][1].isSelected() ? 'w' : '-');
        sb.append(permChecks[1][2].isSelected() ? 'x' : '-');
        sb.append(permChecks[2][0].isSelected() ? 'r' : '-');
        sb.append(permChecks[2][1].isSelected() ? 'w' : '-');
        sb.append(permChecks[2][2].isSelected() ? 'x' : '-');
        symbolicField.setText(sb.toString());
    }

    private void updateFromOctal() {
        try {
            int val = Integer.parseInt(octalField.getText(), 8);
            permChecks[0][0].setSelected((val & 0400) != 0);
            permChecks[0][1].setSelected((val & 0200) != 0);
            permChecks[0][2].setSelected((val & 0100) != 0);
            permChecks[1][0].setSelected((val & 0040) != 0);
            permChecks[1][1].setSelected((val & 0020) != 0);
            permChecks[1][2].setSelected((val & 0010) != 0);
            permChecks[2][0].setSelected((val & 0004) != 0);
            permChecks[2][1].setSelected((val & 0002) != 0);
            permChecks[2][2].setSelected((val & 0001) != 0);
            updateOctalAndSymbolic();
        } catch (Exception ignore) {}
    }

    private boolean applyChanges() {
        Set<PosixFilePermission> perms = new HashSet<>();
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (permChecks[i][j].isSelected()) {
                    perms.add(getPermission(i, j));
                }
            }
        }

        try {
            applyPermissions(path, perms, recursiveCheck.isSelected());
            return true;
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Failed to apply permissions: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }
    }

    private void applyPermissions(Path p, Set<PosixFilePermission> perms, boolean recursive) throws IOException {
        try {
            Files.setPosixFilePermissions(p, perms);
        } catch (UnsupportedOperationException e) {
            // Fallback for non-posix filesystems
            File f = p.toFile();
            f.setReadable(perms.contains(PosixFilePermission.OWNER_READ));
            f.setWritable(perms.contains(PosixFilePermission.OWNER_WRITE));
            f.setExecutable(perms.contains(PosixFilePermission.OWNER_EXECUTE));
        }
        
        if (recursive && Files.isDirectory(p)) {
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
                for (Path entry : stream) {
                    applyPermissions(entry, perms, true);
                }
            }
        }
    }

    private void addInfoRow(JPanel panel, GridBagConstraints gbc, int row, String label, String value) {
        gbc.gridx = 0; gbc.gridy = row; gbc.weightx = 0;
        panel.add(new JLabel(label), gbc);
        gbc.gridx = 1; gbc.weightx = 1.0;
        JTextField textField = new JTextField(value);
        textField.setEditable(false);
        textField.setBorder(null);
        textField.setOpaque(false);
        panel.add(textField, gbc);
    }

    private String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        char pre = "KMGTPE".charAt(exp - 1);
        return String.format("%.1f %cB (%d bytes)", bytes / Math.pow(1024, exp), pre, bytes);
    }
}
