import {
  SIGN_LANGUAGE_CATEGORY_FILTERS,
  SIGN_LANGUAGE_LIFEPRINT_GIF_BASE_URL,
  SIGN_LANGUAGE_LIFEPRINT_LEGACY_IMAGE_SIGNS_PATH,
  SIGN_LANGUAGE_LIFEPRINT_URL,
  SIGN_LANGUAGE_YOUTUBE_EMBED_URL,
  SIGN_LANGUAGE_YOUTUBE_QUERY_PREFIX,
  SIGN_LANGUAGE_YOUTUBE_QUERY_SUFFIX,
  SIGN_LANGUAGE_YOUTUBE_SEARCH_URL,
} from '@/shared/constants/signLanguage';
import type { SignLanguageCategoryFilter } from '@/shared/constants/signLanguage';
import type { SignItem } from '@/shared/types/app';
import type { DashboardSignOfWeek } from '@/shared/types/dashboard';
import { toWeekStartIso } from '@/shared/business/week';
import type {
  SignLanguageCategoryOption,
  SignLanguageFilters,
} from '@/business/sign-language/types';

const LIFEPRINT_HOST = new URL(SIGN_LANGUAGE_LIFEPRINT_URL).hostname;
const YOUTUBE_HOST = 'youtube.com';
const YOUTUBE_SHORT_HOST = 'youtu.be';
const RAW_YOUTUBE_VIDEO_ID_PATTERN = /^[A-Za-z0-9_-]{6,}$/;
const RELATIVE_LIFEPRINT_GIF_PATH_PATTERN = /^[A-Za-z0-9-]+\/[A-Za-z0-9-]+(?:\.gif)?$/;

function trimPathSlashes(value: string): string {
  return value.replace(/^\/+|\/+$/g, '');
}

function isHostOrSubdomain(hostname: string, expectedHost: string): boolean {
  return hostname === expectedHost || hostname.endsWith(`.${expectedHost}`);
}

function extractYoutubeVideoId(videoUrl: string): string | null {
  const value = videoUrl.trim();

  if (!value) {
    return null;
  }

  if (RAW_YOUTUBE_VIDEO_ID_PATTERN.test(value) && !value.includes('/')) {
    return value;
  }

  try {
    const url = new URL(value);
    const hostname = url.hostname.replace(/^www\./, '');

    if (hostname === YOUTUBE_SHORT_HOST) {
      return url.pathname.split('/').filter(Boolean)[0] ?? null;
    }

    if (!isHostOrSubdomain(hostname, YOUTUBE_HOST)) {
      return null;
    }

    const [firstSegment, secondSegment] = url.pathname.split('/').filter(Boolean);

    if (firstSegment === 'embed' || firstSegment === 'shorts') {
      return secondSegment ?? null;
    }

    return url.searchParams.get('v');
  } catch {
    return null;
  }
}

function toLifeprintGifPath(path: string): string {
  const trimmedPath = trimPathSlashes(path)
    .replace(/^asl101\/gifs\//, '')
    .replace(/^gifs\//, '');

  if (!trimmedPath) {
    return '';
  }

  return trimmedPath.endsWith('.gif') ? trimmedPath : `${trimmedPath}.gif`;
}

export function buildSignLanguageCategories(
  signs: readonly SignItem[],
): readonly SignLanguageCategoryOption[] {
  return SIGN_LANGUAGE_CATEGORY_FILTERS.map((filter) => ({
    ...filter,
    count: filter.value === 'all'
      ? signs.length
      : signs.filter((sign) => sign.category === filter.value).length,
  }));
}

export function filterSignLanguageItems(
  signs: readonly SignItem[],
  filters: SignLanguageFilters,
): readonly SignItem[] {
  const normalizedSearchQuery = filters.searchQuery.trim().toLowerCase();

  return signs.filter((sign) => {
    const matchesSearch = normalizedSearchQuery.length === 0
      || sign.word.toLowerCase().includes(normalizedSearchQuery);
    const matchesCategory = filters.categoryFilter === 'all'
      || sign.category === filters.categoryFilter;

    return matchesSearch && matchesCategory;
  });
}

export function getSignLanguageProgressPercent(
  signs: readonly SignItem[],
  learnedSignIds: ReadonlySet<string>,
): number {
  if (signs.length === 0) {
    return 0;
  }

  return (learnedSignIds.size / signs.length) * 100;
}

export function getSignLanguageVideoDurationSeconds(sign: SignItem): number {
  const firstStepDuration = sign.videoSteps[0]?.duration ?? 3;

  return sign.videoSteps.length * firstStepDuration;
}

export function toSignLanguageCategoryFilter(value: string): SignLanguageCategoryFilter {
  const category = SIGN_LANGUAGE_CATEGORY_FILTERS.find((filter) => filter.value === value);

  return category?.value ?? 'all';
}

export function normalizeSignLanguageImageUrl(imageUrl: string): string {
  return imageUrl.trim();
}

export function buildSignLanguageLifeprintGifUrl(path: string): string {
  const gifPath = toLifeprintGifPath(path);

  return gifPath ? `${SIGN_LANGUAGE_LIFEPRINT_GIF_BASE_URL}/${gifPath}` : '';
}

export function normalizeSignLanguageGifUrl(gifUrl: string): string {
  const value = gifUrl.trim();

  if (!value) {
    return '';
  }

  try {
    const url = new URL(value);

    if (!isHostOrSubdomain(url.hostname, LIFEPRINT_HOST)) {
      return value;
    }

    if (url.pathname.startsWith(SIGN_LANGUAGE_LIFEPRINT_LEGACY_IMAGE_SIGNS_PATH)) {
      return '';
    }

    const normalizedPath = trimPathSlashes(url.pathname);

    if (
      normalizedPath.startsWith('asl101/gifs/')
      || normalizedPath.startsWith('gifs/')
      || RELATIVE_LIFEPRINT_GIF_PATH_PATTERN.test(normalizedPath)
    ) {
      return buildSignLanguageLifeprintGifUrl(normalizedPath);
    }

    return value;
  } catch {
    const normalizedPath = trimPathSlashes(value);

    if (
      normalizedPath.startsWith(trimPathSlashes(SIGN_LANGUAGE_LIFEPRINT_LEGACY_IMAGE_SIGNS_PATH))
      || normalizedPath.startsWith('images-signs/')
    ) {
      return '';
    }

    if (
      normalizedPath.startsWith('asl101/gifs/')
      || normalizedPath.startsWith('gifs/')
      || RELATIVE_LIFEPRINT_GIF_PATH_PATTERN.test(normalizedPath)
    ) {
      return buildSignLanguageLifeprintGifUrl(normalizedPath);
    }

    return value;
  }
}

export function normalizeSignLanguageYoutubeVideoUrl(videoUrl: string): string {
  const value = videoUrl.trim();
  const videoId = extractYoutubeVideoId(value);

  if (!videoId) {
    return value;
  }

  return `${SIGN_LANGUAGE_YOUTUBE_EMBED_URL}/${encodeURIComponent(videoId)}`;
}

export function normalizeSignLanguageYoutubeSearchUrl(searchUrl: string | undefined): string | undefined {
  const value = searchUrl?.trim() ?? '';

  return value || undefined;
}

export function normalizeSignLanguageItem(sign: SignItem): SignItem {
  return {
    ...sign,
    image: normalizeSignLanguageImageUrl(sign.image),
    gifUrl: normalizeSignLanguageGifUrl(sign.gifUrl),
    videoUrl: normalizeSignLanguageYoutubeVideoUrl(sign.videoUrl),
    youtubeSearchUrl: normalizeSignLanguageYoutubeSearchUrl(sign.youtubeSearchUrl),
  };
}

export function normalizeSignLanguageItems(signs: readonly SignItem[]): readonly SignItem[] {
  return signs.map(normalizeSignLanguageItem);
}

export function buildSignLanguageYoutubeSearchUrl(word: string): string {
  const searchQuery = `${SIGN_LANGUAGE_YOUTUBE_QUERY_PREFIX} ${word.trim()} ${SIGN_LANGUAGE_YOUTUBE_QUERY_SUFFIX}`;

  return `${SIGN_LANGUAGE_YOUTUBE_SEARCH_URL}?search_query=${encodeURIComponent(searchQuery)}`;
}

export function selectSignLanguageSignOfWeek(
  signs: readonly SignItem[],
  payload: DashboardSignOfWeek | null,
): SignItem | null {
  if (!payload) {
    return null;
  }

  if (payload.signId) {
    return signs.find((sign) => sign.id === payload.signId) ?? null;
  }

  return signs.find((sign) => sign.word === payload.word) ?? null;
}

export function buildDashboardSignOfWeekPayload(
  sign: SignItem,
  now: Date = new Date(),
): DashboardSignOfWeek {
  return {
    signId: sign.id,
    weekOf: toWeekStartIso(now),
    word: sign.word,
    description: sign.description,
    image: sign.image,
    alt: `${sign.word} sign`,
  };
}
