<script lang="ts">
  import { onMount } from 'svelte';
  import { goto } from '$app/navigation';
  import { page } from '$app/stores';
  import { NostrClient } from '$lib/services/nostr/nostr-client.js';
  import { KIND } from '$lib/types/nostr.js';
  import type { NostrEvent } from '$lib/types/nostr.js';
  import { nip19 } from 'nostr-tools';
  import { ForkCountService } from '$lib/services/nostr/fork-count-service.js';
  import { getPublicKeyWithNIP07, isNIP07Available } from '$lib/services/nostr/nip07-signer.js';
  import { userStore } from '$lib/stores/user-store.js';
  import { hasUnlimitedAccess } from '$lib/utils/user-access.js';

  // Registered repos (with domain in clone URLs)
  // Also includes local-only forks (marked with isLocalOnly flag)
  let registeredRepos = $state<Array<{ event: NostrEvent; npub: string; repoName: string; isLocalOnly?: boolean }>>([]);
  let allRegisteredRepos = $state<Array<{ event: NostrEvent; npub: string; repoName: string; isLocalOnly?: boolean }>>([]);
  
  // Local clones (repos without domain in clone URLs)
  let localRepos = $state<Array<{ npub: string; repoName: string; announcement: NostrEvent | null; lastModified: number }>>([]);
  let allLocalRepos = $state<Array<{ npub: string; repoName: string; announcement: NostrEvent | null; lastModified: number }>>([]);
  
  let loading = $state(true);
  let loadingLocal = $state(false);
  let error = $state<string | null>(null);
  let forkCounts = $state<Map<string, number>>(new Map());
  let userPubkey = $state<string | null>(null);
  let userPubkeyHex = $state<string | null>(null);
  let contactPubkeys = $state<Set<string>>(new Set());
  let deletingRepo = $state<{ npub: string; repo: string } | null>(null);
  
  // User's own repositories (where they are owner or maintainer)
  // Also includes repos they transferred away (marked as transferred)
  let myRepos = $state<Array<{ event: NostrEvent; npub: string; repoName: string; transferred?: boolean; currentOwner?: string }>>([]);
  let loadingMyRepos = $state(false);

  // Most Favorited Repositories
  let mostFavoritedRepos = $state<Array<{ event: NostrEvent; npub: string; repoName: string; favoriteCount: number }>>([]);
  let loadingMostFavorited = $state(false);
  let mostFavoritedPage = $state(0);
  let mostFavoritedCache = $state<{ data: typeof mostFavoritedRepos; timestamp: number } | null>(null);
  const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

  import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
  const forkCountService = new ForkCountService(DEFAULT_NOSTR_RELAYS);

  const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS);

  onMount(async () => {
    await loadRepos();
    await loadUserAndContacts();
    await loadMyRepos();
    await loadMostFavoritedRepos();
  });

  // Reload repos when page becomes visible (e.g., after returning from another page)
  $effect(() => {
    if (typeof document !== 'undefined') {
      const handleVisibilityChange = () => {
        if (document.visibilityState === 'visible') {
          // Reload repos when page becomes visible to catch newly published repos
          loadRepos().catch(err => console.warn('Failed to reload repos on visibility change:', err));
          loadMyRepos().catch(err => console.warn('Failed to reload my repos on visibility change:', err));
        }
      };
      document.addEventListener('visibilitychange', handleVisibilityChange);
      return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
    }
  });

  // Reload my repos when navigating to repos page (e.g., after transfer)
  $effect(() => {
    if ($page.url.pathname === '/repos' && userPubkeyHex) {
      loadMyRepos().catch(err => console.warn('Failed to reload my repos on page navigation:', err));
    }
  });

  // Sync with userStore - if userStore says logged out, clear local state
  $effect(() => {
    const currentUser = $userStore;
    if (!currentUser.userPubkey || !currentUser.userPubkeyHex) {
      // User is logged out according to store - clear local state
      const wasLoggedIn = userPubkey !== null || userPubkeyHex !== null;
      userPubkey = null;
      userPubkeyHex = null;
      myRepos = [];
      contactPubkeys.clear();
      
      // If user was logged in before, reload repos to hide private ones
      if (wasLoggedIn) {
        loadRepos().catch(err => console.warn('Failed to reload repos after logout:', err));
        loadLocalRepos().catch(err => console.warn('Failed to reload local repos after logout:', err));
      }
    } else if (currentUser.userPubkey && currentUser.userPubkeyHex) {
      // User is logged in according to store - sync local state
      // Only update if different to avoid unnecessary reloads
      const wasDifferent = userPubkey !== currentUser.userPubkey || userPubkeyHex !== currentUser.userPubkeyHex;
      const wasLoggedOut = userPubkey === null && userPubkeyHex === null;
      
      if (wasDifferent) {
        userPubkey = currentUser.userPubkey;
        userPubkeyHex = currentUser.userPubkeyHex;
        
        // Reload everything when user logs in or pubkey changes
        loadRepos().catch(err => console.warn('Failed to reload repos after login:', err));
        loadLocalRepos().catch(err => console.warn('Failed to reload local repos after login:', err));
        loadMyRepos().catch(err => console.warn('Failed to load my repos after store sync:', err));
        loadContacts().catch(err => console.warn('Failed to load contacts after store sync:', err));
      }
    }
  });

  async function loadUserAndContacts() {
    // Check userStore first - if user is logged out, don't try to get pubkey
    const currentUser = $userStore;
    if (!currentUser.userPubkey || !currentUser.userPubkeyHex) {
      userPubkey = null;
      userPubkeyHex = null;
      contactPubkeys.clear();
      return;
    }

    // If userStore has user info, use it
    if (currentUser.userPubkey && currentUser.userPubkeyHex) {
      userPubkey = currentUser.userPubkey;
      userPubkeyHex = currentUser.userPubkeyHex;
      contactPubkeys.add(userPubkeyHex); // Include user's own repos
      
      // Still fetch contacts even if we have store data
      await loadContacts();
      return;
    }

    // Fallback: try to get from NIP-07 if store doesn't have it
    if (!isNIP07Available()) {
      return;
    }

    try {
      const pubkey = await getPublicKeyWithNIP07();
      if (!pubkey) return;
      
      userPubkey = pubkey;
      
      // Convert npub to hex for API calls
      // NIP-07 may return either npub or hex, so check format first
      if (/^[0-9a-f]{64}$/i.test(userPubkey)) {
        // Already hex format
        userPubkeyHex = userPubkey.toLowerCase();
        contactPubkeys.add(userPubkeyHex); // Include user's own repos
      } else {
        // Try to decode as npub
        try {
          const decoded = nip19.decode(userPubkey);
          if (decoded.type === 'npub') {
            userPubkeyHex = decoded.data as string;
            contactPubkeys.add(userPubkeyHex); // Include user's own repos
          }
        } catch (err) {
          // If decode fails, might still be hex or invalid - skip
          console.warn('Failed to decode user pubkey:', err);
        }
      }
      
      if (userPubkeyHex) {
        await loadContacts();
      }
    } catch (err) {
      console.warn('Failed to load user or contacts:', err);
    }
  }

  async function loadContacts() {
    if (!userPubkeyHex) return;
    
    try {
      // Fetch user's kind 3 contact list
      const contactEvents = await nostrClient.fetchEvents([
        {
          kinds: [KIND.CONTACT_LIST],
          authors: [userPubkeyHex],
          limit: 1
        }
      ]);
      
      if (contactEvents.length > 0) {
        const contactEvent = contactEvents[0];
        // Extract pubkeys from 'p' tags
        for (const tag of contactEvent.tags) {
          if (tag[0] === 'p' && tag[1]) {
            let pubkey = tag[1];
            // Try to decode if it's an npub
            try {
              const decoded = nip19.decode(pubkey);
              if (decoded.type === 'npub') {
                pubkey = decoded.data as string;
              }
            } catch {
              // Assume it's already a hex pubkey
            }
            if (pubkey) {
              contactPubkeys.add(pubkey);
            }
          }
        }
      }
    } catch (err) {
      console.warn('Failed to load contacts:', err);
    }
  }

  async function loadMyRepos() {
    if (!userPubkey || !userPubkeyHex) {
      myRepos = [];
      return;
    }

    loadingMyRepos = true;
    try {
      // Fetch all repos where user is current owner
      const ownerRepos = await nostrClient.fetchEvents([
        {
          kinds: [KIND.REPO_ANNOUNCEMENT],
          authors: [userPubkeyHex],
          limit: 100
        }
      ]);

      const repos: Array<{ event: NostrEvent; npub: string; repoName: string; transferred?: boolean; currentOwner?: string }> = [];
      
      // Add repos where user is current owner
      for (const event of ownerRepos) {
        const dTag = event.tags.find(t => t[0] === 'd')?.[1];
        if (!dTag) continue;
        
        try {
          const npub = nip19.npubEncode(event.pubkey);
          repos.push({
            event,
            npub,
            repoName: dTag,
            transferred: false
          });
        } catch (err) {
          console.warn('Failed to encode npub for repo:', err);
        }
      }

      // Fetch repos that were transferred FROM this user (where they were original owner)
      // Search for transfer events where this user is the 'from' pubkey
      const { OwnershipTransferService } = await import('$lib/services/nostr/ownership-transfer-service.js');
      const ownershipService = new OwnershipTransferService(DEFAULT_NOSTR_RELAYS);
      
      // Get all repos where user was original owner
      const originalOwnerRepos = await nostrClient.fetchEvents([
        {
          kinds: [KIND.REPO_ANNOUNCEMENT],
          authors: [userPubkeyHex],
          limit: 100
        }
      ]);

      for (const originalEvent of originalOwnerRepos) {
        const dTag = originalEvent.tags.find(t => t[0] === 'd')?.[1];
        if (!dTag) continue;

        // Check current owner
        const currentOwner = await ownershipService.getCurrentOwner(userPubkeyHex, dTag);
        
        // If current owner is different, this repo was transferred
        if (currentOwner !== userPubkeyHex) {
          // Fetch the current announcement from the new owner
          const currentAnnouncements = await nostrClient.fetchEvents([
            {
              kinds: [KIND.REPO_ANNOUNCEMENT],
              authors: [currentOwner],
              '#d': [dTag],
              limit: 1
            }
          ]);

          if (currentAnnouncements.length > 0) {
            try {
              const npub = nip19.npubEncode(userPubkeyHex); // Original owner npub
              repos.push({
                event: currentAnnouncements[0], // Use current announcement
                npub,
                repoName: dTag,
                transferred: true,
                currentOwner
              });
            } catch (err) {
              console.warn('Failed to encode npub for transferred repo:', err);
            }
          }
        }
      }

      // Sort by created_at descending (newest first)
      repos.sort((a, b) => b.event.created_at - a.event.created_at);
      
      myRepos = repos;
    } catch (err) {
      console.warn('Failed to load my repos:', err);
      myRepos = [];
    } finally {
      loadingMyRepos = false;
    }
  }

  async function loadRepos(triggerPoll = false) {
    loading = true;
    error = null;
    
    try {
      const gitDomain = $page.data.gitDomain || 'localhost:6543';
      const url = `/api/repos/list?domain=${encodeURIComponent(gitDomain)}`;
      
      const response = await fetch(url, {
        headers: userPubkeyHex ? {
          'X-User-Pubkey': userPubkeyHex
        } : {}
      });
      
      if (!response.ok) {
        throw new Error(`Failed to load repositories: ${response.statusText}`);
      }
      
      const data = await response.json();
      
      // API returns { registered, total }
      registeredRepos = data.registered || [];
      
      // Load local repos and merge them into registered repos list
      await loadLocalRepos();
      
      // Merge local repos into registered repos list with special icon
      const mergedRepos = [...registeredRepos];
      for (const localRepo of localRepos) {
        // Check if this local repo is already in registered repos
        const exists = mergedRepos.some(r => r.npub === localRepo.npub && r.repoName === localRepo.repoName);
        if (!exists && localRepo.announcement) {
          // Add local repo to registered list with a flag indicating it's local-only
          mergedRepos.push({
            event: localRepo.announcement,
            npub: localRepo.npub,
            repoName: localRepo.repoName,
            isLocalOnly: true // Flag to show special icon
          });
        }
      }
      
      registeredRepos = mergedRepos;
      allRegisteredRepos = [...registeredRepos];
      
      // Load fork counts for registered repos (in parallel, but don't block)
      loadForkCounts(registeredRepos.map(r => r.event)).catch(err => {
        console.warn('[RepoList] Failed to load some fork counts:', err);
      });
      
      // If triggerPoll is true, trigger a poll and then refresh the list
      if (triggerPoll) {
        try {
          // Trigger poll (non-blocking)
          const pollResponse = await fetch('/api/repos/poll', {
            method: 'POST',
            headers: userPubkeyHex ? {
              'X-User-Pubkey': userPubkeyHex
            } : {}
          });
          
          if (pollResponse.ok) {
            // Wait a bit for the poll to process (lazy - don't wait for full completion)
            // Give it 2-3 seconds to provision repos
            await new Promise(resolve => setTimeout(resolve, 2500));
            
            // Refresh the list after poll
            await loadRepos(false);
          }
        } catch (pollErr) {
          // Don't fail the whole operation if poll fails
          console.warn('[RepoList] Failed to trigger poll:', pollErr);
        }
      }
    } catch (e) {
      error = String(e);
      console.error('[RepoList] Failed to load repos:', e);
    } finally {
      loading = false;
    }
  }
  
  async function loadLocalRepos() {
    loadingLocal = true;
    
    try {
      const gitDomain = $page.data.gitDomain || 'localhost:6543';
      const url = `/api/repos/local?domain=${encodeURIComponent(gitDomain)}`;
      
      const response = await fetch(url, {
        headers: userPubkeyHex ? {
          'X-User-Pubkey': userPubkeyHex
        } : {}
      });
      
      if (!response.ok) {
        console.warn('Failed to load local repos:', response.statusText);
        return;
      }
      
      const data = await response.json();
      // API returns array of { npub, repoName, announcement }
      localRepos = data.map((item: { npub: string; repoName: string; announcement: NostrEvent }) => ({
        npub: item.npub,
        repoName: item.repoName,
        announcement: item.announcement,
        lastModified: item.announcement?.created_at ? item.announcement.created_at * 1000 : Date.now()
      }));
      allLocalRepos = [...localRepos];
    } catch (e) {
      console.warn('[RepoList] Failed to load local repos:', e);
    } finally {
      loadingLocal = false;
    }
  }
  
  async function deleteLocalRepo(npub: string, repo: string) {
    if (!confirm(`Are you sure you want to delete the local clone of "${repo}"?\n\nThis will permanently remove the repository from this server. The announcement on Nostr will NOT be deleted.\n\nThis action cannot be undone.\n\nClick OK to delete, or Cancel to abort.`)) {
      return;
    }
    
    deletingRepo = { npub, repo };
    
    try {
      const response = await fetch(`/api/repos/${npub}/${repo}/delete`, {
        method: 'DELETE',
        headers: userPubkeyHex ? {
          'X-User-Pubkey': userPubkeyHex
        } : {}
      });
      
      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || 'Failed to delete repository');
      }
      
      // Remove from local repos list
      localRepos = localRepos.filter(r => !(r.npub === npub && r.repoName === repo));
      allLocalRepos = [...localRepos];
      
      alert('Repository deleted successfully');
    } catch (e) {
      alert(`Failed to delete repository: ${e instanceof Error ? e.message : String(e)}`);
    } finally {
      deletingRepo = null;
    }
  }
  
  function registerRepo(npub: string, repo: string) {
    // Navigate to signup page with repo pre-filled
    goto(`/signup?npub=${encodeURIComponent(npub)}&repo=${encodeURIComponent(repo)}`);
  }

  async function loadMostFavoritedRepos() {
    // Check cache first
    if (mostFavoritedCache && Date.now() - mostFavoritedCache.timestamp < CACHE_TTL) {
      updateMostFavoritedPage();
      return;
    }

    loadingMostFavorited = true;
    try {
      // Fetch up to 1000 bookmark events (kind 10003)
      const bookmarkEvents = await nostrClient.fetchEvents([
        {
          kinds: [KIND.BOOKMARKS],
          limit: 1000
        }
      ]);

      // Count how many times each repo a-tag appears
      const repoCounts = new Map<string, { count: number; aTag: string }>();
      
      for (const bookmark of bookmarkEvents) {
        for (const tag of bookmark.tags) {
          if (tag[0] === 'a' && tag[1]?.startsWith(`${KIND.REPO_ANNOUNCEMENT}:`)) {
            const aTag = tag[1];
            const current = repoCounts.get(aTag) || { count: 0, aTag };
            current.count++;
            repoCounts.set(aTag, current);
          }
        }
      }

      // Convert to array and sort by count
      const repoCountsArray = Array.from(repoCounts.entries())
        .map(([aTag, data]) => ({ aTag, count: data.count }))
        .sort((a, b) => b.count - a.count);

      // Fetch repo announcements for the top repos
      const topRepos: Array<{ event: NostrEvent; npub: string; repoName: string; favoriteCount: number }> = [];
      
      for (const { aTag, count } of repoCountsArray.slice(0, 100)) {
        // Parse a-tag: 30617:pubkey:d-tag
        const parts = aTag.split(':');
        if (parts.length >= 3) {
          const pubkey = parts[1];
          const dTag = parts[2];
          
          // Fetch the repo announcement
          const announcements = await nostrClient.fetchEvents([
            {
              kinds: [KIND.REPO_ANNOUNCEMENT],
              authors: [pubkey],
              '#d': [dTag],
              limit: 1
            }
          ]);

          if (announcements.length > 0) {
            try {
              const npub = nip19.npubEncode(pubkey);
              topRepos.push({
                event: announcements[0],
                npub,
                repoName: dTag,
                favoriteCount: count
              });
            } catch (err) {
              console.warn('Failed to encode npub for favorited repo:', err);
            }
          }
        }
      }

      // Cache the results
      mostFavoritedCache = {
        data: topRepos,
        timestamp: Date.now()
      };

      updateMostFavoritedPage();
    } catch (err) {
      console.error('Failed to load most favorited repos:', err);
    } finally {
      loadingMostFavorited = false;
    }
  }

  function updateMostFavoritedPage() {
    if (!mostFavoritedCache) return;
    
    const start = mostFavoritedPage * 10;
    const end = start + 10;
    mostFavoritedRepos = mostFavoritedCache.data.slice(start, end);
  }

  function nextMostFavoritedPage() {
    if (!mostFavoritedCache) return;
    const maxPage = Math.ceil(mostFavoritedCache.data.length / 10) - 1;
    if (mostFavoritedPage < maxPage) {
      mostFavoritedPage++;
      updateMostFavoritedPage();
    }
  }

  function prevMostFavoritedPage() {
    if (mostFavoritedPage > 0) {
      mostFavoritedPage--;
      updateMostFavoritedPage();
    }
  }

  // Background refresh of cache
  async function refreshMostFavoritedCache() {
    if (!mostFavoritedCache || Date.now() - mostFavoritedCache.timestamp >= CACHE_TTL) {
      await loadMostFavoritedRepos();
    }
  }

  // Start background refresh
  $effect(() => {
    if (typeof window !== 'undefined') {
      const interval = setInterval(() => {
        refreshMostFavoritedCache().catch(err => console.warn('Background refresh failed:', err));
      }, CACHE_TTL);
      return () => clearInterval(interval);
    }
  });

  async function loadForkCounts(repoEvents: NostrEvent[]) {
    const counts = new Map<string, number>();
    
    // Extract owner pubkey and repo name for each repo
    const forkCountPromises = repoEvents.map(async (event) => {
      try {
        const dTag = event.tags.find(t => t[0] === 'd')?.[1];
        if (!dTag) return;
        
        const repoKey = `${event.pubkey}:${dTag}`;
        const count = await forkCountService.getForkCount(event.pubkey, dTag);
        counts.set(repoKey, count);
      } catch (err) {
        // Ignore individual failures
      }
    });

    await Promise.all(forkCountPromises);
    forkCounts = counts;
  }

  function getForkCount(event: NostrEvent): number {
    const dTag = event.tags.find(t => t[0] === 'd')?.[1];
    if (!dTag) return 0;
    const repoKey = `${event.pubkey}:${dTag}`;
    return forkCounts.get(repoKey) || 0;
  }
  
  function isOwner(npub: string, repoName: string): boolean {
    if (!userPubkeyHex) return false;
    try {
      const decoded = nip19.decode(npub);
      if (decoded.type === 'npub') {
        return (decoded.data as string) === userPubkeyHex;
      }
    } catch {
      // Invalid npub
    }
    return false;
  }

  function getRepoName(event: NostrEvent): string {
    const nameTag = event.tags.find((t: string[]) => t[0] === 'name' && t[1]);
    if (nameTag?.[1]) return nameTag[1];
    
    const dTag = event.tags.find((t: string[]) => t[0] === 'd')?.[1];
    if (dTag) return dTag;
    
    return `Repository ${event.id.slice(0, 8)}`;
  }

  function getRepoDescription(event: NostrEvent): string {
    const descTag = event.tags.find((t: string[]) => t[0] === 'description' && t[1]);
    return descTag?.[1] || '';
  }

  function getRepoImage(event: NostrEvent): string | null {
    const imageTag = event.tags.find((t: string[]) => t[0] === 'image' && t[1]);
    return imageTag?.[1] || null;
  }

  function getRepoBanner(event: NostrEvent): string | null {
    const bannerTag = event.tags.find((t: string[]) => t[0] === 'banner' && t[1]);
    return bannerTag?.[1] || null;
  }

  function getCloneUrls(event: NostrEvent): string[] {
    const urls: string[] = [];
    
    for (const tag of event.tags) {
      if (tag[0] === 'clone') {
        for (let i = 1; i < tag.length; i++) {
          const url = tag[i];
          if (url && typeof url === 'string') {
            urls.push(url);
          }
        }
      }
    }
    
    return urls;
  }

</script>

<svelte:head>
  <title>Repositories - GitRepublic</title>
  <meta name="description" content="Browse repositories on GitRepublic - Decentralized Git Hosting on Nostr" />
</svelte:head>

<div class="container">
  <main>
    {#if userPubkey && myRepos.length > 0}
      <!-- My Repositories Section -->
      <div class="repo-section">
        <div class="section-header">
          <h3>My Repositories</h3>
          <span class="section-badge">{myRepos.length}</span>
        </div>
        <div class="repos-list">
          {#each myRepos as item}
            {@const repo = item.event}
            {@const repoImage = getRepoImage(repo)}
            {@const isTransferred = item.transferred || false}
            <div class="repo-card repo-card-my" class:transferred={isTransferred}>
              <div class="repo-card-content">
                <div class="repo-header">
                  <div class="repo-header-text">
                    <div class="repo-title-row">
                      {#if repoImage}
                        <img src={repoImage} alt="Repository" class="repo-avatar" />
                      {/if}
                      <h3>{getRepoName(repo)}</h3>
                      {#if isTransferred}
                        <span class="transferred-badge" title="Transferred to another owner">↗</span>
                      {/if}
                    </div>
                    {#if getRepoDescription(repo)}
                      <p class="description">{getRepoDescription(repo)}</p>
                    {/if}
                  </div>
                  <a href="/repos/{item.npub}/{item.repoName}" class="view-button" title="View repository">
                    <img src="/icons/arrow-right.svg" alt="View" />
                  </a>
                </div>
                <div class="repo-meta">
                  <span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span>
                  {#if getForkCount(repo) > 0}
                    {@const forkCount = getForkCount(repo)}
                    <span class="fork-count">
                      <img src="/icons/git-fork.svg" alt="Fork" class="fork-icon" />
                      {forkCount} fork{forkCount === 1 ? '' : 's'}
                    </span>
                  {/if}
                </div>
              </div>
            </div>
          {/each}
        </div>
      </div>
    {/if}

    <!-- Most Favorited Repositories -->
    <div class="repo-section">
      <div class="section-header">
        <h3>Most Favorited Repositories</h3>
        <span class="section-badge">{mostFavoritedRepos.length}</span>
        {#if loadingMostFavorited}
          <span class="loading-indicator">Loading...</span>
        {/if}
      </div>
      {#if loadingMostFavorited && mostFavoritedRepos.length === 0}
        <div class="loading">Loading most favorited repositories...</div>
      {:else if mostFavoritedRepos.length === 0}
        <div class="empty">No favorited repositories found.</div>
      {:else}
        <div class="repos-list">
          {#each mostFavoritedRepos as item}
            {@const repo = item.event}
            {@const repoImage = getRepoImage(repo)}
            <div class="repo-card repo-card-favorited">
              <div class="repo-card-content">
                <div class="repo-header">
                  <div class="repo-header-text">
                    <div class="repo-title-row">
                      {#if repoImage}
                        <img src={repoImage} alt="Repository" class="repo-avatar" />
                      {/if}
                      <h3>{getRepoName(repo)}</h3>
                    </div>
                    {#if getRepoDescription(repo)}
                      <p class="description">{getRepoDescription(repo)}</p>
                    {/if}
                  </div>
                  <a href="/repos/{item.npub}/{item.repoName}" class="view-button" title="View repository">
                    <img src="/icons/arrow-right.svg" alt="View" />
                  </a>
                </div>
                <div class="repo-meta">
                  <span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span>
                  <span class="favorite-count">⭐ {item.favoriteCount} {item.favoriteCount === 1 ? 'favorite' : 'favorites'}</span>
                  {#if getForkCount(repo) > 0}
                    {@const forkCount = getForkCount(repo)}
                    <span class="fork-count">
                      <img src="/icons/git-fork.svg" alt="Fork" class="fork-icon" />
                      {forkCount} fork{forkCount === 1 ? '' : 's'}
                    </span>
                  {/if}
                </div>
              </div>
            </div>
          {/each}
        </div>
        {#if mostFavoritedCache && mostFavoritedCache.data.length > 10}
          <div class="pagination">
            <button onclick={prevMostFavoritedPage} disabled={mostFavoritedPage === 0}>
              Previous
            </button>
            <span class="page-info">
              Page {mostFavoritedPage + 1} of {Math.ceil(mostFavoritedCache.data.length / 10)}
            </span>
            <button 
              onclick={nextMostFavoritedPage} 
              disabled={mostFavoritedPage >= Math.ceil(mostFavoritedCache.data.length / 10) - 1}
            >
              Next
            </button>
          </div>
        {/if}
      {/if}
    </div>

    <div class="repos-header">
      <h2>Repositories on {$page.data.gitDomain || 'localhost:6543'}</h2>
      <button onclick={() => loadRepos(true)} disabled={loading}>
        {loading ? 'Loading...' : 'Refresh'}
      </button>
    </div>


    {#if error}
      <div class="error">
        Error loading repositories: {error}
      </div>
    {:else if loading}
      <div class="loading">Loading repositories...</div>
    {:else}
      <!-- Registered Repositories Section -->
      <div class="repo-section">
        <div class="section-header">
          <h3>Registered Repositories</h3>
          <span class="section-badge">{registeredRepos.length}</span>
        </div>
        {#if registeredRepos.length === 0}
          <div class="empty">No registered repositories found.</div>
        {:else}
          <div class="repos-list">
            {#each registeredRepos as item}
              {@const repo = item.event}
              {@const repoImage = getRepoImage(repo)}
              {@const isLocalOnly = item.isLocalOnly || false}
              <div class="repo-card repo-card-registered">
                <div class="repo-card-content">
                  <div class="repo-header">
                    <div class="repo-header-text">
                      <div class="repo-title-row">
                        {#if repoImage}
                          <img src={repoImage} alt="Repository" class="repo-avatar" />
                        {/if}
                        <h3>{getRepoName(repo)}</h3>
                        {#if isLocalOnly}
                          <img src="/icons/hard-drive.svg" alt="Local-only fork" class="local-only-icon" title="Local-only fork (not published to Nostr)" />
                        {/if}
                      </div>
                      {#if getRepoDescription(repo)}
                        <p class="description">{getRepoDescription(repo)}</p>
                      {/if}
                    </div>
                    <a href="/repos/{item.npub}/{item.repoName}" class="view-button" title="View repository">
                      <img src="/icons/arrow-right.svg" alt="View" />
                    </a>
                  </div>
                  <div class="repo-meta">
                    <span>Created: {new Date(repo.created_at * 1000).toLocaleDateString()}</span>
                    {#if isLocalOnly}
                      <span class="local-only-badge" title="Local-only fork (not published to Nostr)">Local Fork</span>
                    {/if}
                    {#if getForkCount(repo) > 0}
                      {@const forkCount = getForkCount(repo)}
                      <span class="fork-count">🍴 {forkCount} fork{forkCount === 1 ? '' : 's'}</span>
                    {/if}
                  </div>
                </div>
              </div>
            {/each}
          </div>
        {/if}
      </div>

    {/if}
  </main>
</div>

<style>
  .loading-indicator {
    color: var(--text-secondary, #666);
    font-size: 0.875rem;
  }

  .pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 1rem;
    margin-top: 1.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--border-color, #e0e0e0);
  }

  .pagination button {
    padding: 0.5rem 1rem;
    background: var(--accent, #007bff);
    color: var(--accent-text, #ffffff);
    border: none;
    border-radius: 0.25rem;
    cursor: pointer;
    font-weight: 500;
    transition: all 0.2s ease;
  }

  .pagination button:hover:not(:disabled) {
    background: var(--accent-hover, #0056b3);
  }

  .pagination button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .page-info {
    color: var(--text-secondary, #666);
    font-size: 0.9rem;
  }

  .repos-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
  }

  .repo-card {
    background: var(--card-bg, #ffffff);
    border: 1px solid var(--border-color, #e0e0e0);
    border-radius: 0.5rem;
    overflow: hidden;
    transition: box-shadow 0.2s ease;
    display: flex;
    flex-direction: column;
    min-height: 160px;
    height: 100%;
  }

  .repo-card:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }

  .repo-card-content {
    padding: 1rem;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  .repo-header {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
  }

  .repo-title-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .repo-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    border: 1px solid var(--border-color, #e0e0e0);
  }

  .local-only-icon {
    width: 16px;
    height: 16px;
    opacity: 0.7;
    flex-shrink: 0;
    filter: brightness(0) invert(1);
  }

  .local-only-badge {
    background: var(--bg-tertiary, #f5f5f5);
    color: var(--text-secondary, #666);
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
    font-size: 0.75rem;
    font-weight: 500;
  }

  .repo-header-text {
    flex: 1;
    min-width: 0;
  }

  .repo-header-text h3 {
    margin: 0 0 0.25rem 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary, #1a1a1a);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .repo-header-text .description {
    margin: 0;
    font-size: 0.875rem;
    color: var(--text-secondary, #666);
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.4;
  }

  .view-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    background: var(--accent, #007bff);
    color: var(--accent-text, #ffffff);
    text-decoration: none;
    border-radius: 0.25rem;
    transition: all 0.2s ease;
    flex-shrink: 0;
  }

  .view-button:hover {
    background: var(--accent-hover, #0056b3);
  }

  .view-button img {
    width: 18px;
    height: 18px;
    filter: brightness(0) invert(1);
  }

  .repo-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: auto;
    padding-top: 0.75rem;
    border-top: 1px solid var(--border-color, #e0e0e0);
    font-size: 0.75rem;
    color: var(--text-secondary, #666);
  }

  .fork-count {
    color: var(--text-secondary, #666);
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
  }

  .fork-icon {
    width: 14px;
    height: 14px;
    opacity: 0.8;
    filter: brightness(0) invert(1);
  }

  .favorite-count {
    color: var(--text-secondary, #666);
    font-weight: 500;
  }

  .transferred-badge {
    font-size: 0.875rem;
    color: var(--text-secondary, #666);
    margin-left: 0.5rem;
    opacity: 0.7;
  }

  .repo-card.transferred {
    opacity: 0.7;
  }

  .repo-card.transferred:hover {
    opacity: 0.9;
  }

  .repo-section {
    margin: 2rem 0;
  }

  .section-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
  }

  .section-header h3 {
    margin: 0;
    font-size: 1.25rem;
    color: var(--text-primary, #1a1a1a);
  }

  .section-badge {
    padding: 0.25rem 0.75rem;
    background: var(--bg-secondary, #f5f5f5);
    border: 1px solid var(--border-color, #e0e0e0);
    border-radius: 1rem;
    font-size: 0.875rem;
    color: var(--text-secondary, #666);
  }

  @media (max-width: 768px) {
    .repos-list {
      grid-template-columns: 1fr;
    }
  }
</style>
