import type { InputAsset } from '../../types/node';
import { DefaultService } from '@lib/api';

export type MentionToken =
  | { type: 'text'; text: string }
  | { type: 'chip'; asset: InputAsset };

const signedUrlCache = new Map<string, string>();

async function getSignedUrl(url: string): Promise<string | null> {
  if (!url || !url.startsWith('http')) return null;
  if (signedUrlCache.has(url)) return signedUrlCache.get(url)!;
  try {
    const response = await DefaultService.signUrlEndpoint({ url });
    const next = (response as any)?.data?.url;
    if (next && typeof next === 'string' && next !== url) {
      signedUrlCache.set(url, next);
      return next;
    }
  } catch {
    // ignore signing failures and keep original source
  }
  return null;
}

export function parseMentionTokens(text: string, inputAssets?: InputAsset[]): MentionToken[] {
  if (!inputAssets || !text) {
    return text ? [{ type: 'text', text }] : [];
  }

  const candidates = inputAssets.filter((asset) => Boolean(asset.mentionAlias || asset.label));
  if (candidates.length === 0) {
    return [{ type: 'text', text }];
  }

  const result: MentionToken[] = [];
  let i = 0;
  let buffer = '';

  while (i < text.length) {
    let matched: { asset: InputAsset; length: number } | null = null;

    if (text[i] === '@') {
      for (const asset of candidates) {
        const alias = asset.mentionAlias || asset.label!;
        const mentionText = `@${alias}`;
        if (text.slice(i, i + mentionText.length) === mentionText) {
          const nextChar = text[i + mentionText.length];
          const isValidEnd = !nextChar || !/[a-zA-Z0-9]/.test(nextChar);
          if (isValidEnd) {
            matched = { asset, length: mentionText.length };
            break;
          }
        }
      }
    }

    if (matched) {
      if (buffer) {
        result.push({ type: 'text', text: buffer });
        buffer = '';
      }
      result.push({ type: 'chip', asset: matched.asset });
      i += matched.length;
    } else {
      buffer += text[i];
      i += 1;
    }
  }

  if (buffer) {
    result.push({ type: 'text', text: buffer });
  }

  return result;
}

export function createMentionChipElement(asset: InputAsset, isExpanded?: boolean): HTMLSpanElement {
  const alias = asset.mentionAlias || asset.label || asset.id;
  const chip = document.createElement('span');
  chip.contentEditable = 'false';
  chip.className = 'mention-chip';
  chip.setAttribute('data-asset-id', asset.id);
  chip.setAttribute('data-asset-type', asset.type);

  chip.style.cssText = `
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 6px;
    margin: 0 1px;
    border-radius: 9999px;
    background-color: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.15);
    vertical-align: middle;
    ${!isExpanded ? 'transform: scale(0.9);' : ''}
  `;

  const thumbnail = document.createElement('span');
  thumbnail.style.cssText = `
    width: 16px;
    height: 16px;
    border-radius: 9999px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(0, 0, 0, 0.4);
    flex-shrink: 0;
    position: relative;
  `;

  if (asset.type === 'image' || asset.type === 'video') {
    const isVideo = asset.type === 'video';
    const mediaElement = document.createElement(isVideo ? 'video' : 'img');
    const isLocal = asset.src.startsWith('/files/')
      || asset.src.startsWith('http://localhost')
      || asset.src.startsWith('https://localhost')
      || asset.src.startsWith('data:');

    if (isVideo) {
      (mediaElement as HTMLVideoElement).muted = true;
      (mediaElement as HTMLVideoElement).loop = true;
      (mediaElement as HTMLVideoElement).autoplay = true;
      (mediaElement as HTMLVideoElement).playsInline = true;
    }

    // For remote URLs, retry once with freshly signed URL on load failure.
    if (!isLocal) {
      let retried = false;
      mediaElement.addEventListener('error', async () => {
        if (retried) return;
        retried = true;
        const signed = await getSignedUrl(asset.src);
        if (signed) {
          if (isVideo) {
            const video = mediaElement as HTMLVideoElement;
            video.src = signed;
            video.load();
          } else {
            (mediaElement as HTMLImageElement).src = signed;
          }
        }
      });
    }

    if (isVideo) {
      (mediaElement as HTMLVideoElement).src = asset.src;
    } else {
      (mediaElement as HTMLImageElement).src = asset.src;
    }
    mediaElement.style.cssText = 'width: 100%; height: 100%; object-fit: cover; display: block;';
    thumbnail.appendChild(mediaElement);
  } else if (asset.type === 'audio') {
    const iconWrapper = document.createElement('span');
    iconWrapper.style.cssText = `
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      ${asset.gender ? 'background-color: rgba(0, 0, 0, 0.4);' : 'background-color: rgba(6, 182, 212, 0.2);'}
    `;
    iconWrapper.innerHTML = asset.gender
      ? `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="${asset.gender === '男' || asset.gender === 'male' ? 'text-blue-400' : 'text-pink-400'}"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>`
      : '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: rgb(103, 232, 249);"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>';
    thumbnail.appendChild(iconWrapper);
  }

  chip.appendChild(thumbnail);

  const label = document.createElement('span');
  label.style.cssText = `
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 80px;
    font-size: 11px;
    color: currentColor;
  `;
  label.textContent = alias;
  chip.appendChild(label);
  chip.setAttribute('data-mention-text', `@${alias}`);

  return chip;
}

export function filterMentionCandidates(inputAssets: InputAsset[] | undefined, mentionQuery: string): InputAsset[] {
  return (inputAssets || []).filter((asset) => {
    const alias = asset.mentionAlias || asset.label || asset.id;
    if (!alias) return false;
    if (!mentionQuery) return true;
    return alias.includes(mentionQuery);
  });
}
