import { useContext } from 'react';
import System from '@/lib/models/System';
import { SessionContext } from '@/context/SessionContext';
import { LangContext } from '@/context/LangContext';
import { AlertContext } from '@/context/AlertContext';
import OfflineContext from '@/context/OfflineContext';
import { SeriesSyncContext } from '@/context/SeriesSyncContext';
import { SeriesSyncCompare, SyncedSeries } from '@/lib/models/SyncedSeries';
import { useTranslations } from 'next-intl';

interface RemovedItemRecord {
    removal_id: string;
    table_name: string;
    entity_id: string;
    book_id: string | null;
    user_id: string;
    deleted_at: number;
}

interface SyncedSeriesResponse {
    series: SyncedSeries[];
    tombstones: RemovedItemRecord[];
}

/**
 * Complete series data structure for full upload/download operations.
 * Mirrors the backend CompleteSeries interface.
 */
interface CompleteSeries {
    series: unknown[];
    seriesBooks: unknown[];
    seriesCharacters: unknown[];
    seriesCharacterAttributes: unknown[];
    seriesWorlds: unknown[];
    seriesWorldElements: unknown[];
    seriesLocations: unknown[];
    seriesLocationElements: unknown[];
    seriesLocationSubElements: unknown[];
    seriesSpells: unknown[];
    seriesSpellTags: unknown[];
}

/**
 * Hook for managing series synchronization between local database and server.
 * Provides methods for upload, download, and partial sync operations.
 */
export default function useSyncSeries() {
    const t = useTranslations();
    const { session } = useContext(SessionContext);
    const { lang } = useContext(LangContext);
    const { errorMessage } = useContext(AlertContext);
    const { isCurrentlyOffline, offlineMode } = useContext(OfflineContext);
    const {
        seriesToSyncToServer,
        seriesToSyncFromServer,
        localOnlySeries,
        serverOnlySeries,
        setLocalOnlySeries,
        setServerOnlySeries,
        setServerSyncedSeries,
        setLocalSyncedSeries,
        setSeriesToSyncFromServer,
        setSeriesToSyncToServer
    } = useContext(SeriesSyncContext);

    /**
     * Uploads a local-only series to the server.
     * @param seriesId - The ID of the series to upload
     * @returns True if upload was successful, false otherwise
     */
    async function upload(seriesId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const seriesToSync: CompleteSeries = await window.electron.invoke<CompleteSeries>('db:series:uploadToServer', seriesId);
            if (!seriesToSync) {
                errorMessage(t('seriesCard.uploadError'));
                return false;
            }

            const response: boolean = await System.authPostToServer('series/sync/upload', {
                series: seriesToSync
            }, session.accessToken, lang);

            if (!response) {
                errorMessage(t('seriesCard.uploadError'));
                return false;
            }

            // Move series from local-only to synced
            const uploadedSeries: SyncedSeries | undefined = localOnlySeries.find(
                (series: SyncedSeries): boolean => series.id === seriesId
            );
            setLocalOnlySeries((prevSeries: SyncedSeries[]): SyncedSeries[] => {
                return prevSeries.filter((series: SyncedSeries): boolean => series.id !== seriesId);
            });
            if (uploadedSeries) {
                setLocalSyncedSeries((prev: SyncedSeries[]): SyncedSeries[] => [...prev, uploadedSeries]);
                setServerSyncedSeries((prev: SyncedSeries[]): SyncedSeries[] => [...prev, uploadedSeries]);
            }
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('seriesCard.uploadError'));
            }
            return false;
        }
    }

    /**
     * Downloads a server-only series to the local database.
     * @param seriesId - The ID of the series to download
     * @returns True if download was successful, false otherwise
     */
    async function download(seriesId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const response: CompleteSeries = await System.authGetQueryToServer(
                'series/sync/download',
                session.accessToken,
                lang,
                { seriesId }
            );

            if (!response) {
                errorMessage(t('seriesCard.downloadError'));
                return false;
            }

            const syncStatus: boolean = await window.electron.invoke<boolean>('db:series:syncSave', response);
            if (!syncStatus) {
                errorMessage(t('seriesCard.downloadError'));
                return false;
            }

            // Move series from server-only to synced
            const downloadedSeries: SyncedSeries | undefined = serverOnlySeries.find(
                (series: SyncedSeries): boolean => series.id === seriesId
            );
            setServerOnlySeries((prevSeries: SyncedSeries[]): SyncedSeries[] => {
                return prevSeries.filter((series: SyncedSeries): boolean => series.id !== seriesId);
            });
            if (downloadedSeries) {
                setLocalSyncedSeries((prev: SyncedSeries[]): SyncedSeries[] => [...prev, downloadedSeries]);
                setServerSyncedSeries((prev: SyncedSeries[]): SyncedSeries[] => [...prev, downloadedSeries]);
            }
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('seriesCard.downloadError'));
            }
            return false;
        }
    }

    /**
     * Syncs changes from server to local database for a specific series.
     * Only transfers entities that have changed based on the comparison.
     * @param seriesId - The ID of the series to sync
     * @returns True if sync was successful, false otherwise
     */
    async function syncFromServer(seriesId: string): Promise<boolean> {
        if (isCurrentlyOffline()) return false;

        try {
            const seriesToFetch: SeriesSyncCompare | undefined = seriesToSyncFromServer.find(
                (series: SeriesSyncCompare): boolean => series.id === seriesId
            );
            if (!seriesToFetch) {
                errorMessage(t('seriesCard.syncFromServerError'));
                return false;
            }

            const response: CompleteSeries = await System.authPostToServer(
                'series/sync/server-to-client',
                { seriesToSync: seriesToFetch },
                session.accessToken,
                lang
            );

            if (!response) {
                errorMessage(t('seriesCard.syncFromServerError'));
                return false;
            }

            const syncStatus: boolean = await window.electron.invoke<boolean>('db:series:sync:toClient', response);
            if (!syncStatus) {
                errorMessage(t('seriesCard.syncFromServerError'));
                return false;
            }

            // Remove from pending sync list
            setSeriesToSyncFromServer((prev: SeriesSyncCompare[]): SeriesSyncCompare[] =>
                prev.filter((series: SeriesSyncCompare): boolean => series.id !== seriesId)
            );
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('seriesCard.syncFromServerError'));
            }
            return false;
        }
    }

    /**
     * Syncs local changes to the server for a specific series.
     * Only transfers entities that have changed based on the comparison.
     * @param seriesId - The ID of the series to sync
     * @returns True if sync was successful, false otherwise
     */
    async function syncToServer(seriesId: string): Promise<boolean> {
        if (isCurrentlyOffline()) {
            return false;
        }

        try {
            const seriesToFetch: SeriesSyncCompare | undefined = seriesToSyncToServer.find(
                (series: SeriesSyncCompare): boolean => series.id === seriesId
            );
            if (!seriesToFetch) {
                // La série n'est plus dans la liste - probablement déjà sync par AutoSyncOnReconnect
                // Retourner true car ce n'est pas une erreur, juste déjà fait
                return true;
            }

            const seriesToSync: CompleteSeries = await window.electron.invoke<CompleteSeries>(
                'db:series:sync:toServer',
                seriesToFetch
            );
            if (!seriesToSync) {
                errorMessage(t('seriesCard.syncToServerError'));
                return false;
            }

            const response: boolean = await System.authPatchToServer(
                'series/sync/client-to-server',
                { series: seriesToSync },
                session.accessToken,
                lang
            );

            if (!response) {
                errorMessage(t('seriesCard.syncToServerError'));
                return false;
            }

            // Remove from pending sync list
            setSeriesToSyncToServer((prev: SeriesSyncCompare[]): SeriesSyncCompare[] =>
                prev.filter((series: SeriesSyncCompare): boolean => series.id !== seriesId)
            );
            return true;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('seriesCard.syncToServerError'));
            }
            return false;
        }
    }

    /**
     * Syncs all series that have local changes to the server.
     */
    async function syncAllToServer(): Promise<void> {
        for (const diff of seriesToSyncToServer) {
            await syncToServer(diff.id);
        }
    }

    async function syncAllFromServer(): Promise<void> {
        for (const diff of seriesToSyncFromServer) {
            await syncFromServer(diff.id);
        }
    }

    async function refreshSeries(): Promise<void> {
        try {
            let localSeriesResponse: SyncedSeries[] = [];
            let serverSeriesResponse: SyncedSeries[] = [];

            if (!isCurrentlyOffline()) {
                if (offlineMode.isDatabaseInitialized) {
                    localSeriesResponse = await window.electron.invoke<SyncedSeries[]>('db:series:synced');

                    // Get lastOnlineTimestamp from localStorage (or 0 if not set)
                    const lastOnlineStr: string | null = localStorage.getItem('lastOnlineTimestamp');
                    const lastOnlineTimestamp: number = lastOnlineStr ? parseInt(lastOnlineStr, 10) : 0;

                    // Get local tombstones since lastOnlineTimestamp via IPC
                    const localTombstones: RemovedItemRecord[] = await window.electron.invoke<RemovedItemRecord[]>(
                        'db:tombstones:since',
                        lastOnlineTimestamp
                    );

                    // Call server with POST and tombstones
                    const serverResponse: SyncedSeriesResponse = await System.authPostToServer<SyncedSeriesResponse>(
                        'series/synced',
                        { lastOnlineTimestamp, tombstones: localTombstones },
                        session.accessToken,
                        lang
                    );

                    serverSeriesResponse = serverResponse.series;

                    // Apply server tombstones locally via IPC
                    await window.electron.invoke<void>('db:tombstones:apply:series', serverResponse.tombstones);
                } else {
                    // No local DB but online - just get server series without tombstones
                    const serverResponse: SyncedSeriesResponse = await System.authPostToServer<SyncedSeriesResponse>(
                        'series/synced',
                        { lastOnlineTimestamp: 0, tombstones: [] },
                        session.accessToken,
                        lang
                    );
                    serverSeriesResponse = serverResponse.series;
                }
            } else {
                if (offlineMode.isDatabaseInitialized) {
                    localSeriesResponse = await window.electron.invoke<SyncedSeries[]>('db:series:synced');
                }
            }

            setServerSyncedSeries(serverSeriesResponse);
            setLocalSyncedSeries(localSeriesResponse);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('seriesCard.refreshError'));
            }
        }
    }

    return {
        upload,
        download,
        syncFromServer,
        syncToServer,
        syncAllToServer,
        syncAllFromServer,
        refreshSeries,
        localOnlySeries,
        serverOnlySeries,
        seriesToSyncToServer,
        seriesToSyncFromServer
    };
}
