import { env } from '../../env.js';

export type FeedItem = {
  id: string;
  title: string;
  url: string;
};

export type FetchResult =
  | { ok: true; items: FeedItem[] }
  | { ok: false; skipped?: boolean; message: string };

const USER_AGENT = 'NexumiBot/1.0';
const YOUTUBE_CHANNEL_ID_RE = /^UC[\w-]{10,}$/i;

function decodeXmlEntities(value: string): string {
  return value
    .replaceAll('&amp;', '&')
    .replaceAll('&lt;', '<')
    .replaceAll('&gt;', '>')
    .replaceAll('&quot;', '"')
    .replaceAll('&apos;', "'")
    .replaceAll('&#39;', "'");
}

function stripCdata(value: string): string {
  return value.replace(/^<!\[CDATA\[([\s\S]*?)\]\]>$/i, '$1').trim();
}

function extractTag(block: string, tagNames: string[]): string | null {
  for (const tag of tagNames) {
    const match = block.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'i'));
    if (match?.[1]) {
      return decodeXmlEntities(stripCdata(match[1].trim()));
    }
  }
  return null;
}

function extractLink(block: string): string | null {
  const atomLink = block.match(/<link[^>]*href=["']([^"']+)["'][^>]*\/?>/i);
  if (atomLink?.[1]) {
    return atomLink[1];
  }

  const rssLink = extractTag(block, ['link']);
  return rssLink;
}

function extractGuid(block: string, link: string | null): string | null {
  const guid = extractTag(block, ['guid', 'id']);
  if (guid) {
    return guid;
  }
  return link;
}

export function parseRssXml(xml: string): FeedItem[] {
  const items: FeedItem[] = [];
  const blockRegex = /<(?:item|entry)\b[\s\S]*?<\/(?:item|entry)>/gi;
  const blocks = xml.match(blockRegex) ?? [];

  for (const block of blocks) {
    const title = extractTag(block, ['title']);
    const link = extractLink(block);
    const id = extractGuid(block, link);
    if (!title || !link || !id) {
      continue;
    }
    items.push({ id, title, url: link });
  }

  return items;
}

async function fetchText(url: string): Promise<string> {
  const response = await fetch(url, {
    headers: { 'User-Agent': USER_AGENT }
  });
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.text();
}

export async function fetchRssFeed(sourceUrl: string): Promise<FetchResult> {
  try {
    const xml = await fetchText(sourceUrl);
    const items = parseRssXml(xml);
    return { ok: true, items };
  } catch (error) {
    return {
      ok: false,
      message: error instanceof Error ? error.message : 'fetch_failed'
    };
  }
}

export async function fetchYouTubeFeed(source: string): Promise<FetchResult> {
  const trimmed = source.trim();
  const channelIdMatch = trimmed.match(/channel_id=([\w-]+)/i);
  const channelId = YOUTUBE_CHANNEL_ID_RE.test(trimmed)
    ? trimmed
    : (channelIdMatch?.[1] ?? null);

  if (channelId) {
    return fetchRssFeed(`https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`);
  }

  if (/^https?:\/\//i.test(trimmed)) {
    return fetchRssFeed(trimmed);
  }

  return { ok: false, message: 'invalid_youtube_source' };
}

export async function fetchRedditFeed(subreddit: string): Promise<FetchResult> {
  const name = subreddit.replace(/^r\//i, '').trim();
  if (!name) {
    return { ok: false, message: 'invalid_subreddit' };
  }

  try {
    const url = `https://www.reddit.com/r/${encodeURIComponent(name)}/new.json?limit=5`;
    const response = await fetch(url, {
      headers: { 'User-Agent': USER_AGENT }
    });
    if (!response.ok) {
      return { ok: false, message: `HTTP ${response.status}` };
    }

    const data = (await response.json()) as {
      data?: { children?: Array<{ data?: { id?: string; title?: string; permalink?: string } }> };
    };

    const items: FeedItem[] = [];
    for (const child of data.data?.children ?? []) {
      const post = child.data;
      if (!post?.id || !post.title || !post.permalink) {
        continue;
      }
      items.push({
        id: post.id,
        title: post.title,
        url: post.permalink.startsWith('http')
          ? post.permalink
          : `https://www.reddit.com${post.permalink}`
      });
    }

    return { ok: true, items };
  } catch (error) {
    return {
      ok: false,
      message: error instanceof Error ? error.message : 'fetch_failed'
    };
  }
}

let twitchAppToken: { token: string; expiresAt: number } | null = null;

async function getTwitchAppToken(): Promise<string | null> {
  const clientId = env.TWITCH_CLIENT_ID;
  const clientSecret = env.TWITCH_CLIENT_SECRET;
  if (!clientId || !clientSecret) {
    return null;
  }

  if (twitchAppToken && twitchAppToken.expiresAt > Date.now() + 60_000) {
    return twitchAppToken.token;
  }

  const params = new URLSearchParams({
    client_id: clientId,
    client_secret: clientSecret,
    grant_type: 'client_credentials'
  });

  const response = await fetch(`https://id.twitch.tv/oauth2/token?${params.toString()}`, {
    method: 'POST'
  });
  if (!response.ok) {
    throw new Error(`twitch_token_http_${response.status}`);
  }

  const body = (await response.json()) as { access_token?: string; expires_in?: number };
  if (!body.access_token) {
    throw new Error('twitch_token_missing');
  }

  twitchAppToken = {
    token: body.access_token,
    expiresAt: Date.now() + (body.expires_in ?? 3600) * 1000
  };
  return twitchAppToken.token;
}

export async function fetchTwitchFeed(login: string): Promise<FetchResult> {
  const userLogin = login.trim().toLowerCase();
  if (!userLogin) {
    return { ok: false, message: 'invalid_twitch_login' };
  }

  const clientId = env.TWITCH_CLIENT_ID;
  const clientSecret = env.TWITCH_CLIENT_SECRET;
  if (!clientId || !clientSecret) {
    return {
      ok: false,
      skipped: true,
      message: 'twitch_credentials_missing'
    };
  }

  try {
    const token = await getTwitchAppToken();
    if (!token) {
      return {
        ok: false,
        skipped: true,
        message: 'twitch_credentials_missing'
      };
    }

    const url = `https://api.twitch.tv/helix/streams?user_login=${encodeURIComponent(userLogin)}`;
    const response = await fetch(url, {
      headers: {
        'Client-ID': clientId,
        Authorization: `Bearer ${token}`
      }
    });

    if (!response.ok) {
      return { ok: false, message: `HTTP ${response.status}` };
    }

    const body = (await response.json()) as {
      data?: Array<{ id?: string; title?: string; user_login?: string }>;
    };

    const stream = body.data?.[0];
    if (!stream?.id || !stream.title) {
      return { ok: true, items: [] };
    }

    const streamLogin = stream.user_login ?? userLogin;
    return {
      ok: true,
      items: [
        {
          id: stream.id,
          title: stream.title,
          url: `https://twitch.tv/${streamLogin}`
        }
      ]
    };
  } catch (error) {
    return {
      ok: false,
      message: error instanceof Error ? error.message : 'fetch_failed'
    };
  }
}

export async function fetchFeedItems(type: string, sourceId: string): Promise<FetchResult> {
  switch (type) {
    case 'RSS':
      return fetchRssFeed(sourceId);
    case 'YOUTUBE':
      return fetchYouTubeFeed(sourceId);
    case 'REDDIT':
      return fetchRedditFeed(sourceId);
    case 'TWITCH':
      return fetchTwitchFeed(sourceId);
    default:
      return { ok: false, message: 'unsupported_type' };
  }
}
