'use client';

import React, { createContext, useContext, useEffect, useState, useRef, ReactNode } from 'react';
import type { RefreshToken } from '../types/api';
import { API_PATHS } from '../types/api';

interface TokenContextType {
  accessToken: string | null;
  refreshToken: string | null;
  isRefreshing: boolean;
  refreshError: string | null;
  forceRefresh: () => Promise<void>;
  clearTokens: () => void;
}

const TokenContext = createContext<TokenContextType | null>(null);

interface TokenRefreshManagerProps {
  children: ReactNode;
  autoRefresh?: boolean;
  refreshThreshold?: number; // seconds before expiration to refresh
  onTokenExpired?: () => void;
  onRefreshFailed?: (error: string) => void;
}

export function TokenRefreshManager({
  children,
  autoRefresh = true,
  refreshThreshold = 300, // 5 minutes
  onTokenExpired,
  onRefreshFailed,
}: TokenRefreshManagerProps) {
  const [accessToken, setAccessToken] = useState<string | null>(null);
  const [refreshToken, setRefreshToken] = useState<string | null>(null);
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [refreshError, setRefreshError] = useState<string | null>(null);
  const [tokenExpiration, setTokenExpiration] = useState<Date | null>(null);
  const refreshTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  // Load tokens from storage on mount
  useEffect(() => {
    const loadTokens = () => {
      try {
        const storedAccess = localStorage.getItem('access_token');
        const storedRefresh = localStorage.getItem('refresh_token');
        const storedExpiration = localStorage.getItem('token_expiration');

        if (storedAccess) {
          setAccessToken(storedAccess);
        }

        if (storedRefresh) {
          setRefreshToken(storedRefresh);
        }

        if (storedExpiration) {
          setTokenExpiration(new Date(storedExpiration));
        }
      } catch (error) {
        console.error('Failed to load tokens from storage:', error);
      }
    };

    loadTokens();
  }, []);

  // Save tokens to storage
  const saveTokens = (access: string, refresh: string, expiration: Date) => {
    try {
      localStorage.setItem('access_token', access);
      localStorage.setItem('refresh_token', refresh);
      localStorage.setItem('token_expiration', expiration.toISOString());
    } catch (error) {
      console.error('Failed to save tokens to storage:', error);
    }
  };

  // Clear tokens from storage
  const clearTokensFromStorage = () => {
    try {
      localStorage.removeItem('access_token');
      localStorage.removeItem('refresh_token');
      localStorage.removeItem('token_expiration');
    } catch (error) {
      console.error('Failed to clear tokens from storage:', error);
    }
  };

  // Clear tokens (logout)
  const clearTokens = () => {
    setAccessToken(null);
    setRefreshToken(null);
    setTokenExpiration(null);
    setRefreshError(null);
    clearTokensFromStorage();

    if (refreshTimeoutRef.current) {
      clearTimeout(refreshTimeoutRef.current);
    }
  };

  // Refresh access token
  const refreshAccessToken = async (): Promise<boolean> => {
    if (!refreshToken) {
      setRefreshError('No refresh token available');
      return false;
    }

    try {
      setIsRefreshing(true);
      setRefreshError(null);

      const response = await fetch(API_PATHS.AUTH_REFRESH, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          refreshToken,
        }),
      });

      if (!response.ok) {
        if (response.status === 401) {
          // Refresh token is invalid/expired
          clearTokens();
          onTokenExpired?.();
          return false;
        }
        throw new Error('Token refresh failed');
      }

      const data = await response.json();

      // Update tokens
      setAccessToken(data.accessToken);
      setRefreshToken(data.refreshToken || refreshToken);

      const expiration = data.expiresAt
        ? new Date(data.expiresAt)
        : new Date(Date.now() + 3600 * 1000); // Default 1 hour

      setTokenExpiration(expiration);
      saveTokens(data.accessToken, data.refreshToken || refreshToken, expiration);

      return true;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      setRefreshError(errorMessage);
      onRefreshFailed?.(errorMessage);

      // Don't clear tokens on network error, but clear on auth error
      if (!navigator.onLine) {
        return false;
      }

      return false;
    } finally {
      setIsRefreshing(false);
    }
  };

  // Force refresh
  const forceRefresh = async () => {
    await refreshAccessToken();
  };

  // Set up auto-refresh
  useEffect(() => {
    if (!autoRefresh || !tokenExpiration || !refreshToken) {
      return;
    }

    const scheduleRefresh = () => {
      if (refreshTimeoutRef.current) {
        clearTimeout(refreshTimeoutRef.current);
      }

      const now = new Date();
      const timeUntilExpiration = tokenExpiration.getTime() - now.getTime();
      const refreshTime = timeUntilExpiration - refreshThreshold * 1000;

      if (refreshTime <= 0) {
        // Token is already expired or will expire soon
        refreshAccessToken();
      } else {
        // Schedule refresh
        refreshTimeoutRef.current = setTimeout(() => {
          refreshAccessToken();
        }, refreshTime);
      }
    };

    scheduleRefresh();

    return () => {
      if (refreshTimeoutRef.current) {
        clearTimeout(refreshTimeoutRef.current);
      }
    };
  }, [autoRefresh, tokenExpiration, refreshToken, refreshThreshold]);

  // Add token to API requests
  useEffect(() => {
    const originalFetch = window.fetch;

    window.fetch = async (input, init) => {
      let requestInit = init || {};

      // Add auth header if we have a token
      if (accessToken && !requestInit.headers) {
        requestInit.headers = {
          'Authorization': `Bearer ${accessToken}`,
        };
      } else if (accessToken && requestInit.headers) {
        (requestInit.headers as Record<string, string>)['Authorization'] = `Bearer ${accessToken}`;
      }

      try {
        const response = await originalFetch(input, requestInit);

        // Handle 401 responses (token expired)
        if (response.status === 401 && accessToken) {
          // Try to refresh the token
          const refreshed = await refreshAccessToken();

          if (refreshed) {
            // Retry the request with new token
            const retryInit = {
              ...requestInit,
              headers: {
                ...(requestInit.headers as Record<string, string>),
                'Authorization': `Bearer ${accessToken}`,
              },
            };
            return originalFetch(input, retryInit);
          } else {
            // Refresh failed, redirect to login
            clearTokens();
            onTokenExpired?.();
          }
        }

        return response;
      } catch (error) {
        // Handle network errors
        throw error;
      }
    };

    return () => {
      window.fetch = originalFetch;
    };
  }, [accessToken]);

  const contextValue: TokenContextType = {
    accessToken,
    refreshToken,
    isRefreshing,
    refreshError,
    forceRefresh,
    clearTokens,
  };

  return (
    <TokenContext.Provider value={contextValue}>
      {children}
    </TokenContext.Provider>
  );
}

// Hook to use token context
export function useAuth() {
  const context = useContext(TokenContext);
  if (!context) {
    throw new Error('useAuth must be used within a TokenRefreshManager');
  }
  return context;
}

// Component to show refresh status (for debugging)
export function TokenStatus() {
  const { accessToken, isRefreshing, refreshError, forceRefresh } = useAuth();

  return (
    <div className="fixed bottom-4 right-4 bg-white rounded-lg shadow-lg p-4 max-w-xs">
      <h3 className="font-semibold mb-2">Auth Status</h3>

      <div className="space-y-1 text-sm">
        <p>
          <span className="font-medium">Token:</span>{' '}
          <span className={accessToken ? 'text-green-600' : 'text-red-600'}>
            {accessToken ? 'Present' : 'Missing'}
          </span>
        </p>

        <p>
          <span className="font-medium">Status:</span>{' '}
          {isRefreshing ? 'Refreshing...' : 'Idle'}
        </p>

        {refreshError && (
          <p className="text-red-600">Error: {refreshError}</p>
        )}
      </div>

      <button
        onClick={forceRefresh}
        className="mt-2 px-3 py-1 bg-blue-100 hover:bg-blue-200 text-blue-700 text-sm rounded transition-colors"
      >
        Force Refresh
      </button>
    </div>
  );
}