import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DateField } from '../../../fields/DateField';
import { BoolCast, DateCast, NumCast, StrCast } from '../../../fields/Types';
import { GoogleAuthenticationManager } from '../../apis/GoogleAuthenticationManager';
import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldView, FieldViewProps } from './FieldView';
import './TaskBox.scss';

/**
 * TaskBox class for adding task information + completing tasks
 */
@observer
export class TaskBox extends ViewBoxBaseComponent<FieldViewProps>() {
    /**
     * Return the JSX string that will create this component
     * @param fieldStr the Doc field that contains the primary data for this component
     * @returns
     */
    public static LayoutString(fieldStr: string) {
        return FieldView.LayoutString(TaskBox, fieldStr);
    }

    // contains the last synced task information
    private _lastSyncedTask: {
        title: string;
        text: string;
        due?: string;
        completed: boolean;
    } = {
        title: '',
        text: '',
        due: '',
        completed: false,
    };

    // Whether the task needs to be synced with Google Tasks
    @observable _needsSync = false;

    // Getter for needsSync
    get needsSync() {
        return this._needsSync;
    }

    /**
     * Constructor for the task box
     * @param props - props containing the document reference
     */
    constructor(props: FieldViewProps) {
        super(props);
        makeObservable(this);
    }

    /**
     * Method to update the task description
     * @param e - event of changing the description box input
     */

    @action
    updateText = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
        this.Document[this.fieldKey] = e.target.value;
    };

    /**
     * Method to update the task title
     * @param e - event of changing the title box input
     */
    @action
    updateTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.Document.title = e.target.value;
    };

    /**
     * Method to update the all day status
     * @param e - event of changing the all day checkbox
     */
    @action
    updateAllDay = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.Document.$task_allDay = e.target.checked;

        if (e.target.checked) {
            delete this.Document.$task_startTime;
            delete this.Document.$task_endTime;
        }

        this.setTaskDateRange();
    };

    /**
     * Method to update the task start time
     * @param e - event of changing the start time input
     */
    @action
    updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
        const newStart = new Date(e.target.value);

        this.Document.$task_startTime = new DateField(newStart);

        const endDate = this.Document.$task_endTime instanceof DateField ? this.Document.$task_endTime.date : undefined;
        if (endDate && newStart > endDate) {
            // Alert user
            alert('Start time cannot be after end time. End time has been adjusted.');

            // Fix end time
            const adjustedEnd = new Date(newStart.getTime() + 60 * 60 * 1000);
            this.Document.$task_endTime = new DateField(adjustedEnd);
        }

        this.setTaskDateRange();
    };

    /**
     * Method to update the task end time
     * @param e - event of changing the end time input
     */
    @action
    updateEnd = (e: React.ChangeEvent<HTMLInputElement>) => {
        const newEnd = new Date(e.target.value);

        this.Document.$task_endTime = new DateField(newEnd);

        const startDate = this.Document.$task_startTime instanceof DateField ? this.Document.$task_startTime.date : undefined;
        if (startDate && newEnd < startDate) {
            // Alert user
            alert('End time cannot be before start time. Start time has been adjusted.');

            // Fix start time
            const adjustedStart = new Date(newEnd.getTime() - 60 * 60 * 1000);
            this.Document.$task_startTime = new DateField(adjustedStart);
        }

        this.setTaskDateRange();
    };

    /**
     * Method to update the task date range
     */
    @action
    setTaskDateRange() {
        const doc = this.Document;

        if (doc.$task_allDay) {
            const range = StrCast(doc.$task_dateRange).split('|');
            const dateStr = range[0] ?? new Date().toISOString().split('T')[0]; // default to today

            doc.$task_dateRange = `${dateStr}|${dateStr}`;
            doc.$task_allDay = true;
        } else {
            const startField = doc.$task_startTime;
            const endField = doc.$task_endTime;
            const startDate = startField instanceof DateField ? startField.date : null;
            const endDate = endField instanceof DateField ? endField.date : null;

            if (startDate && endDate && !isNaN(startDate.getTime()) && !isNaN(endDate.getTime())) {
                doc.$task_dateRange = `${startDate.toISOString()}|${endDate.toISOString()}`;
                doc.$task_allDay = false;
            }
        }
    }

    /**
     * Method to set task's completion status
     * @param e - event of changing the "completed" input checkbox
     */

    @action
    toggleComplete = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.Document.$task_completed = e.target.checked;
    };

    _googleTaskCreateDisposer?: IReactionDisposer;
    _heightDisposer?: IReactionDisposer;
    _widthDisposer?: IReactionDisposer;

    componentDidMount() {
        this.setTaskDateRange();
        const doc = this.Document;

        // adding task on creation to google

        (async () => {
            if (!doc.$googleTaskId && doc.title) {
                try {
                    const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
                    if (!token) return;

                    const body: { title: string; notes: string; status: string; completed?: string; due?: string } = {
                        title: StrCast(doc.title, 'Untitled Task'),
                        notes: StrCast(doc[this.fieldKey]),
                        status: doc.$task_completed ? 'completed' : 'needsAction',
                        completed: doc.$task_completed ? new Date().toISOString() : undefined,
                    };

                    const datePart = StrCast(doc.$task_dateRange).split('|')[0];
                    if (doc.$task_allDay && datePart) {
                        if (datePart && !isNaN(new Date(datePart).getTime())) {
                            const baseDate = datePart.includes('T') ? datePart : datePart + 'T00:00:00Z';
                            body.due = new Date(baseDate).toISOString();
                        }
                    } else if (doc.$task_endTime instanceof DateField) {
                        body.due = doc.$task_endTime.date.toISOString();
                    }

                    const res = await fetch('/googleTasks/create', {
                        method: 'POST',
                        credentials: 'include',
                        headers: {
                            'Content-Type': 'application/json',
                            Authorization: `Bearer ${token}`,
                        },
                        body: JSON.stringify(body),
                    });

                    const result = await res.json();
                    if (result?.id) {
                        doc.$googleTaskId = result.id;
                        console.log('✅ Google Task created on mount:', result);
                    } else {
                        console.warn('❌ Google Task creation failed:', result);
                    }
                } catch (err) {
                    console.error('❌ Error creating Google Task:', err);
                }
            }
        })();

        this._heightDisposer = reaction(
            () => NumCast(doc._height),
            height => {
                const minHeight = NumCast(doc.height_min);
                if (height < minHeight) {
                    doc._height = minHeight;
                }
            }
        );

        this._widthDisposer = reaction(
            () => NumCast(doc._width),
            width => {
                const minWidth = NumCast(doc.width_min);
                if (width < minWidth) {
                    doc._width = minWidth;
                }
            }
        );

        runInAction(() => {
            const completed = BoolCast(doc.$task_completed);
            const $task_allDay = BoolCast(doc.$task_allDay);
            const endTime = DateCast(doc.$task_endTime);
            const startTime = DateCast(doc.$task_startTime);
            const datePart = StrCast(doc.$task_dateRange)?.split('|')[0];
        
            const due = (() => {
                if ($task_allDay && datePart && !isNaN(new Date(datePart).getTime())) {
                    return new Date(datePart).toISOString();
                } else if (endTime && !isNaN(+endTime.date)) {
                    return endTime.date.toISOString();
                } else if (startTime && !isNaN(+startTime.date)) {
                    return startTime.date.toISOString();
                }
                return undefined;
            })();
        
            this._lastSyncedTask = {
                title: StrCast(doc.title),
                text: StrCast(doc[this.fieldKey]),
                due,
                completed,
            };
            this._needsSync = false;
        });

        this._googleTaskCreateDisposer = reaction(
            () => {
                const completed = BoolCast(doc.$task_completed);
                const $task_allDay = BoolCast(doc.$task_allDay);
                const endTime = DateCast(doc.$task_endTime);
                const startTime = DateCast(doc.$task_startTime);
                const datePart = StrCast(doc.$task_dateRange)?.split('|')[0];

                const due = (() => {
                    if ($task_allDay && datePart) {
                        if (!isNaN(new Date(datePart).getTime())) return new Date(datePart).toISOString();
                    } else if (endTime && !isNaN(+endTime.date)) return endTime.date.toISOString();
                    else if (startTime && !isNaN(+startTime.date)) return startTime.date.toISOString();
                })();

                return { title: StrCast(doc.title), text: StrCast(doc[this.fieldKey]), completed, due };
            },
            ({ title, text, completed, due }) => {
                this._needsSync =
                    title !== this._lastSyncedTask.title || //
                    text !== this._lastSyncedTask.text ||
                    due !== this._lastSyncedTask.due ||
                    completed !== this._lastSyncedTask.completed;
            },
            { fireImmediately: true }
        );
    }

    componentWillUnmount() {
        const doc = this.Document;
        this._googleTaskCreateDisposer?.();
        this._heightDisposer?.();
        this._widthDisposer?.();

        // task deletion
        if (doc.$googleTaskId) {
            (async () => {
                try {
                    const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
                    if (!token) return;

                    await fetch(`/googleTasks/${doc.$googleTaskId}`, {
                        method: 'DELETE',
                        credentials: 'include',
                        headers: {
                            Authorization: `Bearer ${token}`,
                        },
                    });

                    console.log(`✅ Deleted Google Task ${doc.$googleTaskId}`);
                } catch (err) {
                    console.warn('❌ Failed to delete Google Task:', err);
                }
            })();
        }
    }

    /**
     * Method to render the task box
     * @returns - HTML with taskbox components
     */

    render() {
        function toLocalDateTimeString(date: Date): string {
            const pad = (n: number) => n.toString().padStart(2, '0');
            return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()) + 'T' + pad(date.getHours()) + ':' + pad(date.getMinutes());
        }

        const doc = this.Document;

        const taskDesc = StrCast(doc[this.fieldKey]);
        const taskTitle = StrCast(doc.title);
        const allDay = !!doc.$task_allDay;
        const isCompleted = !!this.Document.$task_completed;

        const startTime = DateCast(doc.$task_startTime) ? toLocalDateTimeString(DateCast(doc.$task_startTime)!.date) : '';
        const endTime = DateCast(doc.$task_endTime) ? toLocalDateTimeString(DateCast(doc.$task_endTime)!.date) : '';

        const handleGoogleTaskSync = async () => {
            console.log('GT button clicked');

            try {
                const token = await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
                if (!token) {
                    const listener = () => {
                        window.removeEventListener('focusin', listener);
                        if (confirm('✅ Authorization complete. Try syncing the task again?')) {
                            // you could refactor the click handler here
                            handleGoogleTaskSync();
                        }
                        window.removeEventListener('focusin', listener);
                    };
                    setTimeout(() => window.addEventListener('focusin', listener), 100);
                    return;
                }

                let due: string | undefined;

                if (allDay) {
                    const rawRange = StrCast(doc.$task_dateRange);
                    const datePart = rawRange.split('|')[0];

                    if (datePart && !isNaN(new Date(datePart).getTime())) {
                        // Set time to midnight UTC to represent the start of the all-day event
                        const baseDate = datePart.includes('T') ? datePart : datePart + 'T00:00:00Z';
                        due = new Date(baseDate).toISOString();
                    } else {
                        due = undefined;
                    }
                } else if (doc.$task_endTime instanceof DateField && doc.$task_endTime.date) {
                    due = doc.$task_endTime.date.toISOString();
                } else if (doc.$task_startTime instanceof DateField && doc.$task_startTime.date) {
                    due = doc.$task_startTime.date.toISOString();
                } else {
                    due = undefined;
                }

                const isUpdate = !!doc.$googleTaskId;
                const endpoint = isUpdate ? `/googleTasks/${doc.$googleTaskId}` : '/googleTasks/create';
                const method = isUpdate ? 'PATCH' : 'POST';

                const response = await fetch(endpoint, {
                    method,
                    credentials: 'include',
                    headers: {
                        'Content-Type': 'application/json',
                        Authorization: `Bearer ${token}`,
                    },
                    body: JSON.stringify({
                        title: taskTitle || 'Untitled Task',
                        notes: taskDesc,
                        due,
                        status: doc.$task_completed ? 'completed' : 'needsAction',
                        completed: doc.$task_completed ? new Date().toISOString() : undefined,
                    }),
                });

                const result = await response.json();
                console.log('Google Task result:', result);

                if (result?.id) {
                    alert('✅ Task synced with Google Tasks!');
                    if (result?.id) {
                        this._lastSyncedTask = {
                            title: taskTitle,
                            text: taskDesc,
                            due,
                            completed: isCompleted,
                        };
                        runInAction(() => (this._needsSync = false));
                    }
                } else {
                    alert(`❌ Failed: ${result?.error?.message || 'Unknown error'}`);
                }
            } catch (err) {
                console.error('Fetch error:', err);
                alert('❌ Task syncing failed.');
            }
        };

        return (
            <div className="task-manager-container">
                <input className="task-manager-title" type="text" placeholder="Task Title" value={taskTitle} onChange={this.updateTitle} disabled={isCompleted} style={{ opacity: isCompleted ? 0.7 : 1 }} />

                <textarea className="task-manager-description" placeholder="What’s your task?" value={taskDesc} onChange={this.updateText} disabled={isCompleted} style={{ opacity: isCompleted ? 0.7 : 1 }} />

                <div className="task-manager-checkboxes">
                    <label className="task-manager-allday" style={{ opacity: isCompleted ? 0.7 : 1 }}>
                        <input type="checkbox" checked={allDay} onChange={this.updateAllDay} disabled={isCompleted} />
                        All day
                        {allDay && (
                            <input
                                type="date"
                                value={(() => {
                                    const datePart = StrCast(doc.$task_dateRange).split('|')[0];
                                    if (!datePart) return '';
                                    const d = new Date(datePart);
                                    return !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : '';
                                })()}
                                onChange={e => {
                                    const newDate = new Date(e.target.value);
                                    if (!isNaN(newDate.getTime())) {
                                        const dateStr = e.target.value;
                                        if (dateStr) {
                                            doc.$task_dateRange = `${dateStr}T00:00:00|${dateStr}T00:00:00`;
                                        }
                                    }
                                }}
                                disabled={isCompleted}
                                style={{ marginLeft: '8px' }}
                            />
                        )}
                    </label>

                    <label className="task-manager-complete">
                        <input type="checkbox" checked={isCompleted} onChange={this.toggleComplete} />
                        Complete
                    </label>

                    <button
                        className="task-manager-google"
                        disabled={!this.needsSync}
                        onClick={event => {
                            event.preventDefault();
                            handleGoogleTaskSync();
                        }}>
                        Sync to Google
                    </button>
                </div>

                {!allDay && (
                    <div className="task-manager-times" style={{ opacity: isCompleted ? 0.7 : 1 }}>
                        <label>
                            Start:
                            <input type="datetime-local" value={startTime} onChange={this.updateStart} disabled={isCompleted} />
                        </label>
                        <label>
                            End:
                            <input type="datetime-local" value={endTime} onChange={this.updateEnd} disabled={isCompleted} />
                        </label>
                    </div>
                )}
            </div>
        );
    }
}

Docs.Prototypes.TemplateMap.set(DocumentType.TASK, {
    layout: { view: TaskBox, dataField: 'text' },
    options: {
        acl: '',
        _height: 35,
        _xMargin: 10,
        _yMargin: 10,
        _layout_autoHeight: true,
        _layout_nativeDimEditable: true,
        _layout_reflowVertical: true,
        _layout_reflowHorizontal: true,
        task: '',
        defaultDoubleClick: 'ignore',
        systemIcon: 'BsCheckSquare',
        height_min: 300,
        width_min: 300,
    },
});
