import { NextFunction, Request, Response } from 'express';
import Parser from 'rss-parser';
import urls from '../utils/urls';
import { obtainPreviewNews } from '../utils/obtainPreviews';
import { requestGot } from '../utils/requestCall';
import RadioStationModel, {
  RadioStation,
} from '../database/models/radiostation.model';
import ThemeModel, { Theme } from '../database/models/theme.model';
import ThemeParser from '../utils/animeTheme';
import { structureThemes } from '../utils/util';
import { getThemes } from '../utils/util';
import WaifuModel, { Waifu } from '../database/models/waifu.model';
import util from 'util';
import { hashStringMd5 } from '../utils/util';
import { redisClient } from '../database/connection';

// @ts-ignore
redisClient.get = util.promisify(redisClient.get);

/*
  UtilsController - controller to parse the
  feed and get news, all with scraping and
  parsing RSS.
*/

const themeParser = new ThemeParser();

type CustomFeed = {
  foo: string;
};
type CustomItem = {
  bar: number;
  itunes: { duration: string; image: string };
  'content:encoded': string;
  'content:encodedSnippet': string;
};

const parser: Parser<CustomFeed, CustomItem> = new Parser({
  customFields: {
    feed: ['foo'],
    item: ['bar'],
  },
});

interface News {
  title?: string;
  url?: string;
  author?: string;
  thumbnail?: string;
  content?: string;
}

interface Podcast {
  title?: string;
  duration?: string;
  created?: Date | string;
  mp3?: string;
}

interface rssPage {
  url: string;
  author: string;
  content: string;
}

export default class UtilsController {
  async getAnitakume(req: Request, res: Response, next: NextFunction) {
    let feed: CustomFeed & Parser.Output<CustomItem>;

    try {
      if (redisClient.connected) {
        const resultQueryRedis: any = await redisClient.get(
          `anitakume_${hashStringMd5('anitakume')}`,
        );

        if (resultQueryRedis) {
          const resultRedis: any = JSON.parse(resultQueryRedis);

          return res.status(200).json(resultRedis);
        }
      }

      feed = await parser.parseURL(urls.BASE_IVOOX);
    } catch (err) {
      return next(err);
    }

    const podcast: Podcast[] = [];
    const monthNames = [
      'Enero',
      'Febrero',
      'Marzo',
      'Abril',
      'Mayo',
      'Junio',
      'Julio',
      'Agosto',
      'Septiembre',
      'Octubre',
      'Noviembre',
      'Diciembre',
    ];

    feed.items.forEach((item: any) => {
      const date: Date = new Date(item.pubDate!);

      const formattedObject: Podcast = {
        title: item.title,
        duration: item.itunes.duration,
        created: `${date.getDate()} de ${
          monthNames[date.getMonth()]
        } de ${date.getFullYear()}`,
        mp3: item.enclosure?.url,
      };

      podcast.push(formattedObject);
    });

    if (podcast.length > 0) {
      if (redisClient.connected) {
        /* Set the key in the redis cache. */

        redisClient.set(
          `anitakume_${hashStringMd5('anitakume')}`,
          JSON.stringify({ podcast }),
        );

        /* After 24hrs expire the key. */

        redisClient.expireat(
          `anitakume_${hashStringMd5('anitakume')}`,
          parseInt(`${+new Date() / 1000}`, 10) + 7200,
        );
      }

      res.status(200).json({ podcast });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getNews(req: Request, res: Response, next: NextFunction) {
    const news: News[] = [];
    const pagesRss: rssPage[] = [
      { url: urls.BASE_KUDASAI, author: 'Kudasai', content: 'content_encoded' },
      {
        url: urls.BASE_RAMENPARADOS,
        author: 'Ramen para dos',
        content: 'content',
      },
      {
        url: urls.BASE_CRUNCHYROLL,
        author: 'Crunchyroll',
        content: 'content_encoded',
      },
    ];

    try {
      if (redisClient.connected) {
        const resultQueryRedis: any = await redisClient.get(
          `news_${hashStringMd5('news')}`,
        );

        if (resultQueryRedis) {
          const resultRedis: any = JSON.parse(resultQueryRedis);

          return res.status(200).json(resultRedis);
        }
      }

      for (const rssPage of pagesRss) {
        const feed = await parser.parseURL(rssPage.url);

        feed.items.forEach((item: any) => {
          const formattedObject: News = {
            title: item.title,
            url: item.link,
            author: feed.title?.includes('Crunchyroll')
              ? 'Crunchyroll'
              : feed.title,
            thumbnail: obtainPreviewNews(item['content:encoded']),
            content: item['content:encoded'],
          };

          news.push(formattedObject);
        });
      }
    } catch (err) {
      return next(err);
    }

    if (news.length > 0) {
      if (redisClient.connected) {
        /* Set the key in the redis cache. */

        redisClient.set(
          `news_${hashStringMd5('news')}`,
          JSON.stringify({ news }),
        );

        /* After 24hrs expire the key. */

        redisClient.expireat(
          `news_${hashStringMd5('news')}`,
          parseInt(`${+new Date() / 1000}`, 10) + 7200,
        );
      }

      res.status(200).json({ news });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getImages(req: Request, res: Response, next: NextFunction) {
    const { title } = req.params;
    let data: any;

    try {
      if (redisClient.connected) {
        const resultQueryRedis: any = await redisClient.get(
          `images_${hashStringMd5(title)}`,
        );

        if (resultQueryRedis) {
          const resultRedis: any = JSON.parse(resultQueryRedis);

          return res.status(200).json(resultRedis);
        }
      }

      data = await requestGot(
        `${urls.BASE_QWANT}t=images&q=${encodeURIComponent(
          title,
        )}&count=51&locale=es_ES&safesearch=1`,
        { scrapy: false, parse: true },
      );
    } catch (err) {
      return next(err);
    }

    const results: any[] = data.data.result.items.map((item: any) => {
      return {
        type: item.thumb_type,
        thumbnail: `https:${item.thumbnail}`,
        fullsize: `https:${item.media_fullsize}`,
      };
    });

    if (results.length > 0) {
      if (redisClient.connected) {
        /* Set the key in the redis cache. */

        redisClient.set(
          `images_${hashStringMd5(title)}`,
          JSON.stringify({ images: results }),
        );

        /* After 24hrs expire the key. */

        redisClient.expireat(
          `images_${hashStringMd5(title)}`,
          parseInt(`${+new Date() / 1000}`, 10) + 7200,
        );
      }

      res.set('Cache-Control', 'max-age=604800');
      res.status(200).json({ images: results });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getVideos(req: Request, res: Response, next: NextFunction) {
    const { channelId } = req.params;
    let data: any;

    try {
      if (redisClient.connected) {
        const resultQueryRedis: any = await redisClient.get(
          `videos_${hashStringMd5(channelId)}`,
        );

        if (resultQueryRedis) {
          const resultRedis: any = JSON.parse(resultQueryRedis);

          return res.status(200).json(resultRedis);
        }
      }

      data = await requestGot(
        `${urls.BASE_YOUTUBE}${channelId}&part=snippet,id&order=date&maxResults=50`,
        { scrapy: false, parse: true },
      );
    } catch (err) {
      return next(err);
    }

    const results: any[] = data.items.map((item: any) => {
      return {
        title: item.snippet.title,
        videoId: item.id.videoId,
        thumbDefault: item.snippet.thumbnails.default.url,
        thumbMedium: item.snippet.thumbnails.medium.url,
        thumbHigh: item.snippet.thumbnails.high.url,
      };
    });

    if (results.length > 0) {
      if (redisClient.connected) {
        /* Set the key in the redis cache. */

        redisClient.set(
          `videos_${hashStringMd5(channelId)}`,
          JSON.stringify({ videos: results }),
        );

        /* After 24hrs expire the key. */

        redisClient.expireat(
          `videos_${hashStringMd5(channelId)}`,
          parseInt(`${+new Date() / 1000}`, 10) + 7200,
        );
      }

      res.status(200).json({ videos: results });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getSectionVideos(req: Request, res: Response, next: NextFunction) {
    const { type } = req.params;
    let y1: any, y2: any, y3: any;
    let data: any;

    try {
      if (type === 'learn') {
        data = await requestGot(
          `${urls.BASE_YOUTUBE}UCCyQwSS6m2mVB0-H2FOFJtw&part=snippet,id&order=date&maxResults=50`,
          { parse: true, scrapy: false },
        );
      } else if (type === 'amv') {
        y1 = await requestGot(
          `${urls.BASE_YOUTUBE}UCkTFkshjAsLMKwhAe1uPC1A&part=snippet,id&order=date&maxResults=25`,
          { parse: true, scrapy: false },
        );

        y2 = await requestGot(
          `${urls.BASE_YOUTUBE}UC2cpvlLeowpqnR6bQofwNew&part=snippet,id&order=date&maxResults=25`,
          { parse: true, scrapy: false },
        );
      } else if (type === 'produccer') {
        y1 = await requestGot(
          `${urls.BASE_YOUTUBE}UC-5MT-BUxTzkPTWMediyV0w&part=snippet,id&order=date&maxResults=25`,
          { parse: true, scrapy: false },
        );

        y2 = await requestGot(
          `${urls.BASE_YOUTUBE}UCwUeTOXP3DD9DIvHttowuSA&part=snippet,id&order=date&maxResults=25`,
          { parse: true, scrapy: false },
        );

        y3 = await requestGot(
          `${urls.BASE_YOUTUBE}UCA8Vj7nN8bzT3rsukD2ypUg&part=snippet,id&order=date&maxResults=25`,
          { parse: true, scrapy: false },
        );
      }
    } catch (err) {
      return next(err);
    }

    if (data && !y1 && !y2 && !y3) {
      const results: any[] = data.items.map((item: any) => ({
        title: item.snippet.title,
        videoId: item.id.videoId,
        thumbDefault: item.snippet.thumbnails.default.url,
        thumbMedium: item.snippet.thumbnails.medium.url,
        thumbHigh: item.snippet.thumbnails.high.url,
      }));

      res.status(200).json({ videos: results });
    } else if (!data && y1 && y2 && !y3) {
      const results: any[] = y1.items.concat(y2.items).map((item: any) => ({
        title: item.snippet.title,
        videoId: item.id.videoId,
        thumbDefault: item.snippet.thumbnails.default.url,
        thumbMedium: item.snippet.thumbnails.medium.url,
        thumbHigh: item.snippet.thumbnails.high.url,
      }));

      res.status(200).json({ videos: results });
    } else if (!data && y1 && y2 && y3) {
      const results: any[] = y1.items
        .concat(y2.items.concat(y3.items))
        .map((item: any) => ({
          title: item.snippet.title,
          videoId: item.id.videoId,
          thumbDefault: item.snippet.thumbnails.default.url,
          thumbMedium: item.snippet.thumbnails.medium.url,
          thumbHigh: item.snippet.thumbnails.high.url,
        }));

      res.status(200).json({ videos: results });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getRadioStations(req: Request, res: Response, next: NextFunction) {
    let data: RadioStation[];

    try {
      data = await RadioStationModel.find();
    } catch (err) {
      return next(err);
    }

    const results: any[] = data.map((item: RadioStation) => {
      return {
        name: item.name,
        url: item.url,
      };
    });

    if (results.length > 0) {
      res.status(200).json({ stations: results });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getAllThemes(req: Request, res: Response, next: NextFunction) {
    let data: Theme[];

    try {
      data = await ThemeModel.find();
    } catch (err) {
      return next(err);
    }

    const results: any[] = data.map((item: Theme) => {
      return {
        id: item.id,
        title: item.title,
        year: item.year,
        themes: item.themes,
      };
    });

    if (results.length > 0) {
      res.status(200).json({ themes: results });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getOpAndEd(req: Request, res: Response, next: NextFunction) {
    const { title } = req.params;
    let themes: any;

    try {
      if (redisClient.connected) {
        const resultQueryRedis: any = await redisClient.get(
          `oped_${hashStringMd5(title)}`,
        );

        if (resultQueryRedis) {
          const resultRedis: any = JSON.parse(resultQueryRedis);

          return res.status(200).json(resultRedis);
        }
      }

      themes = await structureThemes(await themeParser.serie(title), true);
    } catch (err) {
      return next(err);
    }

    if (themes) {
      if (redisClient.connected) {
        /* Set the key in the redis cache. */

        redisClient.set(
          `oped_${hashStringMd5(title)}`,
          JSON.stringify({ themes }),
        );

        /* After 24hrs expire the key. */

        redisClient.expireat(
          `oped_${hashStringMd5(title)}`,
          parseInt(`${+new Date() / 1000}`, 10) + 7200,
        );
      }

      res.status(200).json({ themes });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getThemesYear(req: Request, res: Response, next: NextFunction) {
    const { year } = req.params;
    let themes: any;
    let resultQueryRedis: any;

    try {
      if (redisClient.connected) {
        if (year) {
          resultQueryRedis = await redisClient.get(
            `themesyear_${hashStringMd5(year)}`,
          );
        } else {
          resultQueryRedis = await redisClient.get(
            `themesyear_${hashStringMd5('allYear')}`,
          );
        }

        if (resultQueryRedis) {
          const resultRedis: any = JSON.parse(resultQueryRedis);

          return res.status(200).json(resultRedis);
        }
      }

      if (year === undefined) {
        themes = await themeParser.allYears();
      } else {
        themes = await structureThemes(await themeParser.year(year), false);
      }
    } catch (err) {
      return next(err);
    }

    if (themes.length > 0) {
      if (redisClient.connected) {
        /* Set the key in the redis cache. */

        if (year) {
          redisClient.set(
            `themesyear_${hashStringMd5(year)}`,
            JSON.stringify({ themes }),
          );
        } else {
          redisClient.set(
            `themesyear_${hashStringMd5('allYear')}`,
            JSON.stringify({ themes }),
          );
        }

        /* After 24hrs expire the key. */

        if (year) {
          redisClient.expireat(
            `themesyear_${hashStringMd5(year)}`,
            parseInt(`${+new Date() / 1000}`, 10) + 7200,
          );
        } else {
          redisClient.expireat(
            `themesyear_${hashStringMd5('allYear')}`,
            parseInt(`${+new Date() / 1000}`, 10) + 7200,
          );
        }
      }

      res.status(200).json({ themes });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async randomTheme(req: Request, res: Response, next: NextFunction) {
    let data: any;

    try {
      data = await requestGot(`${urls.BASE_THEMEMOE}roulette`, {
        parse: true,
        scrapy: false,
      });
    } catch (err) {
      return next(err);
    }

    const random: any[] = getThemes(data.themes);

    if (random.length > 0) {
      res.set('Cache-Control', 'no-store');
      res.status(200).json({ random });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getArtist(req: Request, res: Response, next: NextFunction) {
    const { id } = req.params;
    let artists: any;

    try {
      if (id === undefined) {
        artists = await themeParser.artists();
      } else {
        artists = await structureThemes(await themeParser.artist(id), false);
      }
    } catch (err) {
      return next(err);
    }

    if (artists.length > 0) {
      res.status(200).json({ artists });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getDestAnimePlatforms(req: Request, res: Response, next: NextFunction) {
    let data: any;

    try {
      data = await requestGot(
        `${urls.BASE_ARUPPI}res/documents/animelegal/top.json`,
        { parse: true, scrapy: false },
      );
    } catch (err) {
      return next(err);
    }

    const destPlatforms: any[] = data.map((item: any) => {
      return {
        id: item.id,
        name: item.name,
        logo: item.logo,
        link: item.link,
      };
    });

    if (destPlatforms.length > 0) {
      res.status(200).json({ destPlatforms });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getPlatforms(req: Request, res: Response, next: NextFunction) {
    const { id } = req.params;
    let data: any;

    try {
      if (id === undefined) {
        data = await requestGot(
          `${urls.BASE_ARUPPI}res/documents/animelegal/typeplatforms.json`,
          { parse: true, scrapy: false },
        );
      } else if (
        id === 'producers' ||
        id === 'apps' ||
        id === 'publishers' ||
        'events'
      ) {
        data = await requestGot(
          `${urls.BASE_ARUPPI}res/documents/animelegal/type/${id}.json`,
          { parse: true, scrapy: false },
        );
      } else {
        data = await requestGot(
          `${urls.BASE_ARUPPI}res/documents/animelegal/type/${id}.json`,
          { parse: true, scrapy: false },
        );
      }
    } catch (err) {
      return next(err);
    }

    const platforms: any[] = data.map((item: any) => {
      if (id === undefined) {
        return {
          id: item.id,
          name: item.name,
          comming: item.comming || false,
          cover: item.cover,
        };
      } else if (
        id === 'producers' ||
        id === 'apps' ||
        id === 'publishers' ||
        'events'
      ) {
        return {
          id: item.id,
          name: item.name,
          logo: item.logo,
          cover: item.cover,
          description: item.description,
          type: item.type,
          moreInfo: item.moreInfo,
          facebook: item.facebook,
          twitter: item.twitter,
          instagram: item.instagram,
          webInfo: item.webInfo,
          webpage: item.webpage,
        };
      } else {
        return {
          id: item.id,
          name: item.name,
          type: item.type,
          logo: item.logo,
          cover: item.cover,
          webpage: item.webpage,
        };
      }
    });

    if (platforms.length > 0) {
      res.status(200).json({ platforms });
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }

  async getWaifuRandom(req: Request, res: Response, next: NextFunction) {
    let waifuQuery: Waifu[] | null;
    let waifuResult: any;

    try {
      waifuQuery = await WaifuModel.aggregate([{ $sample: { size: 1 } }]);
    } catch (err) {
      return next(err);
    }

    if (waifuQuery.length > 0) {
      waifuResult = {
        id: waifuQuery[0].id,
        name: waifuQuery[0].name,
        weight: waifuQuery[0].weight,
        series: waifuQuery[0].series,
        height: waifuQuery[0].height,
        birthday: waifuQuery[0].birthday,
        likes: waifuQuery[0].likes,
        trash: waifuQuery[0].trash,
        blood_type: waifuQuery[0].blood_type,
        hip: waifuQuery[0].hip,
        bust: waifuQuery[0].bust,
        description: waifuQuery[0].description,
        display_picture: waifuQuery[0].display_picture,
        waist: waifuQuery[0].waist,
      };
    }

    if (waifuResult) {
      res.set('Cache-Control', 'no-store');
      res.status(200).json(waifuResult);
    } else {
      res.status(500).json({ message: 'Aruppi lost in the shell' });
    }
  }
}
