import React, { useEffect, useState } from 'react';
import FullCalendar, { EventInput } from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import '../styles/Calendar.css';
import { BeamtimesService, DewarsService, PucksService } from '../../openapi';

const beamlineColors: { [key: string]: string } = {
    X06SA: '#FF5733',
    X10SA: '#33FF57',
    X06DA: '#3357FF',
    Unknown: '#CCCCCC',
};

interface CustomEvent extends EventInput {
    beamline: string;
    beamtime_shift: string;
    beamtime_id?: number;
    isSubmitted?: boolean;
}

const experimentModes = ['SDU-Scheduled', 'SDU-queued', 'Remote', 'In-person'];

const darkenColor = (color: string, percent: number): string => {
    const num = parseInt(color.slice(1), 16);
    const amt = Math.round(2.55 * percent);
    const r = (num >> 16) + amt;
    const g = (num >> 8 & 0x00FF) + amt;
    const b = (num & 0x0000FF) + amt;
    const newColor = (0x1000000 + (r < 255 ? (r < 0 ? 0 : r) : 255) * 0x10000
        + (g < 255 ? (g < 0 ? 0 : g) : 255) * 0x100
        + (b < 255 ? (b < 0 ? 0 : b) : 255)).toString(16).slice(1);
    return `#${newColor}`;
};

const Calendar: React.FC = () => {
    const [events, setEvents] = useState<CustomEvent[]>([]);
    const [isLoading, setIsLoading] = useState(false);
    const [fetchError, setFetchError] = useState<string | null>(null);
    const [selectedEventId, setSelectedEventId] = useState<string | null>(null);
    const [eventDetails, setEventDetails] = useState<CustomEvent | null>(null);
    // eventId => { dewars: [dewar_id], pucks: [puck_id] }
    const [eventAssociations, setEventAssociations] = useState<{ [eventId: string]: { dewars: string[], pucks: string[] } }>({});
    const [userDetails, setUserDetails] = useState({
        name: '',
        firstName: '',
        phone: '',
        email: '',
        extAccount: '',
        experimentMode: experimentModes[0],
    });
    const [shipments, setShipments] = useState<any[]>([]);

    // Load all beamtime events AND their current associations (on mount)
    useEffect(() => {
        const fetchAll = async () => {
            setIsLoading(true);
            setFetchError(null);
            try {
                const beamtimes = await BeamtimesService.getMyBeamtimesProtectedBeamtimesMyBeamtimesGet();
                const formattedEvents: CustomEvent[] = beamtimes.map((beamtime: any) => ({
                    id: `${beamtime.beamline}-${beamtime.start_date}`,
                    title: `${beamtime.beamline}: ${beamtime.shift}`,
                    start: beamtime.start_date,
                    end: beamtime.end_date,
                    beamtime_id: beamtime.id,
                    beamline: beamtime.beamline || 'Unknown',
                    beamtime_shift: beamtime.shift || 'Unknown',
                    backgroundColor: beamlineColors[beamtime.beamline] || beamlineColors.Unknown,
                    borderColor: '#000',
                    textColor: '#fff',
                }));
                setEvents(formattedEvents);

                // Fetch associations for all
                const assoc: { [id: string]: { dewars: string[]; pucks: string[] } } = {};
                await Promise.all(
                    beamtimes.map(async (bm: any) => {
                        const [dewars, pucks] = await Promise.all([
                            DewarsService.getDewarsByBeamtime(bm.id),
                            PucksService.getPucksByBeamtime(bm.id),
                        ]);
                        const eventId = `${bm.beamline}-${bm.start_date}`;
                        assoc[eventId] = {
                            dewars: dewars.map((d: any) => d.id),
                            pucks: pucks.map((p: any) => p.id),
                        };
                    })
                );
                setEventAssociations(assoc);

            } catch (error) {
                setFetchError('Failed to load beamtime data. Please try again later.');
                setEvents([]);
                setEventAssociations({});
            } finally {
                setIsLoading(false);
            }
        };
        fetchAll();
    }, []);

    // When an event is selected, fetch up-to-date dewar list
    useEffect(() => {
        if (eventDetails) {
            const fetchDewars = async () => {
                try {
                    const dewarsWithPucks = await DewarsService.getRecentDewarsWithPucks();
                    setShipments(dewarsWithPucks);
                } catch (err) {
                    setShipments([]);
                }
            };
            fetchDewars();
        } else {
            setShipments([]);
        }
    }, [eventDetails]);

    // Refresh associations after (un)assign action
    const refetchEventAssociations = async (beamtimeId: number, eventId: string) => {
        const [dewars, pucks] = await Promise.all([
            DewarsService.getDewarsByBeamtime(beamtimeId),
            PucksService.getPucksByBeamtime(beamtimeId),
        ]);
        setEventAssociations(prev => ({
            ...prev,
            [eventId]: {
                dewars: dewars.map((d: any) => d.id),
                pucks: pucks.map((p: any) => p.id),
            }
        }));
    };

    const handleEventClick = (eventInfo: any) => {
        const clickedEventId = eventInfo.event.id;
        setSelectedEventId(clickedEventId);
        const selected = events.find(event => event.id === clickedEventId) || null;
        setEventDetails(selected);
    };

    const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
        const { name, value } = e.target;
        setUserDetails(prevDetails => ({
            ...prevDetails,
            [name]: value,
        }));
    };

    const handleSubmit = (e: React.FormEvent) => {
        e.preventDefault();
        if (eventDetails) {
            setEvents(prev =>
                prev.map(event =>
                    event.id === eventDetails.id ? { ...event, isSubmitted: true } : event
                )
            );
        }
        setUserDetails({
            name: '',
            firstName: '',
            phone: '',
            email: '',
            extAccount: '',
            experimentMode: experimentModes[0],
        });
    };

    // Unified assign/unassign for Dewars
    const handleDewarAssignment = async (dewarId: string) => {
        if (!selectedEventId) return;
        const event = events.find(e => e.id === selectedEventId)!;
        const beamtimeId = event.beamtime_id;
        if (!beamtimeId) return;
        // Is this dewar already assigned here?
        const assigned = eventAssociations[selectedEventId]?.dewars.includes(dewarId);
        try {
            if (assigned) {
                await DewarsService.assignDewarToBeamtime(Number(dewarId), 0);
            } else {
                await DewarsService.assignDewarToBeamtime(Number(dewarId), Number(beamtimeId));
            }
            await refetchEventAssociations(Number(beamtimeId), selectedEventId);
        } catch (e) {
            // Optionally report error
        }
    };

    // Unified assign/unassign for Pucks
    const handlePuckAssignment = async (puckId: string) => {
        if (!selectedEventId) return;
        const event = events.find(e => e.id === selectedEventId)!;
        const beamtimeId = event.beamtime_id;
        if (!beamtimeId) return;
        const assigned = eventAssociations[selectedEventId]?.pucks.includes(puckId);
        try {
            if (assigned) {
                await PucksService.assignPuckToBeamtime(Number(puckId), 0);
            } else {
                await PucksService.assignPuckToBeamtime(Number(puckId), Number(beamtimeId));
            }
            await refetchEventAssociations(Number(beamtimeId), selectedEventId);
        } catch (e) {
            // Optionally report error
        }
    };

    // For displaying badge in calendar and UI
    const eventContent = (eventInfo: any) => {
        const beamline = eventInfo.event.extendedProps.beamline || 'Unknown';
        const isSelected = selectedEventId === eventInfo.event.id;
        const isSubmitted = eventInfo.event.extendedProps.isSubmitted;
        const hasAssociations =
            eventAssociations[eventInfo.event.id]?.dewars.length > 0 ||
            eventAssociations[eventInfo.event.id]?.pucks.length > 0;
        const backgroundColor = isSubmitted
            ? darkenColor(beamlineColors[beamline] || beamlineColors.Unknown, -20)
            : isSelected
                ? '#FFD700'
                : (beamlineColors[beamline] || beamlineColors.Unknown);

        return (
            <div
                style={{
                    backgroundColor: backgroundColor,
                    color: 'white',
                    border: isSelected ? '2px solid black' : 'none',
                    borderRadius: '3px',
                    display: 'flex',
                    justifyContent: 'center',
                    alignItems: 'center',
                    height: '100%',
                    width: '100%',
                    cursor: 'pointer',
                    overflow: 'hidden',
                    boxSizing: 'border-box',
                }}
            >
                {eventInfo.event.title}
                {hasAssociations && <span style={{marginLeft: 8}}>🧊</span>}
            </div>
        );
    };

    // Used in Dewar/Puck assign status reporting
    function getAssignedEventForDewar(dewarId: string) {
        return Object.entries(eventAssociations).find(([eid, assoc]) =>
            assoc.dewars.includes(dewarId)
        );
    }
    function getAssignedEventForPuck(puckId: string) {
        return Object.entries(eventAssociations).find(([eid, assoc]) =>
            assoc.pucks.includes(puckId)
        );
    }

    return (
        <div className="calendar-container">
            <h2>Beamline Calendar</h2>
            <FullCalendar
                plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
                initialView="dayGridMonth"
                events={events}
                eventContent={eventContent}
                height={700}
                headerToolbar={{
                    left: 'prev,next',
                    center: 'title',
                    right: 'dayGridMonth',
                }}
                eventClick={handleEventClick}
            />

            {eventDetails && (
                <div className="event-details">
                    <h3>Event Details</h3>
                    <p><strong>Beamline:</strong> {eventDetails.beamline}</p>
                    <p><strong>Shift:</strong> {eventDetails.beamtime_shift}</p>

                    <h4>Select Dewars</h4>
                    <ul>
                        {shipments.map(dewar => {
                            const thisEvent = eventAssociations[selectedEventId!] || { dewars: [], pucks: [] };
                            const dewarAssigned = thisEvent.dewars.includes(dewar.id);

                            const [assocEventId, assoc] = getAssignedEventForDewar(dewar.id) || [];
                            const assocEvent = assocEventId
                                ? events.find(ev => ev.id === assocEventId)
                                : null;
                            const assocShift = assocEvent?.beamtime_shift;
                            const assocDate = assocEvent?.start;
                            const assocBeamline = assocEvent?.beamline;

                            const currentShift = eventDetails?.beamtime_shift;
                            const isAssignedToThis = assocShift && currentShift && assocShift === currentShift;

                            return (
                                <li key={dewar.id}>
                                    <label>
                                        <input
                                            type="checkbox"
                                            checked={dewarAssigned}
                                            onChange={() => handleDewarAssignment(dewar.id)}
                                        />
                                        <b>{dewar.dewar_name}</b>
                                    </label>
                                    {/* List all pucks in this Dewar, each with assign button */}
                                    {Array.isArray(dewar.pucks) && dewar.pucks.length > 0 && (
                                        <ul>
                                            {dewar.pucks.map(puck => {
                                                const [pAssocEventId] = getAssignedEventForPuck(puck.id) || [];
                                                const pAssocEvent = pAssocEventId
                                                    ? events.find(ev => ev.id === pAssocEventId)
                                                    : null;
                                                const pAssocShift = pAssocEvent?.beamtime_shift;
                                                const pAssocDate = pAssocEvent?.start;
                                                const pAssocBeamline = pAssocEvent?.beamline;
                                                const isAssignedHere = pAssocShift && currentShift && pAssocShift === currentShift;
                                                return (
                                                    <li key={puck.id} style={{marginLeft:8}}>
                                                        <button
                                                            type="button"
                                                            style={{
                                                                background: isAssignedHere ? '#4CAF50' : (pAssocShift ? '#B3E5B3' : '#e0e0e0'),
                                                                color: isAssignedHere ? 'white' : 'black',
                                                                border: isAssignedHere ? '1px solid #388e3c' : '1px solid #bdbdbd',
                                                                borderRadius: 4,
                                                                padding: '2px 10px',
                                                                cursor: 'pointer',
                                                                transition: 'background 0.2s',
                                                            }}
                                                            onClick={() => handlePuckAssignment(puck.id)}
                                                        >
                                                            {puck.puck_name || puck.name}
                                                        </button>
                                                        {pAssocEvent && (
                                                            <span style={{
                                                                marginLeft: 8,
                                                                color: isAssignedHere ? 'green' : '#388e3c',
                                                                fontWeight: isAssignedHere ? 700 : 400
                                                            }}>
                                                                ← Assigned to: {pAssocShift} {pAssocDate && <>on {new Date(pAssocDate).toLocaleDateString()}</>} {pAssocBeamline && <>({pAssocBeamline})</>}
                                                            </span>
                                                        )}
                                                    </li>
                                                );
                                            })}
                                        </ul>
                                    )}
                                    {/* Show dewar assignment info if not to this shift */}
                                    {assocEvent && (
                                        <span style={{marginLeft:8, color:isAssignedToThis?'green':'#388e3c', fontWeight:isAssignedToThis?700:400}}>
                                            ← Assigned to: {assocShift}
                                            {assocDate && <> on {new Date(assocDate).toLocaleDateString()}</>}
                                            {assocBeamline && <> ({assocBeamline})</>}
                                        </span>
                                    )}
                                </li>
                            );
                        })}
                    </ul>

                    <h4>User Information</h4>
                    <form onSubmit={handleSubmit}>
                        <label>
                            Name:
                            <input
                                type="text"
                                name="name"
                                value={userDetails.name}
                                onChange={handleInputChange}
                            />
                        </label>
                        <br />
                        <label>
                            First Name:
                            <input
                                type="text"
                                name="firstName"
                                value={userDetails.firstName}
                                onChange={handleInputChange}
                            />
                        </label>
                        <br />
                        <label>
                            Phone:
                            <input
                                type="text"
                                name="phone"
                                value={userDetails.phone}
                                onChange={handleInputChange}
                            />
                        </label>
                        <br />
                        <label>
                            Email:
                            <input
                                type="email"
                                name="email"
                                value={userDetails.email}
                                onChange={handleInputChange}
                            />
                        </label>
                        <br />
                        <label>
                            External Account:
                            <input
                                type="text"
                                name="extAccount"
                                value={userDetails.extAccount}
                                onChange={handleInputChange}
                            />
                        </label>
                        <br />
                        <label>
                            Experiment Mode:
                            <select
                                name="experimentMode"
                                value={userDetails.experimentMode}
                                onChange={handleInputChange}
                            >
                                {experimentModes.map(mode => (
                                    <option key={mode} value={mode}>{mode}</option>
                                ))}
                            </select>
                        </label>
                        <br />
                        <button type="submit">Submit</button>
                    </form>
                </div>
            )}
        </div>
    );
};

export default Calendar;