import React from 'react';
import { Form, Formik } from 'formik';
import { object, string } from 'yup';
import { ServerContext } from '@/state/server';
import { chmodFiles } from '../api/server/files/index';
import { useFlashKey } from '../useFlash';
import { Dialog } from '@/components/elements/dialog';
import { Button } from '@/components/elements/button';
import Field from '@/components/elements/Field';
import Input from '@/components/elements/Input';
import GreyRowBox from '@/components/elements/GreyRowBox';

interface Props {
    visible: boolean;
    onDismiss: () => void;
    file?: string;
    files?: string[];
    initialMode?: string;
    onPermissionsChanged: () => Promise<void>;
}

type PermissionFlags = {
    ownerRead: boolean;
    ownerWrite: boolean;
    ownerExec: boolean;
    groupRead: boolean;
    groupWrite: boolean;
    groupExec: boolean;
    otherRead: boolean;
    otherWrite: boolean;
    otherExec: boolean;
};

const toFlagsFromMode = (mode: string): PermissionFlags => {
    const clean = mode.replace(/[^0-7]/g, '').slice(-3);
    const padded = clean.padStart(3, '0');
    const digits = padded.split('').map((d) => parseInt(d, 8));
    const [owner, group, other] = digits;

    return {
        ownerRead: (owner & 4) !== 0,
        ownerWrite: (owner & 2) !== 0,
        ownerExec: (owner & 1) !== 0,
        groupRead: (group & 4) !== 0,
        groupWrite: (group & 2) !== 0,
        groupExec: (group & 1) !== 0,
        otherRead: (other & 4) !== 0,
        otherWrite: (other & 2) !== 0,
        otherExec: (other & 1) !== 0,
    };
};

const normalizeMode = (mode?: string): string => {
    const clean = (mode || '').replace(/[^0-7]/g, '').slice(-3);
    return clean ? clean.padStart(3, '0') : '644';
};

const toModeFromFlags = (flags: PermissionFlags): string => {
    const owner = (flags.ownerRead ? 4 : 0) + (flags.ownerWrite ? 2 : 0) + (flags.ownerExec ? 1 : 0);
    const group = (flags.groupRead ? 4 : 0) + (flags.groupWrite ? 2 : 0) + (flags.groupExec ? 1 : 0);
    const other = (flags.otherRead ? 4 : 0) + (flags.otherWrite ? 2 : 0) + (flags.otherExec ? 1 : 0);

    return `${owner}${group}${other}`;
};

const PermissionsModal: React.FC<Props> = ({ visible, onDismiss, file, files, initialMode, onPermissionsChanged }) => {
    const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
    const { clearAndAddHttpError } = useFlashKey('better-files');
    const initialPermissionMode = React.useMemo(() => normalizeMode(initialMode), [initialMode]);

    const targets = React.useMemo(() => {
        if (files && files.length > 0) return files;
        if (file) return [file];
        return [];
    }, [files, file]);

    return (
        <Dialog
            open={visible}
            onClose={onDismiss}
            title="Change Permissions"
        >
            <GreyRowBox
                className="betterfiles-scope w-full flex flex-col items-stretch"
                $hoverable={false}
                onMouseDown={(e) => e.stopPropagation()}
                onClick={(e) => e.stopPropagation()}
                style={{ display: 'block' }}
            >
                <Formik
                    enableReinitialize
                    initialValues={{ mode: initialPermissionMode, ...toFlagsFromMode(initialPermissionMode) }}
                    validationSchema={object().shape({
                        mode: string()
                            .required('Permissions are required')
                            .matches(/^[0-7]{3,4}$/, 'Must be a valid permission (e.g., 755, 644)'),
                    })}
                    onSubmit={async (values, { setSubmitting }) => {
                        try {
                            const mode = toModeFromFlags(values);
                            const grouped = new Map<string, Array<{ file: string; mode: string }>>();
                            for (const path of targets) {
                                if (!path) continue;
                                const dir = path.substring(0, path.lastIndexOf('/')) || '/';
                                const name = path.substring(path.lastIndexOf('/') + 1);
                                if (!name) continue;
                                const list = grouped.get(dir) ?? [];
                                list.push({ file: name, mode });
                                grouped.set(dir, list);
                            }
                            for (const [dir, list] of grouped) {
                                await chmodFiles(uuid, dir, list);
                            }
                            await onPermissionsChanged();
                            onDismiss();
                        } catch (error) {
                            clearAndAddHttpError(error as Error);
                        } finally {
                            setSubmitting(false);
                        }
                    }}
                >
                    {({ values, setValues, submitForm, isSubmitting }) => {
                        const updateFromMode = (mode: string) => {
                            const flags = toFlagsFromMode(mode);
                            setValues({ ...values, mode, ...flags }, false);
                        };

                        const updateFlag = (key: keyof PermissionFlags, checked: boolean) => {
                            const next = { ...values, [key]: checked } as typeof values;
                            const mode = toModeFromFlags(next);
                            setValues({ ...next, mode }, false);
                        };

                        return (
                            <Form>
                                {targets.length > 1 && (
                                    <p className="text-xs text-gray-300 mb-3">
                                        Applying permissions to <strong>{targets.length}</strong> items.
                                    </p>
                                )}
                                <div className="grid gap-3">
                                    <GreyRowBox className="overflow-hidden w-full" $hoverable={false} style={{ display: 'block' }}>
                                        <div className="grid w-full grid-cols-[120px_repeat(3,48px)] text-xs text-gray-300">
                                            <div className="px-3 py-2">Permissions</div>
                                            <div className="flex items-center justify-center py-2">R</div>
                                            <div className="flex items-center justify-center py-2">W</div>
                                            <div className="flex items-center justify-center py-2">X</div>
                                        </div>
                                        {[
                                            {
                                                label: 'Owner',
                                                flags: ['ownerRead', 'ownerWrite', 'ownerExec'] as const,
                                            },
                                            {
                                                label: 'Group',
                                                flags: ['groupRead', 'groupWrite', 'groupExec'] as const,
                                            },
                                            {
                                                label: 'Others',
                                                flags: ['otherRead', 'otherWrite', 'otherExec'] as const,
                                            },
                                        ].map((row) => (
                                            <div key={row.label} className="grid w-full grid-cols-[120px_repeat(3,48px)] items-center border-t border-gray-600/60">
                                                <div className="px-3 py-2 text-sm text-gray-300">{row.label}</div>
                                                {row.flags.map((flag) => (
                                                    <label key={flag} className="flex items-center justify-center py-2">
                                                        <Input
                                                            type="checkbox"
                                                            checked={Boolean((values as PermissionFlags)[flag])}
                                                            onChange={(e) => updateFlag(flag, e.currentTarget.checked)}
                                                        />
                                                    </label>
                                                ))}
                                            </div>
                                        ))}
                                    </GreyRowBox>

                                    <div className="grid grid-cols-[120px_1fr] items-center gap-3">
                                        <label htmlFor="mode" className="text-sm text-gray-300">
                                            Octal
                                        </label>
                                        <Input
                                            id="mode"
                                            name="mode"
                                            value={values.mode}
                                            onChange={(e) => {
                                                const next = e.currentTarget.value.replace(/[^0-7]/g, '').slice(0, 4);
                                                updateFromMode(next);
                                            }}
                                            className="w-full border border-gray-600 bg-gray-800 px-3 py-2 font-mono text-sm text-gray-300 focus:border-gray-500 focus:outline-none"
                                            placeholder="755"
                                            autoFocus
                                        />
                                    </div>
                                </div>
                                <Dialog.Footer>
                                    <Button.Text onClick={onDismiss}>
                                        Cancel
                                    </Button.Text>
                                    <Button onClick={submitForm} disabled={isSubmitting}>
                                        Update
                                    </Button>
                                </Dialog.Footer>
                            </Form>
                        );
                    }}
                </Formik>
            </GreyRowBox>
        </Dialog>
    );
};

export default PermissionsModal;
