import { queryOne } from '../../shared/database';

const USER_DB = 'userdata';

// Configuración de reintentos
const RETRY_CONFIG = {
    maxRetries: 3,
    initialDelay: 1000,
    maxDelay: 5000,
    backoffMultiplier: 2
};

// Helper para hacer requests con reintentos y manejo de errores
async function fetchWithRetry(
    url: string,
    options: RequestInit,
    retries = RETRY_CONFIG.maxRetries
): Promise<Response> {
    let lastError: Error | null = null;
    let delay = RETRY_CONFIG.initialDelay;

    for (let attempt = 0; attempt <= retries; attempt++) {
        try {
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout

            const response = await fetch(url, {
                ...options,
                signal: controller.signal
            });

            clearTimeout(timeoutId);

            // Si es rate limit (429), esperamos más tiempo
            if (response.status === 429) {
                const retryAfter = response.headers.get('Retry-After');
                const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : delay;

                if (attempt < retries) {
                    console.warn(`Rate limited. Esperando ${waitTime}ms antes de reintentar...`);
                    await new Promise(resolve => setTimeout(resolve, waitTime));
                    delay = Math.min(delay * RETRY_CONFIG.backoffMultiplier, RETRY_CONFIG.maxDelay);
                    continue;
                }
            }

            // Si es un error de servidor (5xx), reintentamos
            if (response.status >= 500 && attempt < retries) {
                console.warn(`Error del servidor (${response.status}). Reintentando en ${delay}ms...`);
                await new Promise(resolve => setTimeout(resolve, delay));
                delay = Math.min(delay * RETRY_CONFIG.backoffMultiplier, RETRY_CONFIG.maxDelay);
                continue;
            }

            return response;
        } catch (error) {
            lastError = error as Error;

            if (attempt < retries && (
                error instanceof Error && (
                    error.name === 'AbortError' ||
                    error.message.includes('fetch') ||
                    error.message.includes('network')
                )
            )) {
                console.warn(`Error de conexión (intento ${attempt + 1}/${retries + 1}). Reintentando en ${delay}ms...`);
                await new Promise(resolve => setTimeout(resolve, delay));
                delay = Math.min(delay * RETRY_CONFIG.backoffMultiplier, RETRY_CONFIG.maxDelay);
                continue;
            }

            throw error;
        }
    }

    throw lastError || new Error('Request failed after all retries');
}

export async function getUserAniList(appUserId: number) {
    try {
        const sql = `
            SELECT access_token, anilist_user_id
            FROM UserIntegration
            WHERE user_id = ? AND platform = 'AniList';
        `;

        const integration = await queryOne(sql, [appUserId], USER_DB) as any;
        if (!integration) return [];

        const { access_token, anilist_user_id } = integration;
        if (!access_token || !anilist_user_id) return [];

        const query = `
        query ($userId: Int) {
          anime: MediaListCollection(userId: $userId, type: ANIME) {
            lists {
              entries {
                media {
                  id
                  title { romaji english userPreferred }
                  coverImage { extraLarge }
                  episodes
                  nextAiringEpisode { episode }
                }
                status
                progress
                score
                repeat
                notes
                private
                startedAt { year month day }
                completedAt { year month day }
              }
            }
          }

          manga: MediaListCollection(userId: $userId, type: MANGA) {
            lists {
              entries {
                media {
                  id
                  type
                  format
                  title { romaji english userPreferred }
                  coverImage { extraLarge }
                  chapters
                  volumes
                }
                status
                progress
                score
                repeat
                notes
                private
                startedAt { year month day }
                completedAt { year month day }
              }
            }
          }
        }
        `;

        const res = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${access_token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({
                query,
                variables: { userId: anilist_user_id }
            }),
        });

        if (!res.ok) throw new Error(`AniList API error: ${res.status}`);

        const json = await res.json();
        if (json?.errors?.length) throw new Error(json.errors[0].message);

        const fromFuzzy = (d: any) => {
            if (!d?.year) return null;
            const m = String(d.month || 1).padStart(2, '0');
            const day = String(d.day || 1).padStart(2, '0');
            return `${d.year}-${m}-${day}`;
        };

        const normalize = (lists: any[], type: 'ANIME' | 'MANGA') => {
            const result: any[] = [];

            for (const list of lists || []) {
                for (const entry of list.entries || []) {
                    const media = entry.media;

                    const totalEpisodes =
                        media?.episodes ||
                        (media?.nextAiringEpisode?.episode
                            ? media.nextAiringEpisode.episode - 1
                            : 0);

                    const totalChapters =
                        media?.chapters ||
                        (media?.volumes ? media.volumes * 10 : 0);

                    const resolvedType =
                        type === 'MANGA' &&
                        (media?.format === 'LIGHT_NOVEL' || media?.format === 'NOVEL')
                            ? 'NOVEL'
                            : type;


                    result.push({
                        user_id: appUserId,

                        entry_id: media.id,
                        source: 'anilist',

                        // ✅ AHORA TU FRONT RECIBE NOVEL
                        entry_type: resolvedType,

                        status: entry.status,
                        progress: entry.progress || 0,
                        score: entry.score || null,
                        start_date: fromFuzzy(entry.startedAt),
                        end_date: fromFuzzy(entry.completedAt),
                        repeat_count: entry.repeat || 0,
                        notes: entry.notes || null,
                        is_private: entry.private ? 1 : 0,

                        title: media?.title?.userPreferred
                            || media?.title?.english
                            || media?.title?.romaji
                            || 'Unknown Title',

                        poster: media?.coverImage?.extraLarge
                            || 'https://placehold.co/400x600?text=No+Cover',

                        total_episodes: resolvedType === 'ANIME' ? totalEpisodes : undefined,
                        total_chapters: resolvedType !== 'ANIME' ? totalChapters : undefined,

                        updated_at: new Date().toISOString()
                    });
                }
            }

            return result;
        };

        return [
            ...normalize(json?.data?.anime?.lists, 'ANIME'),
            ...normalize(json?.data?.manga?.lists, 'MANGA')
        ];

    } catch (error) {
        console.error('Error fetching AniList data:', error);
        return [];
    }
}

export async function updateAniListEntry(token: string, params: {
    mediaId: number | string;
    status?: string | null;
    progress?: number | null;
    score?: number | null;
    start_date?: string | null;   // YYYY-MM-DD
    end_date?: string | null;     // YYYY-MM-DD
    repeat_count?: number | null;
    notes?: string | null;
    is_private?: boolean | number | null;
}) {
    try {
        if (!token) throw new Error('AniList token is required');

        const mutation = `
            mutation (
                $mediaId: Int,
                $status: MediaListStatus,
                $progress: Int,
                $score: Float,
                $startedAt: FuzzyDateInput,
                $completedAt: FuzzyDateInput,
                $repeat: Int,
                $notes: String,
                $private: Boolean
            ) {
                SaveMediaListEntry (
                    mediaId: $mediaId,
                    status: $status,
                    progress: $progress,
                    score: $score,
                    startedAt: $startedAt,
                    completedAt: $completedAt,
                    repeat: $repeat,
                    notes: $notes,
                    private: $private
                ) {
                    id
                    status
                    progress
                    score
                    startedAt { year month day }
                    completedAt { year month day }
                    repeat
                    notes
                    private
                }
            }
        `;

        const toFuzzyDate = (dateStr?: string | null) => {
            if (!dateStr) return null;
            const [year, month, day] = dateStr.split('-').map(Number);
            return { year, month, day };
        };

        const variables: any = {
            mediaId: Number(params.mediaId),
            status: params.status ?? undefined,
            progress: params.progress ?? undefined,
            score: params.score ?? undefined,
            startedAt: toFuzzyDate(params.start_date),
            completedAt: toFuzzyDate(params.end_date),
            repeat: params.repeat_count ?? undefined,
            notes: params.notes ?? undefined,
            private: typeof params.is_private === 'boolean'
                ? params.is_private
                : params.is_private === 1
        };

        const res = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({ query: mutation, variables }),
        });

        if (!res.ok) {
            const errorText = await res.text();
            throw new Error(`AniList update failed: ${res.status} - ${errorText}`);
        }

        const json = await res.json();

        if (json?.errors?.length) {
            throw new Error(`AniList GraphQL error: ${json.errors[0].message}`);
        }

        return json.data?.SaveMediaListEntry || null;
    } catch (error) {
        console.error('Error updating AniList entry:', error);
        throw error;
    }
}

export async function deleteAniListEntry(token: string, mediaId: number) {
    if (!token) throw new Error("AniList token required");

    try {
        // 1️⃣ OBTENER VIEWER
        const viewerQuery = `query { Viewer { id name } }`;

        const vRes = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({ query: viewerQuery }),
        });

        const vJson = await vRes.json();
        const userId = vJson?.data?.Viewer?.id;

        if (!userId) throw new Error("Invalid AniList token");

        // 2️⃣ DETECTAR TIPO REAL DEL MEDIA
        const mediaQuery = `
      query ($id: Int) {
        Media(id: $id) {
          id
          type
        }
      }
    `;

        const mTypeRes = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({
                query: mediaQuery,
                variables: { id: mediaId }
            }),
        });

        const mTypeJson = await mTypeRes.json();
        const mediaType = mTypeJson?.data?.Media?.type;

        if (!mediaType) {
            throw new Error("Media not found in AniList");
        }

        // 3️⃣ BUSCAR ENTRY CON TIPO REAL
        const listQuery = `
      query ($userId: Int, $mediaId: Int, $type: MediaType) {
        MediaList(userId: $userId, mediaId: $mediaId, type: $type) {
          id
        }
      }
    `;

        const qRes = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({
                query: listQuery,
                variables: {
                    userId,
                    mediaId,
                    type: mediaType
                }
            }),
        });

        const qJson = await qRes.json();
        const listEntryId = qJson?.data?.MediaList?.id;

        if (!listEntryId) {
            throw new Error("Entry not found in user's AniList");
        }

        // 4️⃣ BORRAR
        const mutation = `
      mutation ($id: Int) {
        DeleteMediaListEntry(id: $id) {
          deleted
        }
      }
    `;

        const delRes = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({
                query: mutation,
                variables: { id: listEntryId }
            }),
        });

        const delJson = await delRes.json();

        if (delJson?.errors?.length) {
            throw new Error(delJson.errors[0].message);
        }

        return true;

    } catch (err) {
        console.error("AniList DELETE failed:", err);
        throw err;
    }
}


export async function getSingleAniListEntry(
    token: string,
    mediaId: number,
    type: 'ANIME' | 'MANGA'
) {
    try {
        if (!token) {
            throw new Error('AniList token is required');
        }

        // 1️⃣ Obtener userId desde el token
        const viewerRes = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({
                query: `query { Viewer { id } }`
            })
        });

        const viewerJson = await viewerRes.json();
        const userId = viewerJson?.data?.Viewer?.id;

        if (!userId) {
            throw new Error('Failed to get AniList userId');
        }

        // 2️⃣ Query correcta con userId
        const query = `
            query ($mediaId: Int, $type: MediaType, $userId: Int) {
                MediaList(mediaId: $mediaId, type: $type, userId: $userId) {
                    id
                    status
                    progress
                    score
                    repeat
                    private
                    notes
                    startedAt { year month day }
                    completedAt { year month day }
                }
            }
        `;

        const res = await fetchWithRetry('https://graphql.anilist.co', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            },
            body: JSON.stringify({
                query,
                variables: { mediaId, type, userId }
            })
        });

        if (res.status === 404) {
            return null; // ✅ No existe entry todavía → es totalmente válido
        }

        if (!res.ok) {
            const errorText = await res.text();
            throw new Error(`AniList fetch failed: ${res.status} - ${errorText}`);
        }

        const json = await res.json();

        if (json?.errors?.length) {
            if (json.errors[0].status === 404) return null;
            throw new Error(`GraphQL error: ${json.errors[0].message}`);
        }

        const entry = json?.data?.MediaList;
        if (!entry) return null;

        return {
            entry_id: mediaId,
            source: 'anilist',
            entry_type: type,
            status: entry.status,
            progress: entry.progress || 0,
            score: entry.score ?? null,

            start_date: entry.startedAt?.year
                ? `${entry.startedAt.year}-${String(entry.startedAt.month).padStart(2, '0')}-${String(entry.startedAt.day).padStart(2, '0')}`
                : null,

            end_date: entry.completedAt?.year
                ? `${entry.completedAt.year}-${String(entry.completedAt.month).padStart(2, '0')}-${String(entry.completedAt.day).padStart(2, '0')}`
                : null,

            repeat_count: entry.repeat || 0,
            notes: entry.notes || null,
            is_private: entry.private ? 1 : 0,
        };

    } catch (error) {
        console.error('Error fetching single AniList entry:', error);
        throw error;
    }
}
