/**
 * Platform-specific social profile URL validation (TC_073).
 *
 * Ensures a social link belongs to its own platform (e.g. a Facebook URL cannot
 * be saved in the LinkedIn field), even via direct API requests. Mirrors the
 * frontend rules in src/frontend/src/utils/fieldValidators.ts — keep them in sync.
 */

/**
 * Allowed domains per social platform key. A URL is valid for a platform when
 * its hostname equals one of these hosts OR is a subdomain of one. Platforms
 * whose `hosts` is null accept any valid HTTP/HTTPS URL (e.g. a Website).
 */
/**
 * Maximum allowed length for any social profile URL (TC_080). Must match the
 * frontend constant in src/frontend/src/utils/fieldValidators.ts.
 */
export const SOCIAL_URL_MAX_LENGTH = 255;

export const SOCIAL_URL_RULES: Record<string, { label: string; hosts: string[] | null }> = {
  // Podcast/business platforms are pinned to the product's own host, not the whole
  // corporate domain — otherwise apple.com/iphone or google.com/search would pass
  // as an "Apple Podcast"/"Google Business" link (TC_073).
  applePodcast: { label: 'Apple Podcast', hosts: ['podcasts.apple.com'] },
  facebook: { label: 'Facebook', hosts: ['facebook.com', 'fb.com', 'fb.me'] },
  github: { label: 'GitHub', hosts: ['github.com'] },
  googleBusiness: { label: 'Google Business', hosts: ['business.google.com', 'g.page', 'goo.gl'] },
  instagram: { label: 'Instagram', hosts: ['instagram.com', 'instagr.am'] },
  linkedIn: { label: 'LinkedIn', hosts: ['linkedin.com', 'lnkd.in'] },
  medium: { label: 'Medium', hosts: ['medium.com'] },
  meetup: { label: 'Meetup', hosts: ['meetup.com'] },
  pinterest: { label: 'Pinterest', hosts: ['pinterest.com'] },
  quora: { label: 'Quora', hosts: ['quora.com'] },
  reddit: { label: 'Reddit', hosts: ['reddit.com'] },
  spotifyPodcast: { label: 'Spotify Podcast', hosts: ['open.spotify.com', 'podcasters.spotify.com'] },
  telegram: { label: 'Telegram', hosts: ['t.me', 'telegram.me', 'telegram.org'] },
  threads: { label: 'Threads', hosts: ['threads.net', 'threads.com'] },
  tikTok: { label: 'TikTok', hosts: ['tiktok.com'] },
  twitter: { label: 'X (Twitter)', hosts: ['x.com', 'twitter.com'] },
  website: { label: 'Website', hosts: null },
  whatsApp: { label: 'WhatsApp', hosts: ['whatsapp.com', 'wa.me'] },
  youTube: { label: 'YouTube', hosts: ['youtube.com', 'youtu.be'] },
};

/**
 * Normalize a social URL for storage/validation (TC_084): if the user omitted
 * the protocol (e.g. "linkedin.com/in/rahul"), prepend "https://". URLs that
 * already carry an explicit scheme (http/https or anything else like ftp:,
 * javascript:) are returned unchanged so validation can still reject bad schemes.
 */
export function normalizeSocialUrl(rawValue: unknown): string {
  const value = String(rawValue ?? '').trim();
  if (!value) return '';
  const hasScheme =
    /^[a-z][a-z0-9+.-]*:\/\//i.test(value) ||        // scheme://host  (http://, ftp://, …)
    /^(mailto|tel|javascript|data|file):/i.test(value); // schemes without "//"
  return hasScheme ? value : `https://${value}`;
}

/** Normalize every string value in a socialProfiles object (TC_084). */
export function normalizeSocialProfiles<T>(profiles: T): T {
  if (!profiles || typeof profiles !== 'object' || Array.isArray(profiles)) return profiles;
  const out: Record<string, unknown> = {};
  for (const [k, v] of Object.entries(profiles as Record<string, unknown>)) {
    out[k] = typeof v === 'string' ? normalizeSocialUrl(v) : v;
  }
  return out as T;
}

/** Parse a URL, requiring an http/https scheme. Returns the URL or null if invalid. */
function parseHttpUrl(raw: string): URL | null {
  const value = raw.trim();
  if (!/^https?:\/\//i.test(value)) return null; // must be an explicit http(s) URL
  try {
    const url = new URL(value);
    if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;
    if (!url.hostname.includes('.')) return null; // reject bare hosts like "http://localhost"
    return url;
  } catch {
    return null;
  }
}

/**
 * Validate a single social profile URL against its platform.
 * Returns an error message, or null when valid (empty is allowed — fields are optional).
 */
export function validateSocialUrl(platformKey: string, rawValue: unknown): string | null {
  const raw = String(rawValue ?? '').trim();
  if (!raw) return null; // optional field

  const rule = SOCIAL_URL_RULES[platformKey];
  const label = rule?.label ?? 'profile';

  // Accept URLs with or without a protocol — normalize to https:// (TC_084).
  const value = normalizeSocialUrl(raw);

  // Enforce a maximum length (TC_080) — never truncate silently; surface an error.
  if (value.length > SOCIAL_URL_MAX_LENGTH) {
    return `URL must not exceed ${SOCIAL_URL_MAX_LENGTH} characters.`;
  }

  // Malformed / unparseable / bad-scheme URL → generic message (TC_084).
  const url = parseHttpUrl(value);
  if (!url) return 'Please enter a valid URL.';

  // A second URL after the host — the link pasted twice, for example — parses as a
  // perfectly valid path, so the hostname check alone still accepts it. Matches only an
  // unencoded scheme, so a legitimate percent-encoded redirect parameter still passes.
  const remainder = `${url.pathname}${url.search}${url.hash}`;
  if (/https?:\/\//i.test(remainder)) return 'Please enter a valid URL.';

  // Platforms without a host list (e.g. Website) accept any valid HTTP/HTTPS URL.
  if (!rule || !rule.hosts) return null;

  const host = url.hostname.toLowerCase().replace(/^www\./, '');
  const matches = rule.hosts.some((h) => host === h || host.endsWith(`.${h}`));
  return matches ? null : `Enter a valid ${label} profile link.`;
}

/**
 * Validate a whole socialProfiles object. Returns the first error message found
 * (in the platform order of SOCIAL_URL_RULES), or null if all entries are valid.
 */
export function validateSocialProfiles(profiles: unknown): string | null {
  if (!profiles || typeof profiles !== 'object' || Array.isArray(profiles)) return null;
  const obj = profiles as Record<string, unknown>;
  for (const key of Object.keys(obj)) {
    const err = validateSocialUrl(key, obj[key]);
    if (err) return err;
  }
  return null;
}

// ============================================================
// GOOGLE DRIVE LINK VALIDATION (platform-specific) — TC_104
// ============================================================

/** Hosts accepted for a Google Drive link (Google-owned drive/docs domains). */
export const GOOGLE_DRIVE_HOSTS = ['drive.google.com', 'docs.google.com', 'drive.usercontent.google.com'];

export const GOOGLE_DRIVE_MESSAGE = 'Enter a valid Google Drive link.';

/**
 * Validate a Google Drive link (TC_104). Accepts only Google Drive/Docs URLs
 * (with or without protocol, normalized to https). Any other domain (Dropbox,
 * OneDrive, Box, Mega, …) or malformed URL is rejected. Empty is allowed
 * (the field is optional). Returns an error message or null.
 */
export function validateGoogleDriveUrl(rawValue: unknown): string | null {
  const raw = String(rawValue ?? '').trim();
  if (!raw) return null; // optional field

  const value = normalizeSocialUrl(raw); // accept protocol-less links (TC_084 behaviour)
  const url = parseHttpUrl(value);
  if (!url) return GOOGLE_DRIVE_MESSAGE;

  const host = url.hostname.toLowerCase().replace(/^www\./, '');
  const ok = GOOGLE_DRIVE_HOSTS.some((h) => host === h || host.endsWith(`.${h}`));
  return ok ? null : GOOGLE_DRIVE_MESSAGE;
}

/** Normalize a Google Drive link for storage (prepend https:// when missing). */
export function normalizeGoogleDriveUrl(rawValue: unknown): string {
  return normalizeSocialUrl(rawValue);
}
