<script lang="ts">
  import ProfileBadge from '../../components/layout/ProfileBadge.svelte';
  import MarkdownRenderer from '../../components/content/MarkdownRenderer.svelte';
  import PaymentAddresses from './PaymentAddresses.svelte';
  import FeedPost from '../feed/FeedPost.svelte';
  import ThreadDrawer from '../feed/ThreadDrawer.svelte';
  import { fetchProfile, fetchUserStatus, type ProfileData } from '../../services/user-data.js';
  import { nostrClient } from '../../services/nostr/nostr-client.js';
  import { relayManager } from '../../services/nostr/relay-manager.js';
  import { sessionManager } from '../../services/auth/session-manager.js';
  import { onMount } from 'svelte';
  import { page } from '$app/stores';
  import { nip19 } from 'nostr-tools';
  import type { NostrEvent } from '../../types/nostr.js';
  import { KIND } from '../../types/kind-lookup.js';

  let profile = $state<ProfileData | null>(null);
  let userStatus = $state<string | null>(null);
  let posts = $state<NostrEvent[]>([]);
  let responses = $state<NostrEvent[]>([]);
  let interactionsWithMe = $state<NostrEvent[]>([]);
  let loading = $state(true);
  let activeTab = $state<'posts' | 'responses' | 'interactions'>('posts');
  let nip05Validations = $state<Record<string, boolean | null>>({}); // null = checking, true = valid, false = invalid
  
  // Cache for NIP-05 validation results (nip05+pubkey -> result)
  // This prevents re-validating the same NIP-05 address repeatedly
  const nip05ValidationCache = new Map<string, boolean>();
  
  // Get current logged-in user's pubkey
  let currentUserPubkey = $state<string | null>(sessionManager.getCurrentPubkey());
  
  // Drawer state for viewing threads
  let drawerOpen = $state(false);
  let drawerEvent = $state<NostrEvent | null>(null);
  
  function openDrawer(event: NostrEvent) {
    drawerEvent = event;
    drawerOpen = true;
  }
  
  function closeDrawer() {
    drawerOpen = false;
    drawerEvent = null;
  }
  
  // Subscribe to session changes
  $effect(() => {
    const unsubscribe = sessionManager.session.subscribe((session) => {
      currentUserPubkey = session?.pubkey || null;
      // Reload interactions if session changes and we're viewing another user's profile
      if (profile) {
        const pubkey = decodePubkey($page.params.pubkey);
        if (pubkey && currentUserPubkey && currentUserPubkey !== pubkey) {
          // Reload interactions tab data
          loadInteractionsWithMe(pubkey, currentUserPubkey);
        } else {
          interactionsWithMe = [];
        }
      }
    });
    return unsubscribe;
  });
  
  async function loadInteractionsWithMe(profilePubkey: string, currentUserPubkey: string) {
    if (!currentUserPubkey || currentUserPubkey === profilePubkey) {
      interactionsWithMe = [];
      return;
    }
    
    try {
      const interactionRelays = relayManager.getFeedResponseReadRelays();
      
      // Fetch current user's posts from cache first (fast)
      const currentUserPosts = await nostrClient.fetchEvents(
        [{ kinds: [KIND.SHORT_TEXT_NOTE], authors: [currentUserPubkey], limit: 50 }],
        interactionRelays,
        { useCache: true, cacheResults: true, timeout: 2000 } // Short timeout for cache
      );
      const currentUserPostIds = new Set(currentUserPosts.map(p => p.id));
      
      // Only fetch interactions if we have some posts to check against
      if (currentUserPostIds.size === 0) {
        interactionsWithMe = [];
        return;
      }
      
      // Fetch interactions with timeout to prevent blocking
      const interactionEvents = await Promise.race([
        nostrClient.fetchEvents(
          [
            { kinds: [KIND.SHORT_TEXT_NOTE], authors: [profilePubkey], '#e': Array.from(currentUserPostIds).slice(0, 20), limit: 20 }, // Limit IDs to avoid huge queries
            { kinds: [KIND.SHORT_TEXT_NOTE], authors: [profilePubkey], '#p': [currentUserPubkey], limit: 20 }
          ],
          interactionRelays,
          { useCache: true, cacheResults: true, timeout: 5000 }
        ),
        new Promise<NostrEvent[]>((resolve) => setTimeout(() => resolve([]), 5000)) // 5s timeout
      ]);
      
      // Deduplicate and filter to only include actual interactions
      const seenIds = new Set<string>();
      interactionsWithMe = interactionEvents
        .filter(e => {
          if (seenIds.has(e.id)) return false;
          seenIds.add(e.id);
          
          // Check if it's a reply to current user's post
          const eTag = e.tags.find(t => t[0] === 'e');
          const isReplyToCurrentUser = eTag && currentUserPostIds.has(eTag[1]);
          
          // Check if it mentions current user
          const pTag = e.tags.find(t => t[0] === 'p' && t[1] === currentUserPubkey);
          const mentionsCurrentUser = !!pTag;
          
          return isReplyToCurrentUser || mentionsCurrentUser;
        })
        .sort((a, b) => b.created_at - a.created_at);
    } catch (error) {
      console.debug('Error loading interactions with me:', error);
      interactionsWithMe = [];
    }
  }

  onMount(async () => {
    await nostrClient.initialize();
    // Load profile after initialization
    if ($page.params.pubkey) {
      loadProfile();
    } else {
      // No pubkey provided - show error state
      loading = false;
      profile = null;
    }
  });

  // React to route param changes
  let lastParam = $state<string | null>(null);
  $effect(() => {
    const param = $page.params.pubkey;
    // Only reload if parameter actually changed
    if (param && param !== lastParam) {
      lastParam = param;
      loadProfile();
    }
  });

  /**
   * Decode route parameter to hex pubkey
   * Supports: hex pubkey, npub, or nprofile
   */
  function decodePubkey(param: string): string | null {
    if (!param) return null;
    
    // Check if it's already a hex pubkey (64 hex characters)
    if (/^[0-9a-f]{64}$/i.test(param)) {
      return param.toLowerCase();
    }
    
    // Check if it's a bech32 encoded format (npub or nprofile)
    if (/^(npub|nprofile)1[a-z0-9]+$/i.test(param)) {
      try {
        const decoded = nip19.decode(param);
        if (decoded.type === 'npub') {
          return String(decoded.data);
        } else if (decoded.type === 'nprofile') {
          // nprofile contains pubkey and optional relays
          if (decoded.data && typeof decoded.data === 'object' && 'pubkey' in decoded.data) {
            return String(decoded.data.pubkey);
          }
        }
      } catch (error) {
        console.error('Error decoding bech32:', error);
        return null;
      }
    }
    
    return null;
  }

  /**
   * Get well-known.json URL for a NIP-05 address
   * Uses URL constructor like jumble for proper URL building
   */
  function getNIP05WellKnownUrl(nip05: string): string | null {
    const [localPart, domain] = nip05.split('@');
    if (!localPart || !domain) {
      return null;
    }
    const url = new URL('/.well-known/nostr.json', `https://${domain}`);
    url.searchParams.set('name', localPart);
    return url.toString();
  }

  /**
   * Validate NIP-05 address against well-known.json
   * Uses caching like jumble to avoid repeated lookups
   */
  async function validateNIP05(nip05: string, expectedPubkey: string) {
    console.log(`[NIP-05] Starting validation for ${nip05} with pubkey ${expectedPubkey.substring(0, 8)}...`);
    
    // Check cache first (like jumble does)
    const cacheKey = `${nip05}:${expectedPubkey}`;
    if (nip05ValidationCache.has(cacheKey)) {
      const cachedResult = nip05ValidationCache.get(cacheKey)!;
      console.log(`[NIP-05] Cache hit for ${nip05}: ${cachedResult}`);
      nip05Validations[nip05] = cachedResult;
      return;
    }
    
    // Mark as checking
    console.log(`[NIP-05] Cache miss, fetching for ${nip05}`);
    nip05Validations[nip05] = null;
    
    try {
      // Parse NIP-05: format is "local@domain.com"
      const [localPart, domain] = nip05.split('@');
      if (!localPart || !domain) {
        console.log(`[NIP-05] Invalid format for ${nip05}`);
        nip05Validations[nip05] = false;
        nip05ValidationCache.set(cacheKey, false);
        return;
      }

      // Build URL using URL constructor (like jumble)
      const url = getNIP05WellKnownUrl(nip05);
      if (!url) {
        console.log(`[NIP-05] Failed to build URL for ${nip05}`);
        nip05Validations[nip05] = false;
        nip05ValidationCache.set(cacheKey, false);
        return;
      }
      
      console.log(`[NIP-05] Fetching ${url}`);
      
      // Add timeout to prevent hanging
      const controller = new AbortController();
      const timeoutId = setTimeout(() => {
        console.log(`[NIP-05] Timeout reached for ${nip05}`);
        controller.abort();
      }, 5000); // 5 second timeout
      
      try {
        const response = await fetch(url, {
          method: 'GET',
          headers: {
            'Accept': 'application/json'
          },
          signal: controller.signal
        });

        clearTimeout(timeoutId);
        console.log(`[NIP-05] Response status for ${nip05}: ${response.status}`);

        if (!response.ok) {
          console.log(`[NIP-05] Response not OK for ${nip05}: ${response.status}`);
          nip05Validations[nip05] = false;
          nip05ValidationCache.set(cacheKey, false);
          return;
        }

        let data;
        try {
          data = await response.json();
          console.log(`[NIP-05] Parsed JSON for ${nip05}:`, data);
        } catch (jsonError) {
          console.error(`[NIP-05] Failed to parse JSON for ${nip05}:`, jsonError);
          nip05Validations[nip05] = false;
          nip05ValidationCache.set(cacheKey, false);
          return;
        }
        
        // Check if the response contains the expected pubkey
        // NIP-05 format: { "names": { "local": "hex-pubkey" } }
        const verifiedPubkey = data.names?.[localPart];
        const isValid = verifiedPubkey && typeof verifiedPubkey === 'string' 
          ? verifiedPubkey.toLowerCase() === expectedPubkey.toLowerCase()
          : false;
        
        console.log(`[NIP-05] Validation result for ${nip05}: ${isValid} (verified: ${verifiedPubkey}, expected: ${expectedPubkey.substring(0, 8)}...)`);
        nip05Validations[nip05] = isValid;
        nip05ValidationCache.set(cacheKey, isValid);
      } catch (fetchError) {
        clearTimeout(timeoutId);
        // Check if it was aborted due to timeout
        if (fetchError instanceof Error && fetchError.name === 'AbortError') {
          console.log(`[NIP-05] Timeout/abort for ${nip05}`);
          nip05Validations[nip05] = false;
          nip05ValidationCache.set(cacheKey, false);
        } else {
          console.error(`[NIP-05] Fetch error for ${nip05}:`, fetchError);
          throw fetchError; // Re-throw other errors
        }
      }
    } catch (error) {
      console.error(`[NIP-05] Error validating ${nip05}:`, error);
      nip05Validations[nip05] = false;
      nip05ValidationCache.set(cacheKey, false);
    }
    
    console.log(`[NIP-05] Validation complete for ${nip05}`);
  }

  async function loadProfile() {
    const param = $page.params.pubkey;
    if (!param) {
      console.warn('No pubkey parameter provided to ProfilePage');
      loading = false;
      profile = null;
      return;
    }

    // Decode the parameter to hex pubkey
    const pubkey = decodePubkey(param);
    if (!pubkey) {
      console.warn('Invalid pubkey format:', param);
      loading = false;
      profile = null;
      return;
    }

    loading = true;
    try {
      // Step 1: Load profile and status first (fast from cache) - display immediately
      const [profileData, status] = await Promise.all([
        fetchProfile(pubkey),
        fetchUserStatus(pubkey)
      ]);
      
      profile = profileData;
      userStatus = status;
      loading = false; // Show profile immediately, even if posts are still loading
      
      // Validate NIP-05 addresses in background (non-blocking)
      if (profileData?.nip05 && profileData.nip05.length > 0) {
        for (const nip05 of profileData.nip05) {
          validateNIP05(nip05, pubkey).catch(err => {
            console.error('[NIP-05] Unhandled validation error:', nip05, err);
            // Ensure state is set even on unhandled errors
            nip05Validations[nip05] = false;
            const cacheKey = `${nip05}:${pubkey}`;
            nip05ValidationCache.set(cacheKey, false);
          });
        }
      }

      // Step 2: Load posts and responses in parallel (non-blocking, update when ready)
      const profileRelays = relayManager.getProfileReadRelays();
      const responseRelays = relayManager.getFeedResponseReadRelays();
      
      // Load posts first (needed for response filtering)
      const feedEvents = await nostrClient.fetchEvents(
        [{ kinds: [KIND.SHORT_TEXT_NOTE], authors: [pubkey], limit: 20 }],
        profileRelays,
        { useCache: true, cacheResults: true, timeout: 5000 }
      );
      posts = feedEvents.sort((a, b) => b.created_at - a.created_at);

      // Load responses in parallel with posts (but filter after posts are loaded)
      const userPostIds = new Set(posts.map(p => p.id));
      const responseEvents = await nostrClient.fetchEvents(
        [{ kinds: [KIND.SHORT_TEXT_NOTE], '#p': [pubkey], limit: 50 }], // Fetch more to account for filtering
        responseRelays,
        { useCache: true, cacheResults: true, timeout: 5000 }
      );
      
      // Filter responses (exclude self-replies, only include replies to user's posts)
      responses = responseEvents
        .filter(e => {
          if (e.pubkey === pubkey) return false; // Exclude self-replies
          const eTag = e.tags.find(t => t[0] === 'e');
          return eTag && userPostIds.has(eTag[1]);
        })
        .sort((a, b) => b.created_at - a.created_at)
        .slice(0, 20); // Limit to 20 after filtering

      // Step 3: Load interactions in background (non-blocking)
      if (currentUserPubkey && currentUserPubkey !== pubkey) {
        loadInteractionsWithMe(pubkey, currentUserPubkey).catch(err => {
          console.debug('Error loading interactions:', err);
        });
      } else {
        interactionsWithMe = [];
      }
    } catch (error) {
      console.error('Error loading profile:', error);
      loading = false;
      profile = null;
    }
  }
</script>

<div class="profile-page">
  {#if loading}
    <p class="text-fog-text-light dark:text-fog-dark-text-light">Loading profile...</p>
  {:else if profile}
    <div class="profile-header mb-6">
      {#if profile.picture}
        <img
          src={profile.picture}
          alt={profile.name || 'Profile picture'}
          class="profile-picture w-24 h-24 rounded-full mb-4"
        />
      {/if}
      <h1 class="text-3xl font-bold mb-2">{profile.name || 'Anonymous'}</h1>
      {#if profile.about}
        <p class="text-fog-text dark:text-fog-dark-text mb-2">{profile.about}</p>
      {/if}
      {#if userStatus}
        <p class="text-sm text-fog-text-light dark:text-fog-dark-text-light italic mb-2">
          {userStatus}
        </p>
      {/if}
      {#if profile.website && profile.website.length > 0}
        <div class="websites mb-2">
          {#each profile.website as website}
            <a
              href={website}
              target="_blank"
              rel="noopener noreferrer"
              class="text-fog-accent dark:text-fog-dark-accent hover:underline mr-2"
            >
              {website}
            </a>
          {/each}
        </div>
      {/if}
      {#if profile.nip05 && profile.nip05.length > 0}
        <div class="nip05 mb-2">
          {#each profile.nip05 as nip05}
            {@const isValid = nip05Validations[nip05]}
            {@const wellKnownUrl = getNIP05WellKnownUrl(nip05)}
            <span class="text-sm text-fog-text-light dark:text-fog-dark-text-light mr-2">
              {nip05}
              {#if isValid === true}
                <span class="nip05-valid" title="NIP-05 verified">✓</span>
              {:else if isValid === false}
                {#if wellKnownUrl}
                  <button
                    onclick={() => window.open(wellKnownUrl, '_blank', 'noopener,noreferrer')}
                    class="nip05-invalid-button"
                    title="NIP-05 verification failed - Click to view well-known.json"
                    aria-label="Open well-known.json for verification"
                  >
                    ✗
                  </button>
                {:else}
                  <span class="nip05-invalid" title="NIP-05 verification failed">✗</span>
                {/if}
              {:else}
                <span class="nip05-checking" title="Verifying NIP-05...">⟳</span>
              {/if}
            </span>
          {/each}
        </div>
      {/if}
      <PaymentAddresses pubkey={decodePubkey($page.params.pubkey) || ''} />
    </div>

    <div class="profile-posts">
      <div class="tabs mb-4 flex gap-4 border-b border-fog-border dark:border-fog-dark-border">
        <button
          onclick={() => activeTab = 'posts'}
          class="px-4 py-2 font-semibold {activeTab === 'posts' ? 'border-b-2 border-fog-accent dark:border-fog-dark-accent' : ''}"
        >
          Posts ({posts.length})
        </button>
        <button
          onclick={() => activeTab = 'responses'}
          class="px-4 py-2 font-semibold {activeTab === 'responses' ? 'border-b-2 border-fog-accent dark:border-fog-dark-accent' : ''}"
        >
          Responses ({responses.length})
        </button>
        {#if currentUserPubkey && currentUserPubkey !== decodePubkey($page.params.pubkey)}
          <button
            onclick={() => activeTab = 'interactions'}
            class="px-4 py-2 font-semibold {activeTab === 'interactions' ? 'border-b-2 border-fog-accent dark:border-fog-dark-accent' : ''}"
          >
            Interactions with me ({interactionsWithMe.length})
          </button>
        {/if}
      </div>

      {#if activeTab === 'posts'}
        {#if posts.length === 0}
          <p class="text-fog-text-light dark:text-fog-dark-text-light">No posts yet.</p>
        {:else}
          <div class="posts-list">
            {#each posts as post (post.id)}
              <FeedPost post={post} onOpenEvent={openDrawer} />
            {/each}
          </div>
        {/if}
      {:else if activeTab === 'responses'}
        {#if responses.length === 0}
          <p class="text-fog-text-light dark:text-fog-dark-text-light">No responses yet.</p>
        {:else}
          <div class="responses-list">
            {#each responses as response (response.id)}
              <FeedPost post={response} onOpenEvent={openDrawer} />
            {/each}
          </div>
        {/if}
      {:else if activeTab === 'interactions'}
        {#if interactionsWithMe.length === 0}
          <p class="text-fog-text-light dark:text-fog-dark-text-light">No interactions with you yet.</p>
        {:else}
          <div class="interactions-list">
            {#each interactionsWithMe as interaction (interaction.id)}
              <FeedPost post={interaction} onOpenEvent={openDrawer} />
            {/each}
          </div>
        {/if}
      {/if}
    </div>
  {:else}
    <p class="text-fog-text-light dark:text-fog-dark-text-light">Profile not found</p>
  {/if}
  
  <ThreadDrawer opEvent={drawerEvent} isOpen={drawerOpen} onClose={closeDrawer} />
</div>

<style>
  .profile-page {
    max-width: var(--content-width);
    margin: 0 auto;
    padding: 1rem;
  }

  .profile-picture {
    object-fit: cover;
  }
  
  .profile-header {
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .profile-header h1 {
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .profile-header p {
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .profile-header .websites {
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .profile-header .websites a {
    display: inline-block;
    max-width: 100%;
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .profile-header .nip05 {
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .profile-header .nip05 span {
    display: inline-block;
    max-width: 100%;
    overflow-wrap: break-word;
    word-break: break-word;
  }
  
  .nip05-valid {
    color: #60a5fa;
    margin-left: 0.25rem;
    font-weight: bold;
  }
  
  .nip05-invalid {
    color: #ef4444;
    margin-left: 0.25rem;
    font-weight: bold;
  }
  
  .nip05-invalid-button {
    color: #ef4444;
    margin-left: 0.25rem;
    font-weight: bold;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    font-size: inherit;
    line-height: inherit;
    transition: opacity 0.2s;
  }
  
  .nip05-invalid-button:hover {
    opacity: 0.7;
    text-decoration: underline;
  }
  
  .nip05-invalid-button:active {
    opacity: 0.5;
  }
  
  .nip05-checking {
    color: #9ca3af;
    margin-left: 0.25rem;
    display: inline-block;
    animation: spin 1s linear infinite;
  }
  
  @keyframes spin {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(360deg);
    }
  }
</style>
