import { removeHtmlTags } from '@/utils/creatorHelpers';

import type { CreationListItem, CreationPageItem } from '@/types/Creation';
import type { CreatorData, CreatorIdentity } from '@/types/Creator';

export const FAVORITES_STORAGE_KEY = 'azopus:favorites:v1';
export const FAVORITES_CHANGED_EVENT = 'azopus:favorites-changed';

export type FavoriteKind = 'creation' | 'creator';

export type FavoriteCreationItem = {
  kind: 'creation';
  id: string;
  savedAt: string;
  item: CreationListItem;
};

export type FavoriteCreatorItem = {
  kind: 'creator';
  id: string;
  savedAt: string;
  item: CreatorIdentity;
};

export type FavoriteItem = FavoriteCreationItem | FavoriteCreatorItem;

type FavoriteStore = {
  version: 1;
  items: FavoriteItem[];
};

export const getFavoriteKey = (kind: FavoriteKind, id: string) => `${kind}:${id}`;

const isFavoriteKind = (value: unknown): value is FavoriteKind =>
  value === 'creation' || value === 'creator';

const isRecord = (value: unknown): value is Record<string, unknown> =>
  Boolean(value) && typeof value === 'object';

const isStringOrNull = (value: unknown): value is string | null =>
  typeof value === 'string' || value === null;

const isNumberOrNull = (value: unknown): value is number | null =>
  typeof value === 'number' || value === null;

const isCreationListItemCreator = (value: unknown) => {
  if (!isRecord(value)) return false;

  return (
    typeof value.alkotoAzonosito === 'string' &&
    typeof value.megjelenitendoNev === 'string' &&
    isStringOrNull(value.szakma)
  );
};

const isCreationVideoField = (value: unknown) => {
  if (!isRecord(value)) return false;

  return isStringOrNull(value.tipus) && isStringOrNull(value.hivatkozas);
};

const isCreatorProfileImage = (value: unknown) => {
  if (!isRecord(value)) return false;

  return value.id === undefined || isNumberOrNull(value.id);
};

const isFavoriteCreationItem = (value: unknown): value is FavoriteCreationItem => {
  if (!isRecord(value)) return false;

  const item = value as Partial<FavoriteCreationItem>;
  const creation = item.item;
  if (!isRecord(creation)) return false;

  return (
    item.kind === 'creation' &&
    typeof item.id === 'string' &&
    typeof item.savedAt === 'string' &&
    typeof creation.alkotasAzonosito === 'string' &&
    creation.cardType === 'creation' &&
    isStringOrNull(creation.fokep) &&
    isNumberOrNull(creation.fokepId) &&
    isStringOrNull(creation.fokepNev) &&
    isStringOrNull(creation.hivatkozas) &&
    typeof creation.id === 'number' &&
    isStringOrNull(creation.koordinatak) &&
    isStringOrNull(creation.label) &&
    isNumberOrNull(creation.labelYear) &&
    Array.isArray(creation.alkoto) &&
    creation.alkoto.every(isCreationListItemCreator) &&
    isStringOrNull(creation.nev) &&
    isStringOrNull(creation.telepules) &&
    Array.isArray(creation.kepek) &&
    (creation.hivatkozasLista === null ||
      (Array.isArray(creation.hivatkozasLista) &&
        creation.hivatkozasLista.every(isCreationVideoField)))
  );
};

const isFavoriteCreatorItem = (value: unknown): value is FavoriteCreatorItem => {
  if (!isRecord(value)) return false;

  const item = value as Partial<FavoriteItem>;
  const creator = item.item;
  if (!isRecord(creator)) return false;

  return (
    item.kind === 'creator' &&
    typeof item.id === 'string' &&
    typeof item.savedAt === 'string' &&
    typeof creator.alkotoAzonosito === 'string' &&
    (creator.nev === undefined || isStringOrNull(creator.nev)) &&
    (creator.szakma === undefined || isStringOrNull(creator.szakma)) &&
    (creator.elhalalozasIdeje === undefined || isStringOrNull(creator.elhalalozasIdeje)) &&
    (creator.profilkep === undefined ||
      creator.profilkep === null ||
      isCreatorProfileImage(creator.profilkep))
  );
};

const isFavoriteItem = (value: unknown): value is FavoriteItem => {
  if (!isRecord(value) || !isFavoriteKind(value.kind)) return false;

  return value.kind === 'creation' ? isFavoriteCreationItem(value) : isFavoriteCreatorItem(value);
};

const getLegacyCreatorProfileImage = (
  creator: Record<string, unknown>
): CreatorIdentity['profilkep'] => {
  const legacyImage = creator.imageId;
  if (typeof legacyImage === 'number') return { id: legacyImage };
  if (!isRecord(legacyImage)) return null;

  const id = typeof legacyImage.id === 'number' ? legacyImage.id : null;
  return id ? { id } : null;
};

const normalizeFavoriteCreatorItem = (item: FavoriteCreatorItem): FavoriteCreatorItem => {
  const creator = item.item as CreatorIdentity & Record<string, unknown>;

  return {
    ...item,
    item: {
      alkotoAzonosito: creator.alkotoAzonosito,
      nev: creator.nev ?? null,
      szakma: creator.szakma ?? null,
      elhalalozasIdeje: creator.elhalalozasIdeje ?? null,
      profilkep: creator.profilkep ?? getLegacyCreatorProfileImage(creator),
    },
  };
};

const normalizeItems = (items: unknown): FavoriteItem[] => {
  if (!Array.isArray(items)) return [];

  const seen = new Set<string>();
  return items.reduce<FavoriteItem[]>((normalizedItems, item) => {
    if (!isFavoriteItem(item)) return normalizedItems;

    const key = getFavoriteKey(item.kind, item.id);
    if (seen.has(key)) return normalizedItems;

    seen.add(key);
    normalizedItems.push(item.kind === 'creator' ? normalizeFavoriteCreatorItem(item) : item);
    return normalizedItems;
  }, []);
};

const getFavoriteStore = (items: FavoriteItem[]): FavoriteStore => ({
  version: 1,
  items,
});

export const readFavoriteItems = (): FavoriteItem[] => {
  if (typeof window === 'undefined') return [];

  try {
    const raw = window.localStorage.getItem(FAVORITES_STORAGE_KEY);
    if (!raw) return [];

    const parsed = JSON.parse(raw) as Partial<FavoriteStore> | FavoriteItem[];
    return Array.isArray(parsed) ? normalizeItems(parsed) : normalizeItems(parsed.items);
  } catch {
    return [];
  }
};

export const writeFavoriteItems = (items: FavoriteItem[]): boolean => {
  if (typeof window === 'undefined') return false;

  try {
    const normalizedItems = normalizeItems(items);
    window.localStorage.setItem(
      FAVORITES_STORAGE_KEY,
      JSON.stringify(getFavoriteStore(normalizedItems))
    );
    window.dispatchEvent(new CustomEvent(FAVORITES_CHANGED_EVENT, { detail: normalizedItems }));
    return true;
  } catch {
    return false;
  }
};

export const createFavoriteCreationItem = (creation: CreationPageItem): FavoriteCreationItem => ({
  kind: 'creation',
  id: creation.alkotasAzonosito,
  savedAt: new Date().toISOString(),
  item: {
    alkotasAzonosito: creation.alkotasAzonosito,
    cardType: 'creation',
    fokep: creation.fokep?.key ?? null,
    fokepId: creation.fokep?.id ?? null,
    fokepNev: creation.fokep?.fileName ?? null,
    hivatkozas: null,
    id: creation.fokep?.id ?? 0,
    koordinatak: creation.keletkezesHelyeKoordinatak,
    label: creation.label,
    labelYear: null,
    alkoto: creation.alkoto,
    nev: creation.nev,
    telepules: creation.keletkezesHelyeCity,
    kepek: [],
    hivatkozasLista: null,
  },
});

export const createFavoriteCreatorItem = (creator: CreatorData): FavoriteCreatorItem => ({
  kind: 'creator',
  id: creator.alkotoAzonosito,
  savedAt: new Date().toISOString(),
  item: {
    alkotoAzonosito: creator.alkotoAzonosito,
    nev: creator.nev,
    szakma: creator.szakmaOndef ? removeHtmlTags(creator.szakmaOndef) : creator.szakma,
    elhalalozasIdeje: creator.elhalalozasIdeje,
    profilkep: creator.profilkep ? { id: creator.profilkep.id } : null,
  },
});

export const animateFavoriteToHeader = (source: HTMLElement | null) => {
  if (typeof window === 'undefined' || !source) return;

  const target = Array.from(document.querySelectorAll<HTMLElement>('[data-favorites-target]')).find(
    (element) => element.offsetParent !== null
  );
  if (!target) return;

  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const sourceRect = source.getBoundingClientRect();
  const targetRect = target.getBoundingClientRect();

  const fromX = sourceRect.left + sourceRect.width / 2;
  const fromY = sourceRect.top + sourceRect.height / 2;
  const toX = targetRect.left + targetRect.width / 2;
  const toY = targetRect.top + targetRect.height / 2;

  target.animate(
    [{ transform: 'scale(1)' }, { transform: 'scale(1.16)' }, { transform: 'scale(1)' }],
    {
      duration: prefersReducedMotion ? 160 : 420,
      easing: 'ease-out',
    }
  );

  if (prefersReducedMotion) return;

  const marker = document.createElement('div');
  marker.textContent = '\u2665';
  marker.setAttribute('aria-hidden', 'true');
  marker.className =
    'pointer-events-none fixed z-[9999] flex h-7 w-7 items-center justify-center rounded-full bg-red-600 text-sm font-bold leading-none text-white shadow-lg';
  marker.style.left = `${fromX - 14}px`;
  marker.style.top = `${fromY - 14}px`;
  document.body.appendChild(marker);

  const animation = marker.animate(
    [
      { transform: 'translate3d(0, 0, 0) scale(0.72)', opacity: 0 },
      { transform: 'translate3d(0, -18px, 0) scale(1)', opacity: 1, offset: 0.18 },
      {
        transform: `translate3d(${toX - fromX}px, ${toY - fromY}px, 0) scale(0.42)`,
        opacity: 0.15,
      },
    ],
    {
      duration: 720,
      easing: 'cubic-bezier(0.2, 0.8, 0.2, 1)',
    }
  );

  animation.addEventListener('finish', () => marker.remove());
  animation.addEventListener('cancel', () => marker.remove());
};
