import {DimensionValue, FlexAlignType} from 'react-native';
import {TBlock, TPhrasing, TDocument} from 'react-native-render-html';
import {COLORS} from '@shared/colors';
import {AspectRatios, COMMON_REACTIONS} from '@shared/constants';
import {DEFAULT_FONT_FAMILY} from '@shared/styles';

import {
  CUSTOM_WEBVIEW_URIS,
  REGEX_BASE_URL,
  YOUTUBE_PATTERNS,
  MAX_SAFE_BODY_LENGTH,
} from './constants';

interface Params {
  content: Nullable<{
    title?: string;
    coverPicture?: string;
    body?: string;
  }>;
  aspectRatio?: number;
  timeAgo?: string | boolean;
  backgroundColor?: string;
  hasPadding?: boolean;
}

const getStyleTag = () => `
<style>
  p, span, h1, h2, h3, h4, h5, h6 {
    width: 100%; 
    word-wrap: break-word;
  }
  div {
    max-width: 100%;
  }
  article, section, aside, span, p {
    display: block;
    max-width: 100%;
  }
  img, iframe, audio, figure, video { 
    display: flex;
    height: auto; 
    margin-bottom: 30px; 
    margin-top: 30px; 
    max-width: 100%; 
  }
  .mobile-table {
    display: block;
    overflow-x: scroll;
  }
  ul {
    display: block;
    overflow-x: scroll;
  }
  video {
    background-color: ${COLORS.BLACK};
    height: 200px;
    width: 100%;
  }
  table {
    margin-left: 0 !important;
    margin-right: 0 !important;
  }
</style>`;

const getHeadTag = (isPopup?: boolean) => `
<html>
  <head>
    <meta name="viewport" content="width=device-width${
      isPopup ? ', initial-scale=1.0, maximum-scale=1.0' : ''
    }">
    <link rel="stylesheet" href="https://humand-public-assets.s3.sa-east-1.amazonaws.com/fonts/humand-fonts">
  </head>`;

const getBodytag = (isPopup?: boolean, backgroundColor?: string) => `
<body style="background-color: ${
  backgroundColor || COLORS.WHITE
}; max-width: 100%; ${isPopup ? '' : 'margin: 0 !important;'}">
`;

export const getArticleHtml = ({
  content,
  timeAgo,
  backgroundColor,
  aspectRatio = AspectRatios.DEFAULT,
  hasPadding = true,
}: Params) => {
  return `
  ${getHeadTag()}
  ${getBodytag(false, backgroundColor)}
  ${getStyleTag()}
  ${
    content?.coverPicture
      ? `<img src=${content.coverPicture} alt="" style="height: ${
          aspectRatio * 100
        }vw; margin-bottom: 0; margin-top: 0; object-fit: cover; width: 100vw;">`
      : ''
  }
  <div style="padding-left: ${hasPadding ? 20 : 0}px; padding-right: ${
    hasPadding ? 20 : 0
  }px;">
  ${
    content?.title
      ? `<h2 style="font-family: '${DEFAULT_FONT_FAMILY}'; font-weight: bold; margin-bottom: 5px; margin-top: 20px;">${content.title}</h2>`
      : ''
  }
  ${
    timeAgo
      ? `<span style="color: ${COLORS.DARKEST_GRAY}; font-size: 14px;">
            ${`${COMMON_REACTIONS.CLOCK} ${timeAgo}`}
        </span>
        <hr style="margin-bottom: 30px;" />`
      : ''
  }
    ${content?.body || ''}
  </div>
</body>
</html>`;
};

const truncateBodyToSafeLength = (body: string): string => {
  if (body.length <= MAX_SAFE_BODY_LENGTH) return body;
  const truncated = body.slice(0, MAX_SAFE_BODY_LENGTH);
  const lastTagEnd = truncated.lastIndexOf('>');
  if (lastTagEnd === -1) return truncated;
  return `${truncated.slice(0, lastTagEnd + 1)}<p>…</p>`;
};

export const getBodyNews = ({content, backgroundColor}: Params) => {
  const body = truncateBodyToSafeLength(content?.body || '');
  return `
  ${getHeadTag()}
  ${getBodytag(false, backgroundColor)}
  ${getStyleTag()}
  <div style="padding-left: 20px; padding-right: 20px;">
    ${body}
  </div>
</body>
</html>`;
};

export const getPopupHtml = ({content, backgroundColor}: Params) => {
  return `
  ${getHeadTag(true)}
  ${getBodytag(true, backgroundColor)}
  ${getStyleTag()}
  ${
    content?.title
      ? `<h1 style="font-family: '${DEFAULT_FONT_FAMILY}'; font-weight: bold; margin-bottom: 20px; margin-top: 15px">${content.title}</h1>`
      : ''
  }${
    content?.coverPicture
      ? `<img src=${content.coverPicture} alt="" style="height: auto; width: 100%;">`
      : ''
  }${content?.body || ''}</body></html>`;
};

export const needCustomWebView = (uri: string) => {
  const uriMatch = uri.match(REGEX_BASE_URL);

  return !!(
    uriMatch &&
    uriMatch[0] &&
    CUSTOM_WEBVIEW_URIS.some((url: string) => uriMatch[0].includes(url))
  );
};

export const getCustomHtml = (htmlAttribs: Record<string, string>): string => {
  const attribs = Object.keys(htmlAttribs)
    .filter(attr => !['style', 'width', 'height'].includes(attr))
    .map(attr => `${attr}="${htmlAttribs[attr]}"`)
    .join(' ');

  return `
  <html>
    <head>
      <meta name="viewport" content="width=device-width" />
      <style>
        body {
          margin: 0;
          padding: 0;
        }
        iframe {
          width: 100%;
          height: 100%;
        }
      </style>
    <body>
      <iframe ${attribs} />
    </body>
  </html>
  `;
};

export const isYoutubeUrl = (url: string): boolean => {
  if (!url) {
    return false;
  }

  return YOUTUBE_PATTERNS.some(pattern => pattern.test(url));
};

export const getYoutubeVideoId = (url: string): Nullable<string> => {
  if (!url) {
    return null;
  }

  const cleanUrl = url.trim().replace(/\/+$/, '');

  for (const pattern of YOUTUBE_PATTERNS) {
    const [_, videoId] = cleanUrl.match(pattern) || [];
    if (videoId) {
      return videoId;
    }
  }

  return null;
};

type ParentNode = Nullable<TBlock | TDocument | TPhrasing>;

const parseInlineStyles = (styleString?: string) => {
  if (!styleString) return {};

  const styles: Record<string, string> = {};

  styleString.split(';').forEach(style => {
    const [property, value] = style.split(':').map(s => s.trim());
    if (property && value) {
      styles[property] = value;
    }
  });

  return styles;
};

const parseStyleToDimension = (value?: string): DimensionValue => {
  const matchPercentage = value?.match(/^(\d+(?:\.\d+)?)%$/);
  if (matchPercentage) {
    return `${Number(matchPercentage[1])}%`;
  }

  return null;
};

const mapTextAlignToAlignSelf = (textAlign?: string): FlexAlignType => {
  switch (textAlign) {
    case 'right':
      return 'flex-end';
    case 'center':
      return 'center';
    case 'left':
    default:
      return 'flex-start';
  }
};

const getParentsStyles = (
  tnode?: ParentNode,
): Record<string, string> | undefined => {
  if (!tnode) return {};

  const parent = tnode.parent;
  const parentStyle = parseInlineStyles(parent?.attributes.style);

  return {...parentStyle, ...getParentsStyles(parent)};
};

interface ResizeAndAlignStyle {
  width?: DimensionValue;
  maxWidth?: DimensionValue;
  alignSelf?: FlexAlignType;
  height?: DimensionValue;
}

export const getResizedAndAlignStyle = (
  tnode?: ParentNode,
  contentWidth: number = 0,
  aspectRatio: number = 1,
): ResizeAndAlignStyle => {
  if (!tnode) return {};

  const parentsStyles = getParentsStyles(tnode);

  const textAlign = parentsStyles?.['text-align'];
  const alignSelf = mapTextAlignToAlignSelf(textAlign);

  const elementWidth = parseStyleToDimension(
    tnode.attributes.width || parentsStyles?.width,
  );

  // If the multimedia has no percentage width, we don't need to resize it
  if (!elementWidth) {
    return {};
  }

  const percentageWidth = elementWidth?.toString().includes('%')
    ? Number(elementWidth.toString().replace('%', '')) / 100
    : 1;

  const width =
    percentageWidth > 1
      ? contentWidth
      : Math.round(percentageWidth * contentWidth);
  const height = typeof width === 'number' ? width * aspectRatio : 'auto';

  return {
    width,
    maxWidth: contentWidth,
    alignSelf,
    height,
  };
};

/** Percentage padding-top values editors use for responsive video/embed boxes */
const ASPECT_RATIO_PADDING_TOP = String.raw`padding-top\s*:\s*(?:56\.25|75|62\.5)%`;

const normalizeResponsiveEmbedWrapperStyle = (styleContent: string): string => {
  if (!new RegExp(ASPECT_RATIO_PADDING_TOP).test(styleContent)) {
    return styleContent;
  }

  return styleContent
    .replace(/\bheight\s*:\s*0(?:px)?\s*;?\s*/gi, '')
    .replace(new RegExp(`${ASPECT_RATIO_PADDING_TOP}\\s*;?\\s*`, 'gi'), '')
    .replace(/;{2,}/g, ';')
    .replace(/^\s*;\s*|\s*;\s*$/g, '')
    .trim();
};

/**
 * CMS embeds (Canva, etc.) use a responsive box: `height:0` + `padding-top:
 * 56.25%` (16:9) with an absolutely positioned iframe. On web, padding defines
 * the box and the iframe sits in that area. In RN/Yoga, `height:0` can collapse
 * the wrapper (overlap), and if `height:0` is removed but percentage padding
 * remains, the padding reserves a tall empty band while the iframe lays out in
 * flow — embed appears at the bottom with a gap above. Stripping those layout
 * hacks lets `@native-html/iframe-plugin` size the iframe; wrapper height
 * follows the embed.
 */
export const normalizeAspectRatioEmbedStylesInHtml = (html: string): string => {
  if (!html.includes('padding-top')) {
    return html;
  }

  return html.replace(
    /style=(["'])((?:(?!\1).)*)\1/gi,
    (match, quote: string, styleContent: string) => {
      const next = normalizeResponsiveEmbedWrapperStyle(styleContent);
      return next === styleContent ? match : `style=${quote}${next}${quote}`;
    },
  );
};
