/**
 * Nostr Profile Import
 *
 * Fetches and verifies kind:0 profile events from relays.
 * Used to import existing profiles before editing.
 */

import { SimplePool, verifyEvent, type Event } from "nostr-tools";

import { contentToProfile, type ProfileContent } from "./nostr-profile.js";
import type { NostrProfile } from "./config-schema.js";
import { validateUrlSafety } from "./nostr-profile-http.js";

// ============================================================================
// Types
// ============================================================================

export interface ProfileImportResult {
  /** Whether the import was successful */
  ok: boolean;
  /** The imported profile (if found and valid) */
  profile?: NostrProfile;
  /** The raw event (for advanced users) */
  event?: {
    id: string;
    pubkey: string;
    created_at: number;
  };
  /** Error message if import failed */
  error?: string;
  /** Which relays responded */
  relaysQueried: string[];
  /** Which relay provided the winning event */
  sourceRelay?: string;
}

export interface ProfileImportOptions {
  /** The public key to fetch profile for */
  pubkey: string;
  /** Relay URLs to query */
  relays: string[];
  /** Timeout per relay in milliseconds (default: 5000) */
  timeoutMs?: number;
}

// ============================================================================
// Constants
// ============================================================================

const DEFAULT_TIMEOUT_MS = 5000;

// ============================================================================
// Profile Import
// ============================================================================

/**
 * Sanitize URLs in an imported profile to prevent SSRF attacks.
 * Removes any URLs that don't pass SSRF validation.
 */
function sanitizeProfileUrls(profile: NostrProfile): NostrProfile {
  const result = { ...profile };
  const urlFields = ["picture", "banner", "website"] as const;

  for (const field of urlFields) {
    const value = result[field];
    if (value && typeof value === "string") {
      const validation = validateUrlSafety(value);
      if (!validation.ok) {
        // Remove unsafe URL
        delete result[field];
      }
    }
  }

  return result;
}

/**
 * Fetch the latest kind:0 profile event for a pubkey from relays.
 *
 * - Queries all relays in parallel
 * - Takes the event with the highest created_at
 * - Verifies the event signature
 * - Parses and returns the profile
 */
export async function importProfileFromRelays(
  opts: ProfileImportOptions
): Promise<ProfileImportResult> {
  const { pubkey, relays, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;

  if (!pubkey || !/^[0-9a-fA-F]{64}$/.test(pubkey)) {
    return {
      ok: false,
      error: "Invalid pubkey format (must be 64 hex characters)",
      relaysQueried: [],
    };
  }

  if (relays.length === 0) {
    return {
      ok: false,
      error: "No relays configured",
      relaysQueried: [],
    };
  }

  const pool = new SimplePool();
  const relaysQueried: string[] = [];

  try {
    // Query all relays for kind:0 events from this pubkey
    const events: Array<{ event: Event; relay: string }> = [];

    // Create timeout promise
    const timeoutPromise = new Promise<void>((resolve) => {
      setTimeout(resolve, timeoutMs);
    });

    // Create subscription promise
    const subscriptionPromise = new Promise<void>((resolve) => {
      let completed = 0;

      for (const relay of relays) {
        relaysQueried.push(relay);

        const sub = pool.subscribeMany(
          [relay],
          [
            {
              kinds: [0],
              authors: [pubkey],
              limit: 1,
            },
          ],
          {
            onevent(event) {
              events.push({ event, relay });
            },
            oneose() {
              completed++;
              if (completed >= relays.length) {
                resolve();
              }
            },
            onclose() {
              completed++;
              if (completed >= relays.length) {
                resolve();
              }
            },
          }
        );

        // Clean up subscription after timeout
        setTimeout(() => {
          sub.close();
        }, timeoutMs);
      }
    });

    // Wait for either all relays to respond or timeout
    await Promise.race([subscriptionPromise, timeoutPromise]);

    // No events found
    if (events.length === 0) {
      return {
        ok: false,
        error: "No profile found on any relay",
        relaysQueried,
      };
    }

    // Find the event with the highest created_at (newest wins for replaceable events)
    let bestEvent: { event: Event; relay: string } | null = null;
    for (const item of events) {
      if (!bestEvent || item.event.created_at > bestEvent.event.created_at) {
        bestEvent = item;
      }
    }

    if (!bestEvent) {
      return {
        ok: false,
        error: "No valid profile event found",
        relaysQueried,
      };
    }

    // Verify the event signature
    const isValid = verifyEvent(bestEvent.event);
    if (!isValid) {
      return {
        ok: false,
        error: "Profile event has invalid signature",
        relaysQueried,
        sourceRelay: bestEvent.relay,
      };
    }

    // Parse the profile content
    let content: ProfileContent;
    try {
      content = JSON.parse(bestEvent.event.content) as ProfileContent;
    } catch {
      return {
        ok: false,
        error: "Profile event has invalid JSON content",
        relaysQueried,
        sourceRelay: bestEvent.relay,
      };
    }

    // Convert to our profile format
    const profile = contentToProfile(content);

    // Sanitize URLs from imported profile to prevent SSRF when auto-merging
    const sanitizedProfile = sanitizeProfileUrls(profile);

    return {
      ok: true,
      profile: sanitizedProfile,
      event: {
        id: bestEvent.event.id,
        pubkey: bestEvent.event.pubkey,
        created_at: bestEvent.event.created_at,
      },
      relaysQueried,
      sourceRelay: bestEvent.relay,
    };
  } finally {
    pool.close(relays);
  }
}

/**
 * Merge imported profile with local profile.
 *
 * Strategy:
 * - For each field, prefer local if set, otherwise use imported
 * - This preserves user customizations while filling in missing data
 */
export function mergeProfiles(
  local: NostrProfile | undefined,
  imported: NostrProfile | undefined
): NostrProfile {
  if (!imported) return local ?? {};
  if (!local) return imported;

  return {
    name: local.name ?? imported.name,
    displayName: local.displayName ?? imported.displayName,
    about: local.about ?? imported.about,
    picture: local.picture ?? imported.picture,
    banner: local.banner ?? imported.banner,
    website: local.website ?? imported.website,
    nip05: local.nip05 ?? imported.nip05,
    lud16: local.lud16 ?? imported.lud16,
  };
}
