import { DAVClient } from 'tsdav';
import ICAL from 'ical.js';

export interface SynologyReminderList {
    id: string; // URL
    title: string;
    color?: string;
}

/**
 * Create a configured DAV client for Synology Tasks
 */
const createClient = (serverUrl: string, username: string, password: string) => {
    let url = serverUrl.replace(/\/$/, '');
    if (!url.includes('/caldav')) {
        url = `${url}/caldav/${username}`;
    }
    return new DAVClient({
        serverUrl: url,
        credentials: {
            username,
            password,
        },
        authMethod: 'Basic',
        defaultAccountType: 'caldav',
    });
};

/**
 * Fetch all reminder lists (VTODO collections) from Synology
 */
export const fetchSynologyReminderLists = async (serverUrl: string, username: string, password: string): Promise<SynologyReminderList[]> => {
    try {
        const client = createClient(serverUrl, username, password);
        await client.login();

        const calendars = await client.fetchCalendars();

        // Filter to VTODO collections (Reminders)
        const reminderLists = calendars.filter(cal => {
            const components: string[] = (cal as any).components || [];
            return components.includes('VTODO');
        });

        return reminderLists.map(cal => ({
            id: cal.url,
            title: (cal.displayName as string) || 'Untitled List',
            color: (cal as any).calendarColor,
        }));
    } catch (error) {
        console.error('Error fetching Synology reminder lists:', error);
        throw error;
    }
};

/**
 * Create a new reminder list (collection)
 */
export const createSynologyReminderList = async (serverUrl: string, username: string, password: string, title: string): Promise<SynologyReminderList> => {
    try {
        console.log('[SYNOLOGY] Creating reminder list:', title, 'at', serverUrl);
        const client = createClient(serverUrl, username, password);
        await client.login();

        const calendars = await client.fetchCalendars();
        if (calendars.length === 0) {
            console.error('[SYNOLOGY] No calendars found to derive base URL');
            throw new Error('No existing Synology calendars found to derive base URL');
        }

        // Deriving the base URL for new collections
        const firstCalUrl = calendars[0].url;
        const urlParts = firstCalUrl.split('/');
        if (firstCalUrl.endsWith('/')) urlParts.pop();
        urlParts.pop();
        const baseWebDavUrl = urlParts.join('/') + '/';

        const newId = crypto.randomUUID();
        const newCalendarUrl = `${baseWebDavUrl}${newId}/`;
        console.log('[SYNOLOGY] Proposed new calendar URL:', newCalendarUrl);

        // Create the calendar collection with explicit namespaces if possible
        // Synology requires specific properties to recognize it as a Task list
        await (client as any).makeCalendar({
            url: newCalendarUrl,
            props: {
                'displayname': title,
                'calendar-description': 'Created by My Weekly ToDo List',
                'calendar-color': '#007AFF',
                'supported-calendar-component-set': {
                    'comp': { _attributes: { name: 'VTODO' } }
                }
            }
        });

        console.log('[SYNOLOGY] List created successfully');
        return {
            id: newCalendarUrl,
            title: title,
        };
    } catch (error) {
        console.error('[SYNOLOGY] Error creating reminder list:', error);
        throw error;
    }
};

/**
 * Create a new task (VTODO) in a Synology list via CalDAV
 */
export const createSynologyTask = async (
    serverUrl: string, username: string, password: string,
    listId: string, task: { title: string; notes?: string; due?: string | null }
): Promise<{ id: string }> => {
    const client = createClient(serverUrl, username, password);
    await client.login();

    const calendars = await client.fetchCalendars();
    const getPath = (url: string) => {
        try { return new URL(url).pathname; } catch { return url; }
    };
    const targetCalendar = calendars.find(c => getPath(c.url) === getPath(listId));
    if (!targetCalendar) throw new Error(`List not found: ${listId}`);

    const uid = crypto.randomUUID();
    const now = ICAL.Time.now();

    const vcalendar = new ICAL.Component(['vcalendar', [], []]);
    vcalendar.updatePropertyWithValue('prodid', '-//My Weekly ToDo List//EN');
    vcalendar.updatePropertyWithValue('version', '2.0');

    const vtodo = new ICAL.Component('vtodo');
    vtodo.updatePropertyWithValue('uid', uid);
    vtodo.updatePropertyWithValue('summary', task.title);
    vtodo.updatePropertyWithValue('created', now);
    vtodo.updatePropertyWithValue('dtstamp', now);
    vtodo.updatePropertyWithValue('status', 'NEEDS-ACTION');

    if (task.notes) {
        vtodo.updatePropertyWithValue('description', task.notes);
    }
    if (task.due) {
        const dueTime = ICAL.Time.fromJSDate(new Date(task.due), false);
        vtodo.updatePropertyWithValue('due', dueTime);
    }

    vcalendar.addSubcomponent(vtodo);

    const calendarUrl = targetCalendar.url.replace(/\/$/, '');
    const objectUrl = `${calendarUrl}/${uid}.ics`;

    await client.createCalendarObject({
        calendar: targetCalendar,
        filename: `${uid}.ics`,
        iCalString: vcalendar.toString(),
    });

    return { id: `synology::${uid}` };
};

/**
 * Fetch tasks (VTODOs) from a specific Synology list
 */
export const fetchSynologyTasks = async (serverUrl: string, username: string, password: string, listId: string) => {
    try {
        const client = createClient(serverUrl, username, password);
        await client.login();

        const calendars = await client.fetchCalendars();
        // Match by URL path only (ignoring host/port differences that can arise
        // when the stored URL was saved before the port was included in the server URL)
        const getPath = (url: string) => {
            try { return new URL(url).pathname; } catch { return url; }
        };
        const targetPath = getPath(listId);
        const targetCalendar = calendars.find(c => getPath(c.url) === targetPath);

        if (!targetCalendar) {
            throw new Error(`List not found: ${listId}`);
        }

        // Use VTODO filter to ensure we get tasks, not just events
        const objects = await client.fetchCalendarObjects({
            calendar: targetCalendar,
            filters: [{
                'comp-filter': {
                    _attributes: { name: 'VCALENDAR' },
                    'comp-filter': {
                        _attributes: { name: 'VTODO' },
                    },
                },
            }],
        });

        const parsedTasks: any[] = [];

        objects.forEach((obj) => {
            const data = (obj as any).data;
            if (!data) return;

            try {
                const jcalData = ICAL.parse(data);
                const comp = new ICAL.Component(jcalData);
                const vtodos = comp.getAllSubcomponents('vtodo');

                vtodos.forEach((vtodo: any) => {
                    const status = vtodo.getFirstPropertyValue('status') || 'NEEDS-ACTION';
                    const isCompleted = status === 'COMPLETED';
                    const dueProp = vtodo.getFirstProperty('due');

                    parsedTasks.push({
                        id: `synology::${vtodo.getFirstPropertyValue('uid')}`,
                        title: vtodo.getFirstPropertyValue('summary') || 'Untitled Task',
                        notes: vtodo.getFirstPropertyValue('description') || '',
                        status: isCompleted ? 'completed' : 'needsAction',
                        due: dueProp ? dueProp.getFirstValue().toJSDate() : null,
                    });
                });
            } catch (err) {
                console.error('[SYNOLOGY TASKS] Error parsing VTODO:', err);
            }
        });

        return parsedTasks;
    } catch (error) {
        console.error('[SYNOLOGY TASKS] Error fetching tasks:', error);
        throw error;
    }
};

/**
 * Update a task (VTODO) on Synology via CalDAV
 */
export const updateSynologyTask = async (
    serverUrl: string, username: string, password: string,
    listId: string, taskUid: string,
    updates: { title?: string; notes?: string; completed?: boolean; due?: string | null }
) => {
    const client = createClient(serverUrl, username, password);
    await client.login();

    const calendars = await client.fetchCalendars();
    const getPath = (url: string) => {
        try { return new URL(url).pathname; } catch { return url; }
    };
    const targetCalendar = calendars.find(c => getPath(c.url) === getPath(listId));
    if (!targetCalendar) throw new Error(`List not found: ${listId}`);

    // Fetch all objects to find the one matching our UID
    const objects = await client.fetchCalendarObjects({
        calendar: targetCalendar,
        filters: [{
            'comp-filter': {
                _attributes: { name: 'VCALENDAR' },
                'comp-filter': { _attributes: { name: 'VTODO' } },
            },
        }],
    });

    const cleanUid = taskUid.replace(/^synology::/, '');
    const matchObj = objects.find((obj) => {
        const data = (obj as any).data;
        if (!data) return false;
        try {
            const jcal = ICAL.parse(data);
            const comp = new ICAL.Component(jcal);
            const vtodo = comp.getFirstSubcomponent('vtodo');
            return vtodo?.getFirstPropertyValue('uid') === cleanUid;
        } catch { return false; }
    });

    if (!matchObj) throw new Error(`Task not found: ${cleanUid}`);

    // Parse and modify the VTODO
    const jcal = ICAL.parse((matchObj as any).data);
    const comp = new ICAL.Component(jcal);
    const vtodo = comp.getFirstSubcomponent('vtodo')!;

    if (updates.title !== undefined) {
        vtodo.updatePropertyWithValue('summary', updates.title);
    }
    if (updates.notes !== undefined) {
        vtodo.updatePropertyWithValue('description', updates.notes);
    }
    if (updates.completed !== undefined) {
        if (updates.completed) {
            vtodo.updatePropertyWithValue('status', 'COMPLETED');
            vtodo.updatePropertyWithValue('percent-complete', 100);
            const now = ICAL.Time.now();
            vtodo.updatePropertyWithValue('completed', now);
        } else {
            vtodo.updatePropertyWithValue('status', 'NEEDS-ACTION');
            vtodo.removeProperty('percent-complete');
            vtodo.removeProperty('completed');
        }
    }
    if (updates.due !== undefined) {
        if (updates.due) {
            const dueTime = ICAL.Time.fromJSDate(new Date(updates.due), false);
            vtodo.updatePropertyWithValue('due', dueTime);
        } else {
            vtodo.removeProperty('due');
        }
    }

    // Update last-modified
    vtodo.updatePropertyWithValue('last-modified', ICAL.Time.now());

    await client.updateCalendarObject({
        calendarObject: {
            url: (matchObj as any).url,
            data: comp.toString(),
            etag: (matchObj as any).etag,
        },
    });
};

/**
 * Delete a task (VTODO) from Synology via CalDAV
 */
export const deleteSynologyTask = async (
    serverUrl: string, username: string, password: string,
    listId: string, taskUid: string,
) => {
    const client = createClient(serverUrl, username, password);
    await client.login();

    const calendars = await client.fetchCalendars();
    const getPath = (url: string) => {
        try { return new URL(url).pathname; } catch { return url; }
    };
    const targetCalendar = calendars.find(c => getPath(c.url) === getPath(listId));
    if (!targetCalendar) throw new Error(`List not found: ${listId}`);

    const objects = await client.fetchCalendarObjects({
        calendar: targetCalendar,
        filters: [{
            'comp-filter': {
                _attributes: { name: 'VCALENDAR' },
                'comp-filter': { _attributes: { name: 'VTODO' } },
            },
        }],
    });

    const cleanUid = taskUid.replace(/^synology::/, '');
    const matchObj = objects.find((obj) => {
        const data = (obj as any).data;
        if (!data) return false;
        try {
            const jcal = ICAL.parse(data);
            const comp = new ICAL.Component(jcal);
            const vtodo = comp.getFirstSubcomponent('vtodo');
            return vtodo?.getFirstPropertyValue('uid') === cleanUid;
        } catch { return false; }
    });

    if (!matchObj) throw new Error(`Task not found: ${cleanUid}`);

    await client.deleteCalendarObject({
        calendarObject: {
            url: (matchObj as any).url,
            etag: (matchObj as any).etag,
        },
    });
};
