import React, { useEffect, useState } from 'react';
import {StudentPicker} from './StudentPicker';
import {RegisterMark} from './RegisterMark';
import {Button} from "react-bootstrap";
import {RegisterMarkConfigProvider} from "./RegisterMarkConfigProvider";

interface Link {
    rel: string;
    href: string;
    type: string;
    title?: string;
}

interface Student {
    id: number;
    nameFull: string;
    reference: string;
    nameWithReference: string;
    callouts: string[];
    pronouns: string;
    links: Link[]; // Add links to represent HATEOAS navigation
}

interface DynamicRegisterState {
    student: Student;
    registerMark: string;
    saved: boolean; // Track if the row has been saved
}

interface DynamicRegisterProps {
    saveCallback?: (data: DynamicRegisterState[]) => void; // Optional save handler
    fetchInitialData?: () => Promise<string>; // Fetch initial register data
    fetchDisallowedStudents?: () => Promise<number[]>; // Fetch IDs of disallowed students
    canAddStudents: boolean; // Permission to add students
    canMarkStudents: boolean; // Permission to mark students
}

export function DynamicRegister({
    saveCallback,
    fetchInitialData,
    fetchDisallowedStudents,
    canAddStudents = false,
    canMarkStudents = false
}: DynamicRegisterProps) {
    const READY_TO_SAVE_LABEL = 'Save Students and Marks';
    const SAVING_LABEL = 'Saving...';
    const SAVED_LABEL = '✓ Students and Marks Saved';
    const ERROR_SAVING_LABEL = '✖ Error Saving Students and Marks';
    const [registerData, setRegisterData] = useState<DynamicRegisterState[]>([]); // State for table rows
    const [selectedStudent, setSelectedStudent] = useState<Student | null>(null); // Track selected student
    const [saveStatus, setSaveStatus] = useState<
        typeof READY_TO_SAVE_LABEL | typeof SAVING_LABEL | typeof SAVED_LABEL | typeof ERROR_SAVING_LABEL
    >(READY_TO_SAVE_LABEL);
    const [userEdited, setUserEdited] = useState(false); // Track user-driven changes
    const [excludedStudents, setExcludedStudents] = useState<number[]>([]); // List of excluded student IDs
    const [errorMessage, setErrorMessage] = useState<string | null>(null);
    const [isDataLoaded, setIsDataLoaded] = useState(false); // Track data loading status

    // Handle timeout for hiding the error message
    useEffect(() => {
        if (errorMessage) {
            const timer = setTimeout(() => setErrorMessage(null), 5000); // Clear error after 5 seconds
            return () => clearTimeout(timer); // Cleanup the timeout if the component unmounts or a new error is set
        }
    }, [errorMessage]);

    useEffect(() => {
        const fetchInitialRegisterData = async () => {
            if (!fetchInitialData) return;
            try {
                const initialData = await fetchInitialData();
                const parsedData = typeof initialData === 'string' ? JSON.parse(initialData) : initialData;

                if (Array.isArray(parsedData)) {
                    const enrichedData = parsedData.map((entry) => ({
                        ...entry,
                        saved: true, // Set saved to true for each row - it's already in the system
                    }));
                    setRegisterData(enrichedData); // Set the enriched data as initial state
                } else {
                    console.error(
                        'Invalid initial data format. Expected JSON array in format ' +
                        '[{student: {id:1, nameFull:\'NAME\', nameWithReference:\'NAME (REF)\', reference:\'REF\'}, registerMark:\'A\'}]'
                    );
                }
            } catch (error) {
                console.error('Error fetching initial register data:', error);
            } finally {
                setIsDataLoaded(true); // Mark data as loaded
            }
        };

        fetchInitialRegisterData();
    }, []);

    useEffect(() => {
        const fetchExcludedData = async () => {
            if (fetchDisallowedStudents) {
                try {
                    const excludedData = await fetchDisallowedStudents(); // Fetch the JSON string
                    const parsedData = typeof excludedData === 'string' ? JSON.parse(excludedData) : excludedData;
                    if (Array.isArray(parsedData)) {
                        const excludedIds = parsedData.map((item: { student: { id: number } }) => item.student.id);
                        setExcludedStudents(excludedIds); // Extract and set the IDs
                    } else {
                        console.error('Invalid excluded data format. Expected JSON array with structure [{ student: { id: number } }].');
                    }
                } catch (error) {
                    console.error('Error fetching excluded students:', error);
                }
            }
        };

        fetchExcludedData();
    }, [fetchDisallowedStudents]);

    useEffect(() => {
        if (userEdited) {
            // Reset save status to "Save" whenever registerData changes
            setSaveStatus(READY_TO_SAVE_LABEL);
        }
    }, [registerData, userEdited]);

    const handleStudentSelect = (student: Student) => {
        // Disallow student changes without the correct permission.
        if (!canAddStudents) {
            setErrorMessage(`No permission to add guest students.`);
            return;
        }

        // Mark as user-driven change
        setUserEdited(true);

        if (excludedStudents.includes(Number(student.id))) {
            setErrorMessage(`"${student.reference}" is already in the timetabled register.`);
            return;
        }

        if (registerData.some((entry) => Number(entry.student.id) === Number(student.id))) {
            setErrorMessage(`${student.reference} cannot be added twice.`);
            return;
        }

        setRegisterData((prevData) => [
            ...prevData,
            { student, registerMark: '', saved: false },
        ]);

        setErrorMessage(null);
        setSelectedStudent(null); // Clear selection
    };

    const handleDeleteStudent = (studentId: number) => {
        // Disallow student changes without the correct permission.
        if (!canAddStudents) {
            setErrorMessage(`No permission to remove guest students.`);
            return;
        }
        setRegisterData((prevData) =>
            prevData.filter((entry) => entry.student.id !== studentId)
        );
        setErrorMessage(null);
    };

    const handleRegisterMarkChange = (studentId: number, mark: string) => {
        if (!canMarkStudents) {
            return;
        }
        setUserEdited(true); // Mark as user-driven change
        setRegisterData((prevData) =>
            prevData.map((entry) =>
                entry.student.id === studentId ? { ...entry, registerMark: mark } : entry
            )
        );
    };

    const handleSave = async () => {
        // Do nothing without permission to either mark or manage guest students.
        if (!canAddStudents && !canMarkStudents) {
            return;
        }

        setSaveStatus(SAVING_LABEL);
        setUserEdited(false); // Reset user-edited flag after save
        setErrorMessage(null);

        try {
            if (saveCallback) {
                await saveCallback(registerData); // Await callback
            } else {
                // Default save action is asynchronous
                await new Promise((resolve) => {
                    console.log('Default save action:', registerData);
                    setTimeout(resolve, 1000); // Simulate async operation
                });
            }

            // Mark all rows as saved
            setRegisterData((prevData) =>
                prevData.map((entry) => ({
                    ...entry,
                    saved: true,
                }))
            );

            setSaveStatus(SAVED_LABEL);
        } catch (error) {
            // Check if error has a response with status code 401 (unauthorised)
            if (error?.response?.status === 401) {
                setErrorMessage('Error during save as you are not authorised. Please log in again.');
            } else {
                setErrorMessage('An error occurred while saving. Please try again.');
            }
            setSaveStatus(ERROR_SAVING_LABEL);
            console.error('Error during save:', error);
        }
    };

    const renderRows = () => {
        return registerData.map(({ student, registerMark, saved }) => {
            // Find the 'navigation' link in the student's links
            const navigationLink = student.links?.find(link => link.rel === 'ui:profile' && link.type === 'text\/html');

            return (
                <tr key={student.id} id={student.id.toString()}>
                    <td data-column="0" data-row={student.id.toString()} className="datagrid_cell">
                        {navigationLink ? (
                            <mini-profile
                                name={student.nameFull}
                                callout-csv={student.callouts?.join(',')}
                                pronouns={student.pronouns}
                                reference={student.reference}
                                photo-url={`${window.location.origin}/public/photo/thumbnail/${student.id}-${student.reference}`}
                                profile-url={`${window.location.origin}/student/${student.reference}`}
                            />
                        ) : (
                            student.reference
                        )}
                    </td>
                    <td data-column="1" data-row={student.id.toString()} className="datagrid_cell">
                        {student.nameFull}
                        {!saved && (
                            <button
                                onClick={() => handleDeleteStudent(student.id)}
                                style={{
                                    float: 'right',
                                    background: 'none',
                                    border: 'none',
                                    cursor: 'pointer',
                                    color: '#f00',
                                    fontSize: '1.2em',
                                    marginLeft: '1.2em',
                                    lineHeight: '1.3',
                                }}
                                title="Delete"
                            >
                                ✖
                            </button>
                        )}
                    </td>
                    <td data-column="2" data-row={student.id.toString()}
                        className="register-mark-cell datagrid_cell cell_squeeze">
                        {(
                            <RegisterMark
                                id={`register_mark_${student.id}`}
                                initialValue={registerMark}
                                onChange={(newMark) => handleRegisterMarkChange(student.id, newMark)}
                                canChangeMarks={canMarkStudents}
                            />
                        )}
                    </td>
                </tr>
            );
        });
    };

    const renderTable = () => {
        return (
            <div className="datagrid-container">
                <div className="table-container">
                    <table data-rowcount={registerData.length} id="dynamic-register" role="grid" className="datagrid ops_datagrid dynamic-register">
                        <thead>
                        <tr>
                            <th data-column="0" title="Reference" className="ops_datagrid_column_index_0">Reference</th>
                            <th data-column="1" title="Name" className="ops_datagrid_column_index_1">Name</th>
                            <th data-column="2" title="Mark" className="ops_datagrid_column_index_2">Mark</th>
                        </tr>
                        </thead>
                        <tbody>{renderRows()}</tbody>
                    </table>
                </div>
            </div>
        );
    };

    return (
        <div className="dynamic-register-container">
            {isDataLoaded ? (
                registerData.length > 0 ? (
                    <RegisterMarkConfigProvider>
                        {renderTable()}
                    </RegisterMarkConfigProvider>
                ) : (
                    !canAddStudents && <div className="unimportant message">No students</div>
                )
            ) : (
                <div>Loading...</div>
            )}
            <div className="dynamic-register-selector">
                {errorMessage && <div style={{maxWidth: '98%'}} className="alert message">{errorMessage}</div>}
                {canAddStudents && (
                    <StudentPicker onSelect={handleStudentSelect} selectedStudent={selectedStudent}/>
                )}
            </div>
            {(canAddStudents || (canMarkStudents && registerData.length > 0)) && (
                <div>
                    <input
                        type="button"
                        value={saveStatus}
                        onClick={handleSave}
                        disabled={saveStatus === 'Saving...'} // Prevent multiple clicks while saving
                    />
                </div>
            )}
        </div>
    );
}
