import axios from 'axios';
import { parseStringPromise } from 'xml2js';
import { Feed, FeedItem, ParseResult, FeedSubscription } from '@/types/feed';

const parseOptions = {
  explicitArray: false,
  mergeAttrs: true,
  trim: true,
};

// Feed detection
function detectFeedType(data: string): 'rss' | 'atom' | 'unknown' {
  if (data.includes('<rss')) return 'rss';
  if (data.includes('<feed')) return 'atom';
  return 'unknown';
}

// RSS 2.0 Parser with iTunes podcast support
async function parseRSS(xml: any): Promise<Feed> {
  const rss = xml.rss;
  const channel = rss?.channel;
  
  if (!channel) {
    throw new Error('Invalid RSS: No channel found');
  }

  // Extract iTunes namespace fields
  const itunes = channel['itunes:navigation'] || {};
  const itunesImage = itunes.image || channel['itunes:image'];
  
  const items: FeedItem[] = (channel.item || []).map((item: any) => {
    const itunesItem = item['itunes:navigation'] || {};
    const enclosure = item.enclosure?.['$'] || item.enclosure;
    
    return {
      id: item.guid?.['$']?.value || item.link || crypto.randomUUID(),
      title: item.title,
      link: item.link,
      description: item.description || itunesItem.summary,
      content: item['content:encoded'] || item.description,
      author: item.author || itunesItem.author,
      published: item.pubDate ? new Date(item.pubDate) : undefined,
      categories: item.category?.map((c: any) => c) || [],
      enclosure: enclosure ? {
        url: enclosure.url,
        type: enclosure.type,
        length: enclosure.length ? parseInt(enclosure.length) : undefined,
      } : undefined,
      guid: item.guid?.['$']?.value,
    };
  });
  
  return {
    id: crypto.randomUUID(),
    title: channel.title || 'Untitled Feed',
    link: channel.link,
    description: channel.description,
    subtitle: itunes.summary,
    language: channel.language,
    lastBuildDate: channel.lastBuildDate ? new Date(channel.lastBuildDate) : undefined,
    generator: channel.generator,
    ttl: channel.ttl ? parseInt(channel.ttl) : undefined,
    items,
    rawUrl: '',
  };
}

// Atom 1.0 Parser
async function parseAtom(xml: any): Promise<Feed> {
  const feed = xml.feed;
  
  if (!feed) {
    throw new Error('Invalid Atom: No feed found');
  }
  
  const entries: FeedItem[] = (feed.entry || []).map((entry: any) => ({
    id: entry.id?.$?.value || entry.link?.href || crypto.randomUUID(),
    title: entry.title,
    link: Array.isArray(entry.link) 
      ? entry.link.find((l: any) => !l.rel)?.href 
      : entry.link?.href,
    summary: entry.summary,
    content: entry.content,
    author: Array.isArray(entry.author) 
      ? entry.author[0]?.name 
      : entry.author?.name,
    published: entry.published ? new Date(entry.published) : undefined,
    updated: entry.updated ? new Date(entry.updated) : undefined,
    categories: (feed.category || []).map((c: any) => c.term || c),
    guid: entry.id?.$?.value,
  }));
  
  return {
    id: crypto.randomUUID(),
    title: feed.title || 'Untitled Feed',
    link: feed.link?.href,
    subtitle: feed.subtitle,
    updated: feed.updated ? new Date(feed.updated) : undefined,
    generator: feed.generator,
    items: entries,
    rawUrl: '',
  };
}

// Main parse function
export async function parseFeed(url: string, data: string): Promise<ParseResult> {
  try {
    const feedType = detectFeedType(data);
    
    if (feedType === 'unknown') {
      return {
        success: false,
        error: 'Could not detect feed type',
        feedType,
      };
    }
    
    const xml = await parseStringPromise(data, parseOptions);
    let feed: Feed;
    
    if (feedType === 'rss') {
      feed = await parseRSS(xml);
    } else {
      feed = await parseAtom(xml);
    }
    
    feed.rawUrl = url;
    feed.lastFetchedAt = new Date();
    
    return {
      success: true,
      feed,
      feedType,
    };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error occurred',
      feedType: detectFeedType(data),
    };
  }
}

// Fetch feed from URL
export async function fetchFeed(
  url: string, 
  auth?: { username: string; password: string }
): Promise<ParseResult> {
  try {
    const config: any = {
      timeout: 15000,
      validateStatus: (status: number) => status >= 200 && status < 400,
    };
    
    if (auth) {
      config.auth = {
        username: auth.username,
        password: auth.password,
      };
    }
    
    const response = await axios.get(url, config);
    return await parseFeed(url, response.data);
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Failed to fetch feed',
    };
  }
}

// Batch fetch multiple feeds
export async function fetchFeeds(
  subscriptions: FeedSubscription[]
): Promise<Map<string, ParseResult>> {
  const results = new Map<string, ParseResult>();
  
  for (const subscription of subscriptions) {
    const auth = subscription.httpAuth;
    const result = await fetchFeed(subscription.url, auth);
    results.set(subscription.id, result);
  }
  
  return results;
}
