• Settings page component for the Kenmei to AniList sync tool.

    Handles authentication, sync preferences, data management, and cache clearing for the user.

    Returns Element

    export function SettingsPage() {
    const {
    authState,
    login,
    logout,
    isLoading,
    error: authError,
    statusMessage,
    setCredentialSource,
    updateCustomCredentials,
    customCredentials,
    } = useAuth();

    // Add a ref to track the previous credential source to prevent loops
    const prevCredentialSourceRef = useRef<"default" | "custom">(
    authState.credentialSource,
    );

    const [error, setError] = useState<AppError | null>(null);
    const [cacheCleared, setCacheCleared] = useState(false);
    const [isClearing, setIsClearing] = useState(false);
    const [showStatusMessage, setShowStatusMessage] = useState(true);
    const [cachesToClear, setCachesToClear] = useState({
    auth: false,
    settings: false,
    sync: false,
    import: false,
    review: false,
    manga: false,
    search: false,
    other: false,
    });
    const [useCustomCredentials, setUseCustomCredentials] = useState(
    authState.credentialSource === "custom",
    );
    const [clientId, setClientId] = useState("");
    const [clientSecret, setClientSecret] = useState("");
    const [redirectUri, setRedirectUri] = useState(
    `http://localhost:${DEFAULT_AUTH_PORT}/callback`,
    );
    const [syncConfig, setSyncConfig] = useState<SyncConfig>(getSyncConfig());
    const [useCustomThreshold, setUseCustomThreshold] = useState<boolean>(
    typeof syncConfig.autoPauseThreshold === "string" ||
    ![1, 7, 14, 30, 60, 90, 180, 365].includes(
    Number(syncConfig.autoPauseThreshold),
    ),
    );
    // Version status state
    const [versionStatus, setVersionStatus] = useState<AppVersionStatus | null>(
    null,
    );
    // Update Check State
    const [updateChannel, setUpdateChannel] = useState<"stable" | "beta">(
    "stable",
    );
    const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
    const [updateInfo, setUpdateInfo] = useState<null | {
    version: string;
    url: string;
    isBeta: boolean;
    }>(null);
    const [updateError, setUpdateError] = useState<string | null>(null);

    // Handler for opening external links in the default browser
    const handleOpenExternal = (url: string) => (e: React.MouseEvent) => {
    e.preventDefault();
    if (window.electronAPI?.shell?.openExternal) {
    window.electronAPI.shell.openExternal(url);
    } else {
    // Fallback to regular link behavior if not in Electron
    window.open(url, "_blank", "noopener,noreferrer");
    }
    };

    // Track previous credential values to prevent unnecessary updates
    const prevCredentialsRef = useRef({
    id: "",
    secret: "",
    uri: "",
    });

    // Update error state when auth error changes
    useEffect(() => {
    if (authError) {
    setError(createError(ErrorType.AUTHENTICATION, authError));
    } else {
    setError(null);
    }
    }, [authError]);

    // Update credential source when toggle changes, but avoid infinite loop
    useEffect(() => {
    const newSource = useCustomCredentials ? "custom" : "default";
    // Only update if actually changed and not from authState sync
    if (newSource !== prevCredentialSourceRef.current) {
    prevCredentialSourceRef.current = newSource;
    setCredentialSource(newSource);
    }
    }, [useCustomCredentials, setCredentialSource]);

    // Update local state if authState.credentialSource changes externally
    useEffect(() => {
    if (authState.credentialSource !== prevCredentialSourceRef.current) {
    prevCredentialSourceRef.current = authState.credentialSource;
    setUseCustomCredentials(authState.credentialSource === "custom");
    }
    }, [authState.credentialSource]);

    // Update custom credentials when fields change
    useEffect(() => {
    if (useCustomCredentials && clientId && clientSecret && redirectUri) {
    // Only update if values actually changed
    if (
    clientId !== prevCredentialsRef.current.id ||
    clientSecret !== prevCredentialsRef.current.secret ||
    redirectUri !== prevCredentialsRef.current.uri
    ) {
    // Update the ref
    prevCredentialsRef.current = {
    id: clientId,
    secret: clientSecret,
    uri: redirectUri,
    };

    // Update context
    updateCustomCredentials(clientId, clientSecret, redirectUri);
    }
    }
    }, [
    useCustomCredentials,
    clientId,
    clientSecret,
    redirectUri,
    updateCustomCredentials,
    ]);

    // Reset error when auth state changes
    useEffect(() => {
    if (authState.isAuthenticated) {
    setError(null);

    // If we have a status message and authentication is complete,
    // set a timeout to clear the status message
    if (statusMessage && !isLoading) {
    const timer = setTimeout(() => {
    setShowStatusMessage(false);
    }, 3000); // Auto-dismiss after 3 seconds

    return () => clearTimeout(timer);
    }
    } else {
    // Reset the status message visibility when not authenticated
    setShowStatusMessage(true);
    }
    }, [authState.isAuthenticated, statusMessage, isLoading]);

    // Add a timeout to detect stuck loading state
    useEffect(() => {
    let timeoutId: NodeJS.Timeout | null = null;

    if (isLoading) {
    // If loading state persists for more than 20 seconds, trigger a refresh
    timeoutId = setTimeout(() => {
    console.log(
    "Loading state persisted for too long - triggering refresh",
    );
    handleRefreshPage();
    }, 20000);
    }

    return () => {
    if (timeoutId) clearTimeout(timeoutId);
    };
    }, [isLoading]);

    // Add a useEffect to load custom credential settings from localStorage on initial mount
    useEffect(() => {
    try {
    // Load custom credentials toggle state
    const savedUseCustom = localStorage.getItem("useCustomCredentials");
    if (savedUseCustom) {
    setUseCustomCredentials(JSON.parse(savedUseCustom));
    }

    // Load saved custom credentials if they exist
    const savedCustomCreds = localStorage.getItem("customCredentials");
    if (savedCustomCreds) {
    const credentials = JSON.parse(savedCustomCreds);
    setClientId(credentials.clientId || "");
    setClientSecret(credentials.clientSecret || "");
    setRedirectUri(
    credentials.redirectUri ||
    `http://localhost:${DEFAULT_AUTH_PORT}/callback`,
    );

    // Also update context with saved credentials
    if (
    credentials.clientId &&
    credentials.clientSecret &&
    credentials.redirectUri
    ) {
    updateCustomCredentials(
    credentials.clientId,
    credentials.clientSecret,
    credentials.redirectUri,
    );
    }
    }
    } catch (err) {
    console.error("Failed to load saved credential settings:", err);
    }
    }, []);

    // Save custom credentials toggle state whenever it changes
    useEffect(() => {
    localStorage.setItem(
    "useCustomCredentials",
    JSON.stringify(useCustomCredentials),
    );
    }, [useCustomCredentials]);

    // Save custom credentials whenever they change
    useEffect(() => {
    if (clientId || clientSecret || redirectUri) {
    localStorage.setItem(
    "customCredentials",
    JSON.stringify({
    clientId,
    clientSecret,
    redirectUri,
    }),
    );
    }
    }, [clientId, clientSecret, redirectUri]);

    // Initialize fields from customCredentials prop when it changes
    useEffect(() => {
    if (customCredentials) {
    // Use refs to avoid unnecessary state updates
    if (clientId !== customCredentials.clientId) {
    setClientId(customCredentials.clientId);
    }
    if (clientSecret !== customCredentials.clientSecret) {
    setClientSecret(customCredentials.clientSecret);
    }
    if (redirectUri !== customCredentials.redirectUri) {
    setRedirectUri(customCredentials.redirectUri);
    }
    }
    }, [customCredentials]);

    // Version status useEffect
    useEffect(() => {
    let mounted = true;
    getAppVersionStatus().then((status) => {
    if (mounted) setVersionStatus(status);
    });
    return () => {
    mounted = false;
    };
    }, []);

    const handleLogin = async () => {
    try {
    // Create credentials object based on source
    const credentials: APICredentials = useCustomCredentials
    ? {
    source: "custom",
    clientId,
    clientSecret,
    redirectUri,
    }
    : {
    source: "default",
    clientId: DEFAULT_ANILIST_CONFIG.clientId,
    clientSecret: DEFAULT_ANILIST_CONFIG.clientSecret,
    redirectUri: DEFAULT_ANILIST_CONFIG.redirectUri,
    };

    await login(credentials);
    } catch (err: unknown) {
    setError(
    createError(
    ErrorType.AUTHENTICATION,
    err instanceof Error
    ? err.message
    : "Failed to authenticate with AniList. Please try again.",
    ),
    );
    }
    };

    const handleCancelAuth = () => {
    window.electronAuth.cancelAuth();
    };

    const handleClearCache = async () => {
    try {
    // Start clearing process and show loading state
    setCacheCleared(false);
    setIsClearing(true);
    setError(null);

    // Get all cache clearing functions
    const { clearMangaCache, cacheDebugger } = await import(
    "../api/matching/manga-search-service"
    );
    const { clearSearchCache } = await import("../api/anilist/client");

    console.log("🧹 Starting selective cache clearing...");

    // Define which localStorage keys belong to which cache type
    const cacheKeysByType = {
    auth: ["authState", "customCredentials", "useCustomCredentials"],
    search: ["anilist_search_cache"],
    manga: ["anilist_manga_cache"],
    review: ["match_results", "pending_manga", "matching_progress"],
    import: ["kenmei_data", "import_history", "import_stats"],
    sync: ["anilist_sync_history"],
    settings: ["sync_config", "theme"],
    other: ["cache_version"],
    };

    // Additional keys from STORAGE_KEYS constant
    if (STORAGE_KEYS) {
    Object.entries(STORAGE_KEYS).forEach(([key, value]) => {
    if (typeof value === "string") {
    // Add to appropriate category based on key name
    if (key.includes("MATCH") || key.includes("REVIEW")) {
    if (!cacheKeysByType.review.includes(value)) {
    cacheKeysByType.review.push(value);
    }
    } else if (key.includes("IMPORT")) {
    if (!cacheKeysByType.import.includes(value)) {
    cacheKeysByType.import.push(value);
    }
    } else if (key.includes("CACHE")) {
    if (!cacheKeysByType.other.includes(value)) {
    cacheKeysByType.other.push(value);
    }
    }
    }
    });
    }

    // Keep track of which caches were cleared for user feedback
    const clearedCacheTypes = [];

    // Clear Search Cache if selected
    if (cachesToClear.search) {
    clearSearchCache();
    clearedCacheTypes.push("search");
    console.log("🧹 Search cache cleared");
    }

    // Clear Manga Cache if selected
    if (cachesToClear.manga) {
    clearMangaCache();
    clearedCacheTypes.push("manga");
    console.log("🧹 Manga cache cleared");
    }

    // If both search and manga are selected, use the full reset
    if (cachesToClear.search && cachesToClear.manga) {
    cacheDebugger.resetAllCaches();
    console.log("🧹 All in-memory caches reset");
    }

    // Get all localStorage keys to clear based on selections
    const keysToRemove: string[] = [];

    Object.entries(cachesToClear).forEach(([type, selected]) => {
    if (selected && cacheKeysByType[type as keyof typeof cacheKeysByType]) {
    keysToRemove.push(
    ...cacheKeysByType[type as keyof typeof cacheKeysByType],
    );
    }
    });

    // Remove duplicates
    const uniqueKeysToRemove = [...new Set(keysToRemove)];

    console.log(
    "🧹 Clearing the following localStorage keys:",
    uniqueKeysToRemove,
    );

    // Clear selected localStorage keys
    uniqueKeysToRemove.forEach((cacheKey) => {
    try {
    localStorage.removeItem(cacheKey);
    if (
    window.electronStore &&
    typeof window.electronStore.removeItem === "function"
    ) {
    window.electronStore.removeItem(cacheKey);
    console.log(`🧹 Cleared Electron Store cache: ${cacheKey}`);
    }
    console.log(`🧹 Cleared cache: ${cacheKey}`);
    } catch (e) {
    console.warn(`Failed to clear cache: ${cacheKey}`, e);
    }
    });

    // Clear IndexedDB if any cache is selected
    if (Object.values(cachesToClear).some(Boolean)) {
    try {
    const DBDeleteRequest =
    window.indexedDB.deleteDatabase("anilist-cache");
    DBDeleteRequest.onsuccess = () =>
    console.log("🧹 Successfully deleted IndexedDB database");
    DBDeleteRequest.onerror = () =>
    console.error("Error deleting IndexedDB database");
    clearedCacheTypes.push("indexeddb");
    } catch (e) {
    console.warn("Failed to clear IndexedDB:", e);
    }
    }

    console.log("🧹 Selected caches cleared");

    // Show success message
    setCacheCleared(true);

    // Create a summary of cleared caches for user feedback
    const clearedSummary = Object.entries(cachesToClear)
    .filter(([, selected]) => selected)
    .map(([type]) => `✅ Cleared ${type} cache`)
    .join("\n");

    // Show a detailed summary to the user
    try {
    window.alert(
    "Cache Cleared Successfully!\n\n" +
    clearedSummary +
    "\n\nYou may need to restart the application for all changes to take effect.",
    );
    } catch (e) {
    console.warn("Failed to show alert:", e);
    }

    setTimeout(() => setCacheCleared(false), 5000);

    // Remove loading state
    setIsClearing(false);
    } catch (error) {
    console.error("Error clearing cache:", error);
    setError(
    createError(
    ErrorType.SYSTEM,
    error instanceof Error
    ? error.message
    : "An unexpected error occurred while clearing cache",
    ),
    );
    setIsClearing(false);
    }
    };

    const dismissError = () => {
    setError(null);
    };

    const handleRefreshPage = () => {
    // Clear error states and status messages
    setError(null);
    window.location.reload();
    };

    const calculateExpiryTime = () => {
    if (!authState.expiresAt) return "unknown";

    const hoursRemaining = Math.round(
    (authState.expiresAt - Date.now()) / 3600000,
    );

    if (hoursRemaining > 24) {
    const days = Math.floor(hoursRemaining / 24);
    const hours = hoursRemaining % 24;
    return `${days}d ${hours}h`;
    }

    return `${hoursRemaining}h`;
    };

    // Fetch update info from GitHub
    const handleCheckForUpdates = async () => {
    setIsCheckingUpdate(true);
    setUpdateError(null);
    setUpdateInfo(null);
    try {
    const response = await fetch(
    "https://api.github.com/repos/RLAlpha49/KenmeiToAnilist/releases?per_page=10",
    );
    if (!response.ok) throw new Error("Failed to fetch releases");
    type Release = {
    draft: boolean;
    prerelease: boolean;
    tag_name: string;
    html_url: string;
    body: string;
    };
    const releases: Release[] = await response.json();
    let release: Release | null = null;
    if (updateChannel === "stable") {
    release = releases.find((r) => !r.draft && !r.prerelease) || null;
    } else {
    release =
    releases.find((r) => !r.draft && r.prerelease) ||
    releases.find((r) => !r.draft && !r.prerelease) ||
    null;
    }
    if (!release) throw new Error("No release found for selected channel");
    setUpdateInfo({
    version: release.tag_name,
    url: release.html_url,
    isBeta: !!release.prerelease,
    });
    } catch (e) {
    setUpdateError(e instanceof Error ? e.message : "Unknown error");
    } finally {
    setIsCheckingUpdate(false);
    }
    };

    return (
    <motion.div
    className="container mx-auto px-4 py-8 md:px-6"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ duration: 0.4 }}
    >
    <motion.div
    className="mb-8 space-y-2"
    initial={{ opacity: 0, y: -20 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ delay: 0.1, duration: 0.5 }}
    >
    <div className="flex items-center justify-between">
    <h1 className="bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-4xl font-bold text-transparent">
    Settings
    </h1>
    </div>
    <p className="text-muted-foreground max-w-2xl">
    Configure your AniList authentication and manage application settings.
    </p>
    </motion.div>

    {error && (
    <motion.div
    className="mb-6"
    initial={{ opacity: 0, scale: 0.95 }}
    animate={{ opacity: 1, scale: 1 }}
    transition={{ duration: 0.3 }}
    >
    <ErrorMessage
    message={error.message}
    type={error.type}
    dismiss={dismissError}
    retry={
    error.type === ErrorType.AUTHENTICATION ? handleLogin : undefined
    }
    />
    </motion.div>
    )}

    {statusMessage && !error && showStatusMessage && (
    <motion.div
    initial={{ opacity: 0, y: 10 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ duration: 0.4 }}
    exit={{ opacity: 0, y: -10 }}
    >
    <Alert className="mb-6" variant="default">
    <ExternalLink className="h-4 w-4" />
    <AlertTitle>Authentication Status</AlertTitle>
    <AlertDescription className="flex items-center justify-between">
    <span>{statusMessage}</span>
    {isLoading ? (
    <Button
    variant="outline"
    size="sm"
    onClick={handleCancelAuth}
    className="ml-auto flex items-center gap-1.5"
    >
    <XCircle className="h-4 w-4" />
    Cancel
    </Button>
    ) : (
    <Button
    variant="outline"
    size="sm"
    onClick={() => setShowStatusMessage(false)}
    className="ml-auto flex items-center gap-1.5"
    >
    <XCircle className="h-4 w-4" />
    Dismiss
    </Button>
    )}
    </AlertDescription>
    </Alert>
    </motion.div>
    )}

    <motion.div
    initial={{ opacity: 0, y: 20 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ duration: 0.5, delay: 0.2 }}
    >
    <Tabs defaultValue="account" className="space-y-6">
    <TabsList className="bg-muted/50 grid w-full grid-cols-3 md:w-auto dark:bg-gray-800/50">
    <TabsTrigger
    value="account"
    className="data-[state=active]:bg-background flex items-center gap-1.5 dark:text-gray-300 dark:data-[state=active]:bg-gray-700 dark:data-[state=active]:text-white dark:data-[state=active]:shadow-sm"
    >
    <UserCircle className="h-4 w-4" />
    Account
    </TabsTrigger>
    <TabsTrigger
    value="sync"
    className="data-[state=active]:bg-background flex items-center gap-1.5 dark:text-gray-300 dark:data-[state=active]:bg-gray-700 dark:data-[state=active]:text-white dark:data-[state=active]:shadow-sm"
    >
    <RefreshCw className="h-4 w-4" />
    Sync
    </TabsTrigger>
    <TabsTrigger
    value="data"
    className="data-[state=active]:bg-background flex items-center gap-1.5 dark:text-gray-300 dark:data-[state=active]:bg-gray-700 dark:data-[state=active]:text-white dark:data-[state=active]:shadow-sm"
    >
    <Database className="h-4 w-4" />
    Data
    </TabsTrigger>
    </TabsList>

    <TabsContent value="account" className="space-y-6">
    <motion.div variants={itemVariants} initial="hidden" animate="show">
    <Card className="bg-muted/10 overflow-hidden border-none shadow-md">
    <CardHeader className="mr-2 ml-2 rounded-t-lg rounded-b-lg bg-gradient-to-r from-indigo-500/10 to-purple-500/10">
    <CardTitle className="mt-2 flex items-center gap-2">
    <motion.div
    className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-r from-indigo-500 to-purple-500 text-white"
    whileHover={{ scale: 1.05 }}
    whileTap={{ scale: 0.95 }}
    >
    <Key className="h-4 w-4" />
    </motion.div>
    AniList Authentication
    </CardTitle>
    <CardDescription className="mb-2">
    Connect your AniList account to sync your manga collection.
    </CardDescription>
    </CardHeader>
    <CardContent className="space-y-6 pt-6">
    {/* API Credentials Control */}
    <motion.div
    className="bg-muted/40 rounded-lg border p-4"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ delay: 0.3, duration: 0.5 }}
    >
    <div className="mb-4 flex items-center justify-between">
    <div className="space-y-0.5">
    <h3 className="text-sm font-medium">API Credentials</h3>
    <p className="text-muted-foreground text-xs">
    Choose which API credentials to use for authentication
    </p>
    </div>
    <div className="flex items-center gap-2">
    <span className="text-muted-foreground text-sm">
    Custom
    </span>
    <Switch
    checked={useCustomCredentials}
    onCheckedChange={setUseCustomCredentials}
    disabled={authState.isAuthenticated}
    />
    </div>
    </div>

    {authState.isAuthenticated && (
    <Alert variant="destructive" className="mb-4">
    <AlertTriangle className="h-4 w-4" />
    <AlertDescription className="text-xs">
    You must log out before changing API credentials
    </AlertDescription>
    </Alert>
    )}

    {/* Custom Credentials Fields */}
    {useCustomCredentials && (
    <div className="space-y-3">
    <div className="grid gap-1.5">
    <label className="text-xs font-medium">
    Client ID
    </label>
    <input
    type="text"
    className="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
    value={clientId}
    onChange={(e) => setClientId(e.target.value)}
    disabled={authState.isAuthenticated || isLoading}
    placeholder="Your AniList Client ID"
    />
    </div>
    <div className="grid gap-1.5">
    <label className="text-xs font-medium">
    Client Secret
    </label>
    <input
    type="password"
    className="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
    value={clientSecret}
    onChange={(e) => setClientSecret(e.target.value)}
    disabled={authState.isAuthenticated || isLoading}
    placeholder="Your AniList Client Secret"
    />
    </div>
    <div className="grid gap-1.5">
    <label className="flex items-center gap-1.5 text-xs font-medium">
    <Link className="h-3.5 w-3.5" />
    Redirect URI
    </label>
    <input
    type="text"
    className="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
    value={redirectUri}
    onChange={(e) => setRedirectUri(e.target.value)}
    disabled={authState.isAuthenticated || isLoading}
    placeholder={`http://localhost:${DEFAULT_AUTH_PORT}/callback`}
    />
    <p className="text-muted-foreground text-xs">
    Must match the redirect URI registered in your
    AniList app settings
    </p>
    </div>
    <p className="text-muted-foreground text-xs">
    You can get these by registering a new client on{" "}
    <a
    href="https://anilist.co/settings/developer"
    target="_blank"
    rel="noopener noreferrer"
    className="text-primary hover:underline"
    onClick={handleOpenExternal(
    "https://anilist.co/settings/developer",
    )}
    >
    AniList Developer Settings
    </a>
    </p>
    </div>
    )}
    </motion.div>

    {authState.isAuthenticated ? (
    <motion.div
    className="space-y-4"
    initial={{ opacity: 0, y: 10 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ delay: 0.4, duration: 0.4 }}
    >
    <motion.div
    className="rounded-lg bg-gradient-to-b from-indigo-50 to-purple-50 p-6 dark:from-indigo-950/30 dark:to-purple-950/30"
    initial={{ scale: 0.95 }}
    animate={{ scale: 1 }}
    transition={{
    delay: 0.5,
    duration: 0.4,
    type: "spring",
    stiffness: 300,
    damping: 24,
    }}
    >
    <div className="flex flex-col items-center justify-center gap-4">
    <div className="relative">
    <div className="border-background h-20 w-20 overflow-hidden rounded-full border-4 shadow-xl">
    <img
    src={authState.avatarUrl}
    alt={authState.username}
    className="h-full w-full object-cover"
    />
    </div>
    <Badge className="absolute -right-1 -bottom-1 flex h-6 w-6 items-center justify-center rounded-full bg-green-500 p-0">
    <CheckCircle className="h-3 w-3" />
    </Badge>
    </div>

    <div className="text-center">
    <h3 className="text-xl font-semibold">
    {authState.username}
    </h3>
    <div className="text-muted-foreground mt-1 flex items-center justify-center gap-1.5 text-sm">
    <Clock className="h-3.5 w-3.5" />
    <span>Expires in {calculateExpiryTime()}</span>
    </div>
    <a
    href={`https://anilist.co/user/${authState.username}`}
    target="_blank"
    rel="noopener noreferrer"
    className="mt-2 inline-flex items-center gap-1.5 rounded-full bg-indigo-100 px-3 py-1 text-xs font-medium text-indigo-700 transition-colors hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-300 dark:hover:bg-indigo-800/40"
    onClick={handleOpenExternal(
    `https://anilist.co/user/${authState.username}`,
    )}
    >
    <ExternalLink className="h-3 w-3" />
    View AniList Profile
    </a>
    </div>
    </div>
    </motion.div>

    <motion.div
    className="grid grid-cols-2 gap-3"
    variants={containerVariants}
    initial="hidden"
    animate="show"
    >
    <Button
    onClick={handleLogin}
    disabled={isLoading}
    variant="outline"
    className="flex items-center justify-center gap-2"
    >
    <RefreshCw className="h-4 w-4" />
    Refresh Token
    </Button>
    <Button
    onClick={logout}
    variant="destructive"
    className="flex items-center justify-center gap-2"
    >
    <UserCircle className="h-4 w-4" />
    Logout
    </Button>
    </motion.div>
    </motion.div>
    ) : (
    <motion.div
    className="space-y-6"
    initial={{ opacity: 0, y: 10 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ delay: 0.4, duration: 0.4 }}
    >
    <Alert className="border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-900/30">
    <AlertTriangle className="h-4 w-4 text-amber-600" />
    <AlertTitle className="text-amber-600">
    Not Connected
    </AlertTitle>
    <AlertDescription className="text-amber-600">
    You need to authenticate with AniList to sync your
    manga collection.
    </AlertDescription>
    </Alert>

    <Button
    onClick={handleLogin}
    disabled={
    isLoading ||
    (useCustomCredentials &&
    (!clientId || !clientSecret || !redirectUri))
    }
    className="w-full bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700"
    size="lg"
    >
    {isLoading ? (
    <div className="flex items-center gap-2">
    <svg
    className="h-4 w-4 animate-spin"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    >
    <circle
    className="opacity-25"
    cx="12"
    cy="12"
    r="10"
    stroke="currentColor"
    strokeWidth="4"
    ></circle>
    <path
    className="opacity-75"
    fill="currentColor"
    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
    ></path>
    </svg>
    Connecting...
    </div>
    ) : (
    "Connect to AniList"
    )}
    </Button>
    </motion.div>
    )}
    </CardContent>
    </Card>
    </motion.div>
    </TabsContent>

    <TabsContent value="sync" className="space-y-6">
    <motion.div variants={itemVariants} initial="hidden" animate="show">
    <Card className="bg-muted/10 overflow-hidden border-none shadow-md">
    <CardHeader className="mr-2 ml-2 rounded-t-lg rounded-b-lg bg-gradient-to-r from-purple-500/10 to-blue-500/10">
    <CardTitle className="mt-2 flex items-center gap-2">
    <motion.div
    className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-r from-purple-500 to-blue-500 text-white"
    whileHover={{ scale: 1.05 }}
    whileTap={{ scale: 0.95 }}
    >
    <RefreshCw className="h-4 w-4" />
    </motion.div>
    Sync Preferences
    </CardTitle>
    <CardDescription className="mb-2">
    Configure how your manga collection is synchronized to
    AniList.
    </CardDescription>
    </CardHeader>
    <CardContent className="space-y-6 pt-6">
    {/* Auto-Pause Settings */}
    <motion.div
    className="bg-muted/40 rounded-lg border p-4"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ delay: 0.3, duration: 0.5 }}
    >
    <div className="mb-4 flex items-center justify-between">
    <div className="space-y-0.5">
    <h3 className="text-sm font-medium">
    Auto-Pause Inactive Manga
    </h3>
    <p className="text-muted-foreground text-xs">
    Automatically pause manga that haven&apos;t been
    updated recently
    </p>
    </div>
    <div className="flex items-center gap-2">
    <Switch
    id="auto-pause"
    checked={syncConfig.autoPauseInactive}
    onCheckedChange={(checked) => {
    const newConfig = {
    ...syncConfig,
    autoPauseInactive: checked,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }}
    />
    </div>
    </div>

    <div className="space-y-4">
    <div className="grid gap-1.5">
    <label className="text-xs font-medium">
    Auto-Pause Threshold
    </label>
    <select
    className="border-input bg-background ring-offset-background focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
    value={
    useCustomThreshold
    ? "custom"
    : syncConfig.autoPauseThreshold.toString()
    }
    onChange={(e) => {
    const value = e.target.value;
    if (value === "custom") {
    setUseCustomThreshold(true);
    } else {
    setUseCustomThreshold(false);
    const newConfig = {
    ...syncConfig,
    autoPauseThreshold: Number(value),
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }
    }}
    disabled={!syncConfig.autoPauseInactive}
    >
    <option value="1">1 day</option>
    <option value="7">7 days</option>
    <option value="14">14 days</option>
    <option value="30">30 days</option>
    <option value="60">2 months</option>
    <option value="90">3 months</option>
    <option value="180">6 months</option>
    <option value="365">1 year</option>
    <option value="custom">Custom...</option>
    </select>
    </div>

    {useCustomThreshold && (
    <div className="grid gap-1.5">
    <label className="text-xs font-medium">
    Custom threshold (days)
    </label>
    <input
    type="number"
    min="1"
    className="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
    placeholder="Enter days"
    value={
    syncConfig.customAutoPauseThreshold ||
    syncConfig.autoPauseThreshold
    }
    onChange={(e) => {
    const value = parseInt(e.target.value);
    if (!isNaN(value) && value > 0) {
    const newConfig = {
    ...syncConfig,
    autoPauseThreshold: value,
    customAutoPauseThreshold: value,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }
    }}
    disabled={!syncConfig.autoPauseInactive}
    />
    </div>
    )}

    <Alert className="bg-amber-50 text-amber-700 dark:bg-amber-900/20 dark:text-amber-300">
    <AlertTriangle className="h-4 w-4" />
    <AlertDescription className="text-xs">
    Auto-pause applies to manga with status READING.
    </AlertDescription>
    </Alert>
    </div>
    </motion.div>

    {/* Status Priority Settings */}
    <motion.div
    className="bg-muted/40 rounded-lg border p-4"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ delay: 0.4, duration: 0.5 }}
    >
    <div className="mb-4">
    <h3 className="text-sm font-medium">Status Priority</h3>
    <p className="text-muted-foreground text-xs">
    Configure which status values take priority during
    synchronization
    </p>
    </div>

    <div className="space-y-3">
    <div className="flex items-center justify-between">
    <label className="text-sm" htmlFor="preserve-completed">
    Preserve completed status
    </label>
    <Switch
    id="preserve-completed"
    checked={syncConfig.preserveCompletedStatus}
    onCheckedChange={(checked) => {
    const newConfig = {
    ...syncConfig,
    preserveCompletedStatus: checked,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }}
    />
    </div>
    <div className="flex items-center justify-between">
    <label
    className="text-sm"
    htmlFor="prioritize-anilist-status"
    >
    Prioritize AniList status
    </label>
    <Switch
    id="prioritize-anilist-status"
    checked={syncConfig.prioritizeAniListStatus}
    onCheckedChange={(checked) => {
    const newConfig = {
    ...syncConfig,
    prioritizeAniListStatus: checked,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }}
    />
    </div>
    <div className="flex items-center justify-between">
    <label
    className="text-sm"
    htmlFor="prioritize-anilist-progress"
    >
    Prioritize AniList progress
    </label>
    <Switch
    id="prioritize-anilist-progress"
    checked={syncConfig.prioritizeAniListProgress}
    onCheckedChange={(checked) => {
    const newConfig = {
    ...syncConfig,
    prioritizeAniListProgress: checked,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }}
    />
    </div>
    <div className="flex items-center justify-between">
    <label
    className="text-sm"
    htmlFor="prioritize-anilist-score"
    >
    Prioritize AniList score
    </label>
    <Switch
    id="prioritize-anilist-score"
    checked={syncConfig.prioritizeAniListScore}
    onCheckedChange={(checked) => {
    const newConfig = {
    ...syncConfig,
    prioritizeAniListScore: checked,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }}
    />
    </div>
    </div>
    </motion.div>

    {/* Privacy Settings */}
    <motion.div
    className="bg-muted/40 rounded-lg border p-4"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ delay: 0.5, duration: 0.5 }}
    >
    <div className="mb-4 flex items-center justify-between">
    <div className="space-y-0.5">
    <h3 className="text-sm font-medium">
    Privacy Settings
    </h3>
    <p className="text-muted-foreground text-xs">
    Control privacy for synchronized entries
    </p>
    </div>
    <div className="flex items-center gap-2">
    <label className="text-sm" htmlFor="set-private">
    Set entries as private
    </label>
    <Switch
    id="set-private"
    checked={syncConfig.setPrivate}
    onCheckedChange={(checked) => {
    const newConfig = {
    ...syncConfig,
    setPrivate: checked,
    };
    setSyncConfig(newConfig);
    saveSyncConfig(newConfig);
    }}
    />
    </div>
    </div>
    </motion.div>
    </CardContent>
    </Card>
    </motion.div>
    </TabsContent>

    <TabsContent value="data" className="space-y-6">
    <motion.div variants={itemVariants} initial="hidden" animate="show">
    <Card className="bg-muted/10 overflow-hidden border-none shadow-md">
    <CardHeader className="mr-2 ml-2 rounded-t-lg rounded-b-lg bg-gradient-to-r from-blue-500/10 to-cyan-500/10">
    <CardTitle className="mt-2 flex items-center gap-2">
    <motion.div
    className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-r from-blue-500 to-cyan-500 text-white"
    whileHover={{ scale: 1.05 }}
    whileTap={{ scale: 0.95 }}
    >
    <Database className="h-4 w-4" />
    </motion.div>
    Data Management
    </CardTitle>
    <CardDescription className="mb-2">
    Manage your local data and clear application caches.
    </CardDescription>
    </CardHeader>
    <CardContent className="space-y-6 pt-6">
    <motion.div
    className="bg-muted/40 space-y-4 rounded-lg border p-4"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ delay: 0.3, duration: 0.5 }}
    >
    <div>
    <h3 className="flex items-center gap-2 text-sm font-medium">
    <Trash2 className="h-4 w-4 text-blue-500" />
    Clear Local Cache
    </h3>
    <p className="text-muted-foreground text-xs">
    Select which types of cached data to remove.
    </p>
    </div>

    <Separator />

    <div className="grid grid-cols-2 gap-3">
    <div className="space-y-2">
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.auth}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    auth: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Auth Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Authentication state
    </p>
    </div>
    </label>
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.settings}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    settings: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Settings Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Sync preferences
    </p>
    </div>
    </label>
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.sync}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    sync: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Sync Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Sync history
    </p>
    </div>
    </label>
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.import}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    import: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Import Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Import history
    </p>
    </div>
    </label>
    </div>

    <div className="space-y-2">
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.review}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    review: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Review Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Matching results
    </p>
    </div>
    </label>
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.manga}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    manga: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Manga Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Manga metadata
    </p>
    </div>
    </label>
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.search}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    search: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Search Cache
    </span>
    <p className="text-muted-foreground text-xs">
    Search results
    </p>
    </div>
    </label>
    <label className="hover:bg-muted flex items-center gap-2 rounded-md p-2">
    <input
    type="checkbox"
    className="border-primary text-primary h-4 w-4 rounded"
    checked={cachesToClear.other}
    onChange={(e) =>
    setCachesToClear({
    ...cachesToClear,
    other: e.target.checked,
    })
    }
    />
    <div>
    <span className="text-sm font-medium">
    Other Caches
    </span>
    <p className="text-muted-foreground text-xs">
    Miscellaneous application data
    </p>
    </div>
    </label>
    </div>
    </div>

    <div className="flex justify-between">
    <Button
    variant="link"
    size="sm"
    className="h-auto p-0 text-xs text-blue-600 dark:text-blue-400"
    onClick={() =>
    setCachesToClear({
    auth: true,
    settings: true,
    sync: true,
    import: true,
    review: true,
    manga: true,
    search: true,
    other: true,
    })
    }
    >
    Select All
    </Button>
    <Button
    variant="link"
    size="sm"
    className="h-auto p-0 text-xs text-blue-600 dark:text-blue-400"
    onClick={() =>
    setCachesToClear({
    auth: false,
    settings: false,
    sync: false,
    import: false,
    review: false,
    manga: false,
    search: false,
    other: false,
    })
    }
    >
    Deselect All
    </Button>
    </div>

    <Button
    onClick={handleClearCache}
    variant={cacheCleared ? "outline" : "default"}
    disabled={
    isClearing ||
    !Object.values(cachesToClear).some(Boolean)
    }
    className={`w-full ${
    cacheCleared
    ? "bg-green-50 text-green-600 hover:bg-green-100 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/40"
    : ""
    }`}
    >
    {isClearing ? (
    <>
    <RefreshCw className="mr-2 h-4 w-4 animate-spin" />
    Clearing Cache...
    </>
    ) : cacheCleared ? (
    <>
    <CheckCircle className="mr-2 h-4 w-4" />
    Cache Cleared Successfully
    </>
    ) : (
    <>
    <Trash2 className="mr-2 h-4 w-4" />
    Clear Selected Caches
    </>
    )}
    </Button>
    </motion.div>
    </CardContent>
    </Card>
    </motion.div>
    </TabsContent>
    </Tabs>
    </motion.div>

    {/* Check for Updates Section */}
    <Card className="bg-muted/10 mt-6 border-none shadow-sm">
    <CardContent className="space-y-4 py-6">
    <div className="flex flex-col space-y-2 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
    <div className="flex items-center gap-2">
    <RefreshCw className="text-muted-foreground h-4 w-4" />
    <h3 className="text-sm font-medium">Check for Updates</h3>
    </div>
    </div>
    <div className="flex flex-col gap-4 md:flex-row md:items-center md:gap-8">
    <RadioGroup
    value={updateChannel}
    onValueChange={(v) => setUpdateChannel(v as "stable" | "beta")}
    className="flex flex-row gap-4"
    aria-label="Update Channel"
    >
    <div className="flex items-center gap-2">
    <RadioGroupItem value="stable" id="update-stable" />
    <label htmlFor="update-stable" className="text-sm font-medium">
    Stable
    </label>
    </div>
    <div className="flex items-center gap-2">
    <RadioGroupItem value="beta" id="update-beta" />
    <label htmlFor="update-beta" className="text-sm font-medium">
    Beta/Early Access
    </label>
    </div>
    </RadioGroup>
    <Button
    onClick={handleCheckForUpdates}
    disabled={isCheckingUpdate}
    aria-label="Check for updates"
    className="w-full md:w-auto"
    >
    {isCheckingUpdate ? (
    <>
    <Loader2 className="mr-2 h-4 w-4 animate-spin" />
    Checking...
    </>
    ) : (
    <>
    <RefreshCw className="mr-2 h-4 w-4" />
    Check for Updates
    </>
    )}
    </Button>
    </div>
    {updateError && (
    <div className="text-sm text-red-600 dark:text-red-400">
    {updateError}
    </div>
    )}
    {updateInfo && (
    <div className="bg-muted/40 rounded-lg border p-4">
    <div className="mb-2 flex items-center gap-2">
    <Badge
    className={
    updateInfo.isBeta
    ? "bg-yellow-50 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300"
    : "bg-green-50 text-green-700 dark:bg-green-900/30 dark:text-green-300"
    }
    >
    {updateInfo.isBeta ? "Beta/Early Access" : "Stable"}
    </Badge>
    <span className="font-mono text-xs">
    Latest: {updateInfo.version}
    </span>
    <a
    role="button"
    tabIndex={0}
    aria-label="View release on GitHub"
    className="ml-2 cursor-pointer text-xs text-blue-600 underline hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
    onClick={handleOpenExternal(updateInfo.url)}
    onKeyDown={(e) => {
    if (e.key === "Enter" || e.key === " ") {
    e.preventDefault();
    if (window.electronAPI?.shell?.openExternal) {
    window.electronAPI.shell.openExternal(updateInfo.url);
    } else {
    window.open(
    updateInfo.url,
    "_blank",
    "noopener,noreferrer",
    );
    }
    }
    }}
    >
    View on GitHub
    </a>
    </div>
    <div className="mt-2 flex items-center gap-2">
    <span className="font-mono text-xs">
    Current: {getAppVersion()}
    </span>
    {(() => {
    const current = getAppVersion().replace(/^v/, "");
    const latest = updateInfo.version.replace(/^v/, "");
    if (current === latest) {
    return (
    <Badge className="bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300">
    Up to date
    </Badge>
    );
    }
    if (compareVersions(current, latest) < 0) {
    return (
    <Badge className="bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300">
    Update available
    </Badge>
    );
    }
    return (
    <Badge className="bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300">
    Development version
    </Badge>
    );
    })()}
    </div>
    </div>
    )}
    </CardContent>
    </Card>

    {/* Application Info Section */}
    <Card className="bg-muted/10 mt-6 border-none shadow-sm">
    <CardContent className="space-y-4 py-6">
    <div className="flex flex-col space-y-2 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
    <div className="flex items-center gap-2">
    <InfoIcon className="text-muted-foreground h-4 w-4" />
    <h3 className="text-sm font-medium">Application Info</h3>
    </div>
    <div className="flex items-center space-x-2">
    <Badge
    variant="outline"
    className="bg-indigo-50 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300"
    >
    Version {getAppVersion()}
    </Badge>
    {versionStatus === null ? (
    <Badge
    variant="outline"
    className="bg-gray-100 text-gray-600 dark:bg-gray-800/30 dark:text-gray-300"
    >
    Checking...
    </Badge>
    ) : versionStatus.status === "stable" ? (
    <Badge
    variant="outline"
    className="bg-green-50 text-green-700 dark:bg-green-900/30 dark:text-green-300"
    >
    Stable
    </Badge>
    ) : versionStatus.status === "beta" ? (
    <Badge
    variant="outline"
    className="bg-yellow-50 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300"
    >
    Beta
    </Badge>
    ) : (
    <Badge
    variant="outline"
    className="bg-blue-50 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
    >
    Development
    </Badge>
    )}
    </div>
    </div>
    <div className="grid gap-3 sm:grid-cols-3">
    <div className="bg-muted/40 rounded-lg p-3">
    <div className="flex items-center gap-2">
    <Clock className="text-muted-foreground h-4 w-4" />
    <p className="text-xs font-medium">Last Synced</p>
    </div>
    {(() => {
    try {
    const syncHistoryStr = localStorage.getItem(
    "anilist_sync_history",
    );
    if (!syncHistoryStr)
    return (
    <p className="text-muted-foreground ml-6 text-sm">
    Never
    </p>
    );

    const syncHistory = JSON.parse(syncHistoryStr);
    if (!syncHistory || !syncHistory.length)
    return (
    <p className="text-muted-foreground ml-6 text-sm">
    Never
    </p>
    );

    const latestSync = syncHistory[0];
    const timestamp = new Date(latestSync.timestamp);

    // Format the date nicely
    const formattedDate = timestamp.toLocaleString(undefined, {
    year: "numeric",
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
    });

    return (
    <div className="ml-6 space-y-1">
    <p className="text-muted-foreground text-sm">
    {formattedDate}
    </p>
    <div className="flex flex-wrap gap-2 text-xs">
    <span className="text-green-600 dark:text-green-400">
    ✓ {latestSync.successfulUpdates} successful
    </span>
    {latestSync.failedUpdates > 0 && (
    <span className="text-red-600 dark:text-red-400">
    ✗ {latestSync.failedUpdates} failed
    </span>
    )}
    <span className="text-muted-foreground">
    ({latestSync.totalEntries} total)
    </span>
    </div>
    </div>
    );
    } catch (e) {
    console.error("Error parsing sync history:", e);
    return (
    <p className="text-muted-foreground ml-6 text-sm">Never</p>
    );
    }
    })()}
    </div>
    <div className="bg-muted/40 rounded-lg p-3">
    <div className="flex items-center gap-2">
    <Key className="text-muted-foreground h-4 w-4" />
    <p className="text-xs font-medium">API Credentials</p>
    </div>
    <p className="text-muted-foreground ml-6 text-sm">
    {authState.credentialSource === "default"
    ? "Using default"
    : "Using custom"}
    </p>
    </div>
    <div className="bg-muted/40 rounded-lg p-3">
    <div className="flex items-center gap-2">
    <UserCircle className="text-muted-foreground h-4 w-4" />
    <p className="text-xs font-medium">Authentication Status</p>
    </div>
    <p className="text-muted-foreground ml-6 text-sm">
    {authState.isAuthenticated ? (
    <span className="flex items-center gap-1">
    <Badge
    variant="default"
    className="h-1.5 w-1.5 rounded-full bg-green-500 p-0"
    />
    Connected
    </span>
    ) : (
    <span className="flex items-center gap-1">
    <Badge
    variant="destructive"
    className="h-1.5 w-1.5 rounded-full p-0"
    />
    Not connected
    </span>
    )}
    </p>
    </div>
    </div>

    <div className="flex items-center justify-center space-x-3 pt-2">
    <a
    href="https://github.com/RLAlpha49/KenmeiToAnilist"
    target="_blank"
    rel="noopener noreferrer"
    className="text-muted-foreground hover:text-foreground inline-flex items-center text-xs font-medium transition-colors"
    onClick={handleOpenExternal(
    "https://github.com/RLAlpha49/KenmeiToAnilist",
    )}
    >
    <ExternalLink className="mr-1 h-3 w-3" />
    GitHub
    </a>
    <Separator orientation="vertical" className="h-4" />
    <span className="text-muted-foreground/60 text-xs">
    Made with ❤️ for manga readers
    </span>
    </div>
    </CardContent>
    </Card>
    </motion.div>
    );
    }