/* eslint-disable @typescript-eslint/no-explicit-any */
'use client';

import { useState, useEffect } from 'react';
import Image from 'next/image';
import styles from './page.module.css';
import {
  Config,
  Resolution,
  SortBy,
  Quality,
  VisualTag,
  AudioTag,
  Encode,
  ServiceDetail,
  ServiceCredential,
  StreamType,
} from '@aiostreams/types';
import SortableCardList from '../../components/SortableCardList';
import ServiceInput from '../../components/ServiceInput';
import AddonsList from '../../components/AddonsList';
import { Slide, ToastContainer, toast } from 'react-toastify';
import showToast, { toastOptions } from '@/components/Toasts';
import addonPackage from '../../../../../package.json';
import { formatSize } from '@aiostreams/formatters';
import {
  allowedFormatters,
  allowedLanguages,
  validateConfig,
} from '@aiostreams/config';
import {
  addonDetails,
  isValueEncrypted,
  serviceDetails,
  Settings,
} from '@aiostreams/utils';

import Slider from '@/components/Slider';
import CredentialInput from '@/components/CredentialInput';
import CreateableSelect from '@/components/CreateableSelect';
import MultiSelect from '@/components/MutliSelect';
import InstallWindow from '@/components/InstallWindow';
import FormatterPreview from '@/components/FormatterPreview';
import CustomFormatter from '@/components/CustomFormatter';

const version = addonPackage.version;

interface Option {
  label: string;
  value: string;
}

const defaultQualities: Quality[] = [
  { 'BluRay REMUX': true },
  { BluRay: true },
  { 'WEB-DL': true },
  { WEBRip: true },
  { HDRip: true },
  { 'HC HD-Rip': true },
  { DVDRip: true },
  { HDTV: true },
  { CAM: true },
  { TS: true },
  { TC: true },
  { SCR: true },
  { Unknown: true },
];

const defaultVisualTags: VisualTag[] = [
  { 'HDR+DV': true },
  { 'HDR10+': true },
  { DV: true },
  { HDR10: true },
  { HDR: true },
  { '10bit': true },
  { '3D': true },
  { IMAX: true },
  { AI: true },
  { SDR: true },
];

const defaultAudioTags: AudioTag[] = [
  { Atmos: true },
  { 'DD+': true },
  { DD: true },
  { 'DTS-HD MA': true },
  { 'DTS-HD': true },
  { DTS: true },
  { TrueHD: true },
  { '5.1': true },
  { '7.1': true },
  { FLAC: true },
  { AAC: true },
];

const defaultEncodes: Encode[] = [
  { AV1: true },
  { HEVC: true },
  { AVC: true },
  { Xvid: true },
  { DivX: true },
  { 'H-OU': true },
  { 'H-SBS': true },
  { Unknown: true },
];

const defaultSortCriteria: SortBy[] = [
  { cached: true, direction: 'desc' },
  { personal: true, direction: 'desc' },
  { resolution: true },
  { language: true },
  { size: true, direction: 'desc' },
  { streamType: false },
  { visualTag: false },
  { service: false },
  { audioTag: false },
  { encode: false },
  { quality: false },
  { seeders: false, direction: 'desc' },
  { addon: false },
  { regexSort: false, direction: 'desc' },
];

const defaultResolutions: Resolution[] = [
  { '2160p': true },
  { '1440p': true },
  { '1080p': true },
  { '720p': true },
  { '480p': true },
  { Unknown: true },
];

const defaultServices = serviceDetails.map((service) => ({
  name: service.name,
  id: service.id,
  enabled: false,
  credentials: {},
}));

const defaultStreamTypes: StreamType[] = [
  { usenet: true },
  { debrid: true },
  { unknown: true },
  { p2p: true },
  { live: true },
];

export default function Configure() {
  const [formatterOptions, setFormatterOptions] = useState<string[]>(
    allowedFormatters.filter((f) => f !== 'imposter')
  );
  const [streamTypes, setStreamTypes] =
    useState<StreamType[]>(defaultStreamTypes);
  const [resolutions, setResolutions] =
    useState<Resolution[]>(defaultResolutions);
  const [qualities, setQualities] = useState<Quality[]>(defaultQualities);
  const [visualTags, setVisualTags] = useState<VisualTag[]>(defaultVisualTags);
  const [audioTags, setAudioTags] = useState<AudioTag[]>(defaultAudioTags);
  const [encodes, setEncodes] = useState<Encode[]>(defaultEncodes);
  const [sortCriteria, setSortCriteria] =
    useState<SortBy[]>(defaultSortCriteria);
  const [formatter, setFormatter] = useState<string>();
  const [services, setServices] = useState<Config['services']>(defaultServices);
  const [onlyShowCachedStreams, setOnlyShowCachedStreams] =
    useState<boolean>(false);
  const [prioritisedLanguages, setPrioritisedLanguages] = useState<
    string[] | null
  >(null);
  const [excludedLanguages, setExcludedLanguages] = useState<string[] | null>(
    null
  );
  const [addons, setAddons] = useState<Config['addons']>([]);
  /*
  const [maxSize, setMaxSize] = useState<number | null>(null);
  const [minSize, setMinSize] = useState<number | null>(null);
  */
  const [maxMovieSize, setMaxMovieSize] = useState<number | null>(null);
  const [minMovieSize, setMinMovieSize] = useState<number | null>(null);
  const [maxEpisodeSize, setMaxEpisodeSize] = useState<number | null>(null);
  const [minEpisodeSize, setMinEpisodeSize] = useState<number | null>(null);
  const [cleanResults, setCleanResults] = useState<boolean>(false);
  const [maxResultsPerResolution, setMaxResultsPerResolution] = useState<
    number | null
  >(null);
  const [excludeFilters, setExcludeFilters] = useState<readonly Option[]>([]);
  const [strictIncludeFilters, setStrictIncludeFilters] = useState<
    readonly Option[]
  >([]);
  /*
  const [prioritiseIncludeFilters, setPrioritiseIncludeFilters] = useState<
    readonly Option[]
  >([]);
  */
  const [mediaFlowEnabled, setMediaFlowEnabled] = useState<boolean>(false);
  const [mediaFlowProxyUrl, setMediaFlowProxyUrl] = useState<string>('');
  const [mediaFlowApiPassword, setMediaFlowApiPassword] = useState<string>('');
  const [mediaFlowPublicIp, setMediaFlowPublicIp] = useState<string>('');
  const [mediaFlowProxiedAddons, setMediaFlowProxiedAddons] = useState<
    string[] | null
  >(null);
  const [mediaFlowProxiedServices, setMediaFlowProxiedServices] = useState<
    string[] | null
  >(null);

  const [stremThruEnabled, setStremThruEnabled] = useState<boolean>(false);
  const [stremThruUrl, setStremThruUrl] = useState<string>('');
  const [stremThruCredential, setStremThruCredential] = useState<string>('');
  const [stremThruPublicIp, setStremThruPublicIp] = useState<string>('');
  const [stremThruProxiedAddons, setStremThruProxiedAddons] = useState<
    string[] | null
  >(null);
  const [stremThruProxiedServices, setStremThruProxiedServices] = useState<
    string[] | null
  >(null);

  const [overrideName, setOverrideName] = useState<string>('');
  const [apiKey, setApiKey] = useState<string>('');

  const [disableButtons, setDisableButtons] = useState<boolean>(false);
  const [maxMovieSizeSlider, setMaxMovieSizeSlider] = useState<number>(
    Settings.MAX_MOVIE_SIZE
  );
  const [maxEpisodeSizeSlider, setMaxEpisodeSizeSlider] = useState<number>(
    Settings.MAX_EPISODE_SIZE
  );
  const [choosableAddons, setChoosableAddons] = useState<string[]>(
    addonDetails.map((addon) => addon.id)
  );
  const [showApiKeyInput, setShowApiKeyInput] = useState<boolean>(false);
  const [manifestUrl, setManifestUrl] = useState<string | null>(null);
  const [regexFilters, setRegexFilters] = useState<{
    excludePattern?: string;
    includePattern?: string;
  }>({});
  const [regexSortPatterns, setRegexSortPatterns] = useState<string>('');

  useEffect(() => {
    // get config from the server
    fetch('/get-addon-config')
      .then((res) => res.json())
      .then((data) => {
        if (data.success) {
          setMaxMovieSizeSlider(data.maxMovieSize);
          setMaxEpisodeSizeSlider(data.maxEpisodeSize);
          setShowApiKeyInput(data.apiKeyRequired);
          // filter out 'torrentio' from choosableAddons if torrentioDisabled is true
          if (data.torrentioDisabled) {
            setChoosableAddons(
              addonDetails
                .map((addon) => addon.id)
                .filter((id) => id !== 'torrentio')
            );
          }
        }
      });
  }, []);

  const createConfig = (): Config => {
    const config = {
      apiKey: apiKey,
      overrideName,
      streamTypes,
      resolutions,
      qualities,
      visualTags,
      audioTags,
      encodes,
      sortBy: sortCriteria,
      onlyShowCachedStreams,
      prioritisedLanguages,
      excludedLanguages,
      maxMovieSize,
      minMovieSize,
      maxEpisodeSize,
      minEpisodeSize,
      cleanResults,
      maxResultsPerResolution,
      strictIncludeFilters:
        strictIncludeFilters.length > 0
          ? strictIncludeFilters.map((filter) => filter.value)
          : null,
      excludeFilters:
        excludeFilters.length > 0
          ? excludeFilters.map((filter) => filter.value)
          : null,
      formatter: formatter || 'gdrive',
      mediaFlowConfig: {
        mediaFlowEnabled: mediaFlowEnabled && !stremThruEnabled,
        proxyUrl: mediaFlowProxyUrl,
        apiPassword: mediaFlowApiPassword,
        publicIp: mediaFlowPublicIp,
        proxiedAddons: mediaFlowProxiedAddons,
        proxiedServices: mediaFlowProxiedServices,
      },
      stremThruConfig: {
        stremThruEnabled: stremThruEnabled && !mediaFlowEnabled,
        url: stremThruUrl,
        credential: stremThruCredential,
        publicIp: stremThruPublicIp,
        proxiedAddons: stremThruProxiedAddons,
        proxiedServices: stremThruProxiedServices,
      },
      addons,
      services,
      regexFilters:
        regexFilters.excludePattern || regexFilters.includePattern
          ? {
              excludePattern: regexFilters.excludePattern || undefined,
              includePattern: regexFilters.includePattern || undefined,
            }
          : undefined,
      regexSortPatterns: regexSortPatterns,
    };
    return config;
  };

  const fetchWithTimeout = async (
    url: string,
    options: RequestInit | undefined,
    timeoutMs = 30000
  ) => {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), timeoutMs);
    try {
      console.log('Fetching', url, `with data: ${options?.body}`);
      const res = await fetch(url, { ...options, signal: controller.signal });
      clearTimeout(timeout);
      return res;
    } catch {
      console.log('Clearing timeout');
      return clearTimeout(timeout);
    }
  };

  const getManifestUrl = async (
    protocol = window.location.protocol,
    root = window.location.host
  ): Promise<{
    success: boolean;
    manifest: string | null;
    message: string | null;
  }> => {
    const config = createConfig();
    const { valid, errorMessage } = validateConfig(config, 'client');
    if (!valid) {
      return {
        success: false,
        manifest: null,
        message: errorMessage || 'Invalid config',
      };
    }
    console.log('Config', config);
    setDisableButtons(true);

    try {
      const encryptPath = `/encrypt-user-data`;
      const response = await fetchWithTimeout(encryptPath, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ data: JSON.stringify(config) }),
      });
      if (!response) {
        throw new Error('encrypt-user-data failed: no response within timeout');
      }

      if (!response.ok) {
        throw new Error(
          `encrypt-user-data failed with status ${response.status} and statusText ${response.statusText}`
        );
      }

      const data = await response.json();
      if (!data.success) {
        if (data.error) {
          return {
            success: false,
            manifest: null,
            message: data.error || 'Failed to generate config',
          };
        }
        throw new Error(`Encryption service failed, ${data.message}`);
      }

      const configString = data.data;
      return {
        success: true,
        manifest: `${protocol}//${root}/${configString}/manifest.json`,
        message: null,
      };
    } catch (error: any) {
      console.error(error);
      return {
        success: false,
        manifest: null,
        message: error.message || 'Failed to encrypt config',
      };
    }
  };

  const loadValidValuesFromObject = (
    object: { [key: string]: boolean }[] | undefined,
    validValues: { [key: string]: boolean }[]
  ) => {
    if (!object) {
      return validValues;
    }

    const mergedValues = object.filter((value) =>
      validValues.some((validValue) =>
        Object.keys(validValue).includes(Object.keys(value)[0])
      )
    );

    for (const validValue of validValues) {
      if (
        !mergedValues.some(
          (value) => Object.keys(value)[0] === Object.keys(validValue)[0]
        )
      ) {
        mergedValues.push(validValue);
      }
    }

    return mergedValues;
  };

  const loadValidSortCriteria = (sortCriteria: Config['sortBy']) => {
    if (!sortCriteria) {
      return defaultSortCriteria;
    }

    const mergedValues = sortCriteria
      .map((sort) => {
        const defaultSort = defaultSortCriteria.find(
          (defaultSort) => Object.keys(defaultSort)[0] === Object.keys(sort)[0]
        );
        if (!defaultSort) {
          return null;
        }
        return {
          ...sort,
          direction: defaultSort?.direction // only load direction if it exists in the defaultSort
            ? sort.direction || defaultSort.direction
            : undefined,
        };
      })
      .filter((sort) => sort !== null);

    defaultSortCriteria.forEach((defaultSort) => {
      if (
        !mergedValues.some(
          (sort) => Object.keys(sort)[0] === Object.keys(defaultSort)[0]
        )
      ) {
        mergedValues.push({
          ...defaultSort,
          direction: defaultSort.direction || undefined,
        });
      }
    });

    return mergedValues;
  };

  const validateValue = (value: string | null, validValues: string[]) => {
    if (!value) {
      return null;
    }
    return validValues.includes(value) ? value : null;
  };

  const loadValidServices = (services: Config['services']) => {
    if (!services) {
      return defaultServices;
    }

    const mergedServices = services
      // filter out services that are not in serviceDetails
      .filter((service) => defaultServices.some((ds) => ds.id === service.id))
      .map((service) => {
        const defaultService = defaultServices.find(
          (ds) => ds.id === service.id
        );
        if (!defaultService) {
          return null;
        }

        // only load enabled and credentials from the previous config
        return {
          ...defaultService,
          enabled: service.enabled,
          credentials: service.credentials,
        };
      })
      .filter((service) => service !== null);

    // add any services that are in defaultServices but not in services
    defaultServices.forEach((defaultService) => {
      if (!mergedServices.some((service) => service.id === defaultService.id)) {
        mergedServices.push(defaultService);
      }
    });

    return mergedServices;
  };

  const loadValidAddons = (addons: Config['addons']) => {
    if (!addons) {
      return [];
    }
    return addons.filter((addon) =>
      addonDetails.some((detail) => detail.id === addon.id)
    );
  };

  // Load config from the window path if it exists
  useEffect(() => {
    async function decodeConfig(config: string) {
      let decodedConfig: Config;
      if (isValueEncrypted(config) || config.startsWith('B-')) {
        throw new Error('Encrypted Config Not Supported');
      } else {
        decodedConfig = JSON.parse(
          Buffer.from(decodeURIComponent(config), 'base64').toString('utf-8')
        );
      }
      return decodedConfig;
    }

    function loadFromConfig(decodedConfig: Config) {
      console.log('Loaded config', decodedConfig);
      setOverrideName(decodedConfig.overrideName || '');
      setStreamTypes(
        loadValidValuesFromObject(decodedConfig.streamTypes, defaultStreamTypes)
      );
      setResolutions(
        loadValidValuesFromObject(decodedConfig.resolutions, defaultResolutions)
      );
      setQualities(
        loadValidValuesFromObject(decodedConfig.qualities, defaultQualities)
      );
      setVisualTags(
        loadValidValuesFromObject(decodedConfig.visualTags, defaultVisualTags)
      );
      setAudioTags(
        loadValidValuesFromObject(decodedConfig.audioTags, defaultAudioTags)
      );
      setEncodes(
        loadValidValuesFromObject(decodedConfig.encodes, defaultEncodes)
      );
      setSortCriteria(loadValidSortCriteria(decodedConfig.sortBy));
      setOnlyShowCachedStreams(decodedConfig.onlyShowCachedStreams || false);
      // create an array for prioritised languages. if the old prioritiseLanguage is set, add it to the array
      const finalPrioritisedLanguages =
        decodedConfig.prioritisedLanguages || [];
      if (decodedConfig.prioritiseLanguage) {
        finalPrioritisedLanguages.push(decodedConfig.prioritiseLanguage);
      }
      setPrioritisedLanguages(
        finalPrioritisedLanguages.filter((lang) =>
          allowedLanguages.includes(lang)
        ) || null
      );
      setExcludedLanguages(
        decodedConfig.excludedLanguages?.filter((lang) =>
          allowedLanguages.includes(lang)
        ) || null
      );
      setStrictIncludeFilters(
        decodedConfig.strictIncludeFilters?.map((filter) => ({
          label: filter,
          value: filter,
        })) || []
      );
      setExcludeFilters(
        decodedConfig.excludeFilters?.map((filter) => ({
          label: filter,
          value: filter,
        })) || []
      );
      setRegexFilters(decodedConfig.regexFilters || {});
      setRegexSortPatterns(decodedConfig.regexSortPatterns || '');

      setServices(loadValidServices(decodedConfig.services));
      setMaxMovieSize(
        decodedConfig.maxMovieSize || decodedConfig.maxSize || null
      );
      setMinMovieSize(
        decodedConfig.minMovieSize || decodedConfig.minSize || null
      );
      setMaxEpisodeSize(
        decodedConfig.maxEpisodeSize || decodedConfig.maxSize || null
      );
      setMinEpisodeSize(
        decodedConfig.minEpisodeSize || decodedConfig.minSize || null
      );
      setAddons(loadValidAddons(decodedConfig.addons));
      setCleanResults(decodedConfig.cleanResults || false);
      setMaxResultsPerResolution(decodedConfig.maxResultsPerResolution || null);
      setMediaFlowEnabled(
        decodedConfig.mediaFlowConfig?.mediaFlowEnabled || false
      );
      setMediaFlowProxyUrl(decodedConfig.mediaFlowConfig?.proxyUrl || '');
      setMediaFlowApiPassword(decodedConfig.mediaFlowConfig?.apiPassword || '');
      setMediaFlowPublicIp(decodedConfig.mediaFlowConfig?.publicIp || '');
      setMediaFlowProxiedAddons(
        decodedConfig.mediaFlowConfig?.proxiedAddons || null
      );
      setMediaFlowProxiedServices(
        decodedConfig.mediaFlowConfig?.proxiedServices || null
      );
      setStremThruEnabled(
        decodedConfig.stremThruConfig?.stremThruEnabled || false
      );
      setStremThruUrl(decodedConfig.stremThruConfig?.url || '');
      setStremThruCredential(decodedConfig.stremThruConfig?.credential || '');
      setStremThruPublicIp(decodedConfig.stremThruConfig?.publicIp || '');
      setApiKey(decodedConfig.apiKey || '');

      // set formatter
      const formatterValue = validateValue(
        decodedConfig.formatter,
        allowedFormatters
      );
      if (
        decodedConfig.formatter.startsWith('custom') &&
        decodedConfig.formatter.length > 7
      ) {
        setFormatter(decodedConfig.formatter);
      } else if (formatterValue) {
        setFormatter(formatterValue);
      }
    }

    const path = window.location.pathname;
    try {
      const configMatch = path.match(/\/([^/]+)\/configure/);

      if (configMatch) {
        const config = configMatch[1];
        decodeConfig(config).then(loadFromConfig);
      }
    } catch (error) {
      console.error('Failed to load config', error);
    }
  }, []);

  return (
    <div className={styles.container}>
      <div className={styles.content}>
        <button
          className={styles.supportMeButton}
          onClick={() => {
            window.open(
              'https://github.com/sponsors/Viren070',
              '_blank',
              'noopener noreferrer'
            );
          }}
        >
          <svg
            fill="#b30000"
            height="24px"
            width="24px"
            version="1.1"
            id="Capa_1"
            xmlns="http://www.w3.org/2000/svg"
            xmlnsXlink="http://www.w3.org/1999/xlink"
            viewBox="0 0 490 490"
            xmlSpace="preserve"
            stroke="#b30000"
          >
            <g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
            <g
              id="SVGRepo_tracerCarrier"
              strokeLinecap="round"
              strokeLinejoin="round"
            ></g>
            <g id="SVGRepo_iconCarrier">
              {' '}
              <path
                id="XMLID_25_"
                d="M316.554,108.336c4.553,6.922,2.629,16.223-4.296,20.774c-3.44,2.261-6.677,4.928-9.621,7.929 c-2.938,2.995-6.825,4.497-10.715,4.497c-3.791,0-7.585-1.427-10.506-4.291c-5.917-5.801-6.009-15.298-0.207-21.212 c4.439-4.524,9.338-8.559,14.562-11.992C302.698,99.491,312.002,101.414,316.554,108.336z M447.022,285.869 c-1.506,1.536-148.839,151.704-148.839,151.704C283.994,452.035,265.106,460,245,460s-38.994-7.965-53.183-22.427L42.978,285.869 c-57.304-58.406-57.304-153.441,0-211.847C70.83,45.634,107.882,30,147.31,30c36.369,0,70.72,13.304,97.69,37.648 C271.971,43.304,306.32,30,342.689,30c39.428,0,76.481,15.634,104.332,44.021C504.326,132.428,504.326,227.463,447.022,285.869z M425.596,95.028C403.434,72.44,373.991,60,342.69,60c-31.301,0-60.745,12.439-82.906,35.027c-1.122,1.144-2.129,2.533-3.538,3.777 c-7.536,6.654-16.372,6.32-22.491,0c-1.308-1.352-2.416-2.633-3.538-3.777C208.055,72.44,178.612,60,147.31,60 c-31.301,0-60.744,12.439-82.906,35.027c-45.94,46.824-45.94,123.012,0,169.836c1.367,1.393,148.839,151.704,148.839,151.704 C221.742,425.229,233.02,430,245,430c11.98,0,23.258-4.771,31.757-13.433l148.839-151.703l0,0 C471.535,218.04,471.535,141.852,425.596,95.028z M404.169,116.034c-8.975-9.148-19.475-16.045-31.208-20.499 c-7.746-2.939-16.413,0.953-19.355,8.698c-2.942,7.744,0.953,16.407,8.701,19.348c7.645,2.902,14.521,7.431,20.436,13.459 c23.211,23.658,23.211,62.153,0,85.811l-52.648,53.661c-5.803,5.915-5.711,15.412,0.206,21.212 c2.921,2.863,6.714,4.291,10.506,4.291c3.889,0,7.776-1.502,10.714-4.497l52.648-53.661 C438.744,208.616,438.744,151.275,404.169,116.034z"
              ></path>{' '}
            </g>
          </svg>
          Donate
        </button>
        <div className={styles.header}>
          <Image
            src="/assets/logo.png"
            alt="AIOStreams Logo"
            width={200}
            height={200}
            style={{ display: 'block', margin: '0 auto' }}
          />
          <div style={{ position: 'relative', display: 'inline-block' }}>
            <input
              type="text"
              value={overrideName || 'AIOStreams'}
              onChange={(e) => setOverrideName(e.target.value)}
              style={{
                border: 'none',
                backgroundColor: 'black',
                color: 'white',
                fontWeight: 'bold',
                background: 'black',
                height: '30px',
                textAlign: 'center',
                fontSize: '30px',
                padding: '0',
                maxWidth: '300px',
                width: 'auto',
                margin: '0 auto',
              }}
              size={overrideName?.length < 8 ? 8 : overrideName?.length || 8}
            ></input>
            <span
              className={styles.version}
              title={`See what's new in v${version}`}
              onClick={() => {
                window.open(
                  `https://github.com/Viren070/AIOStreams/releases/tag/v${version}`,
                  '_blank',
                  'noopener noreferrer'
                );
              }}
            >
              v{version}
            </span>
          </div>
          {process.env.NEXT_PUBLIC_BRANDING && (
            <div
              className={styles.branding}
              dangerouslySetInnerHTML={{
                __html: process.env.NEXT_PUBLIC_BRANDING || '',
              }}
            />
          )}
          <p style={{ textAlign: 'center', padding: '15px' }}>
            AIOStreams, the all-in-one streaming addon for Stremio. Combine your
            streams from all your addons into one and filter them by resolution,
            quality, visual tags and more.
            <br />
            <br />
            This addon will return any result from the addons you enable. These
            can be P2P results, direct links, or anything else. Results that are
            P2P are marked as P2P, however.
            <br />
            <br />
            This addon also has no persistence. Nothing you enter here is
            stored. They are encrypted within the manifest URL and are only used
            to retrieve streams from any addons you enable.
          </p>
          <p style={{ textAlign: 'center', padding: '15px' }}>
            <a
              href="https://guides.viren070.me/stremio/addons/aiostreams"
              target="_blank"
              rel="noreferrer"
              style={{ textDecoration: 'underline' }}
            >
              Configuration Guide
            </a>
            {' | '}
            <a
              href="https://github.com/Viren070/AIOStreams"
              target="_blank"
              rel="noreferrer"
              style={{ textDecoration: 'underline' }}
            >
              GitHub
            </a>

            {' | '}
            <a
              href="https://guides.viren070.me/stremio"
              target="_blank"
              rel="noreferrer"
              style={{ textDecoration: 'underline' }}
            >
              Stremio Guide
            </a>
          </p>
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Services</h2>
          <p style={{ padding: '5px' }}>
            Enable the services you have accounts with and enter your
            credentials.
          </p>
          {services.map((service, index) => (
            <ServiceInput
              key={service.id}
              serviceName={service.name}
              enabled={service.enabled}
              setEnabled={(enabled) => {
                const newServices = [...services];
                const serviceIndex = newServices.findIndex(
                  (s) => s.id === service.id
                );
                newServices[serviceIndex] = { ...service, enabled };
                setServices(newServices);
              }}
              fields={
                serviceDetails
                  .find((detail: ServiceDetail) => detail.id === service.id)
                  ?.credentials.map((credential: ServiceCredential) => ({
                    label: credential.label,
                    link: credential.link,
                    value: service.credentials[credential.id] || '',
                    setValue: (value) => {
                      const newServices = [...services];
                      const serviceIndex = newServices.findIndex(
                        (s) => s.id === service.id
                      );
                      newServices[serviceIndex] = {
                        ...service,
                        credentials: {
                          ...service.credentials,
                          [credential.id]: value,
                        },
                      };
                      setServices(newServices);
                    },
                  })) || []
              }
              moveService={(direction) => {
                const newServices = [...services];
                const serviceIndex = newServices.findIndex(
                  (s) => s.id === service.id
                );
                const [movedService] = newServices.splice(serviceIndex, 1);
                if (direction === 'up' && serviceIndex > 0) {
                  newServices.splice(serviceIndex - 1, 0, movedService);
                } else if (
                  direction === 'down' &&
                  serviceIndex < newServices.length
                ) {
                  newServices.splice(serviceIndex + 1, 0, movedService);
                }
                setServices(newServices);
              }}
              canMoveUp={index > 0}
              canMoveDown={index < services.length - 1}
              signUpLink={
                serviceDetails.find((detail) => detail.id === service.id)
                  ?.signUpLink
              }
            />
          ))}

          <div
            className={styles.section}
            style={{ marginTop: '20px', marginBottom: '0px' }}
          >
            <div className={styles.setting}>
              <div className={styles.settingDescription}>
                <h2 style={{ padding: '5px' }}>Only Show Cached Streams</h2>
                <p style={{ padding: '5px' }}>
                  Only show streams that are cached by the enabled services.
                </p>
              </div>
              <div className={styles.settingInput}>
                <input
                  type="checkbox"
                  checked={onlyShowCachedStreams}
                  onChange={(e) => setOnlyShowCachedStreams(e.target.checked)}
                  // move to the right
                  style={{
                    marginLeft: 'auto',
                    marginRight: '20px',
                    width: '25px',
                    height: '25px',
                  }}
                />
              </div>
            </div>
          </div>
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Addons</h2>
          <AddonsList
            choosableAddons={choosableAddons}
            addonDetails={addonDetails}
            addons={addons}
            setAddons={setAddons}
          />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Stream Types</h2>
          <p style={{ padding: '5px' }}>
            Choose which stream types you want to see and reorder their priority
            if needed. You can uncheck P2P to remove P2P streams from the
            results.
          </p>
          <SortableCardList items={streamTypes} setItems={setStreamTypes} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Resolutions</h2>
          <p style={{ padding: '5px' }}>
            Choose which resolutions you want to see and reorder their priority
            if needed.
          </p>
          <SortableCardList items={resolutions} setItems={setResolutions} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Qualities</h2>
          <p style={{ padding: '5px' }}>
            Choose which qualities you want to see and reorder their priority if
            needed.
          </p>
          <SortableCardList items={qualities} setItems={setQualities} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Visual Tags</h2>
          <p style={{ padding: '5px' }}>
            Choose which visual tags you want to see and reorder their priority
            if needed.
          </p>
          <SortableCardList items={visualTags} setItems={setVisualTags} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Audio Tags</h2>
          <p style={{ padding: '5px' }}>
            Choose which audio tags you want to see and reorder their priority
            if needed.
          </p>
          <SortableCardList items={audioTags} setItems={setAudioTags} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Encodes</h2>
          <p style={{ padding: '5px' }}>
            Choose which encodes you want to see and reorder their priority if
            needed.
          </p>
          <SortableCardList items={encodes} setItems={setEncodes} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px' }}>Sort By</h2>
          <p style={{ padding: '5px' }}>
            Choose the criteria by which to sort streams.
          </p>
          <SortableCardList items={sortCriteria} setItems={setSortCriteria} />
        </div>

        <div className={styles.section}>
          <h2 style={{ padding: '5px', margin: '0px ' }}>Languages</h2>
          <p style={{ margin: '5px 0 12px 5px' }}>
            Choose which languages you want to prioritise and exclude from the
            results
          </p>
          <div className={styles.section}>
            <div>
              <h3 style={{ margin: '2px 0 2px 0' }}>Prioritise Languages</h3>
              <p style={{ margin: '10px 0 10px 0' }}>
                Any results that are detected to have one of the prioritised
                languages will be sorted according to your sort criteria. You
                must have the <code>Langage</code> sort criteria enabled for
                this to work. If there are multiple results with a different
                prioritised language, the order is determined by the order of
                the prioritised languages.
              </p>
            </div>
            <div>
              <MultiSelect
                options={allowedLanguages
                  .sort((a, b) => a.localeCompare(b))
                  .map((language) => ({ value: language, label: language }))}
                setValues={setPrioritisedLanguages}
                values={prioritisedLanguages || []}
              />
            </div>
          </div>
          <div style={{ marginBottom: '0px' }} className={styles.section}>
            <div>
              <h3 style={{ margin: '2px 0 2px 0' }}>Exclude Languages</h3>
              <p style={{ margin: '10px 0 10px 0' }}>
                Any results that are detected to have an excluded language will
                be removed from the results. A result will only be excluded if
                it only has one of or more of the excluded languages. If it
                contains a language that is not excluded, it will still be
                included.
              </p>
            </div>
            <div>
              <MultiSelect
                options={allowedLanguages
                  .sort((a, b) => a.localeCompare(b))
                  .map((language) => ({ value: language, label: language }))}
                setValues={setExcludedLanguages}
                values={excludedLanguages || []}
              />
            </div>
          </div>
        </div>

        <div className={styles.section}>
          <div>
            <div>
              <h2 style={{ padding: '5px', margin: '0px ' }}>Keyword Filter</h2>
              <p style={{ margin: '5px 0 12px 5px' }}>
                Filter streams by keywords. You can exclude streams that contain
                specific keywords or only include streams that contain specific
                keywords.
              </p>
            </div>
            <div style={{ marginBottom: '0px' }}>
              <div className={styles.section}>
                <h3 style={{ margin: '2px 0 2px 0' }}>Exclude Filter</h3>
                <p style={{ margin: '10px 0 10px 0' }}>
                  Enter keywords to filter streams by. Streams that contain any
                  of the keywords will be excluded.
                </p>
                <CreateableSelect
                  value={excludeFilters}
                  setValue={setExcludeFilters}
                />
              </div>
              <div className={styles.section} style={{ marginBottom: '0px' }}>
                <h3 style={{ margin: '2px 0 2px 0' }}>Include Filter</h3>
                <p style={{ margin: '10px 0 10px 0' }}>
                  Enter keywords to filter streams by. Streams that do not
                  contain any of the keywords will be excluded.
                </p>
                <CreateableSelect
                  value={strictIncludeFilters}
                  setValue={setStrictIncludeFilters}
                />
              </div>
            </div>
          </div>
        </div>

        {showApiKeyInput && (
          <div className={styles.section}>
            <div>
              <h2 style={{ padding: '5px', margin: '0px ' }}>
                Regex Filtering
              </h2>
              <p style={{ margin: '5px 0 12px 5px' }}>
                Configure regex patterns to filter streams. These filters will
                be applied in addition to keyword filters.
              </p>
            </div>
            <div style={{ marginBottom: '0px' }}>
              <div className={styles.section}>
                <h3 style={{ margin: '2px 0 2px 0' }}>Exclude Pattern</h3>
                <p style={{ margin: '10px 0 10px 0' }}>
                  Enter a regex pattern to exclude streams. Streams will be
                  excluded if their filename OR indexers match this pattern.
                </p>
                <input
                  type="text"
                  value={regexFilters.excludePattern || ''}
                  onChange={(e) =>
                    setRegexFilters({
                      ...regexFilters,
                      excludePattern: e.target.value,
                    })
                  }
                  placeholder="Example: \b(0neshot|1XBET)\b"
                  className={styles.input}
                />
                <p className={styles.helpText}>
                  Example patterns:
                  <br />
                  - \b(0neshot|1XBET|24xHD)\b (exclude 0neshot, 1XBET, and 24xHD
                  releases)
                  <br />- ^.*Hi10.*$ (exclude Hi10 profile releases)
                </p>
              </div>
              <div className={styles.section} style={{ marginBottom: '0px' }}>
                <h3 style={{ margin: '2px 0 2px 0' }}>Include Pattern</h3>
                <p style={{ margin: '10px 0 10px 0' }}>
                  Enter a regex pattern to include streams. Only streams whose
                  filename or indexers match this pattern will be included.
                </p>
                <input
                  type="text"
                  value={regexFilters.includePattern || ''}
                  onChange={(e) =>
                    setRegexFilters({
                      ...regexFilters,
                      includePattern: e.target.value,
                    })
                  }
                  placeholder="Example: \b(3L|BiZKiT)\b"
                  className={styles.input}
                />
                <p className={styles.helpText}>
                  Example patterns:
                  <br />- \b(3L|BiZKiT|BLURANiUM)\b (only include 3L, BiZKiT,
                  and BLURANiUM releases)
                </p>
              </div>
            </div>
          </div>
        )}

        {showApiKeyInput && (
          <div className={styles.section}>
            <h2 style={{ padding: '5px' }}>Regex Sort Patterns</h2>
            <p style={{ padding: '5px' }}>
              Enter a space separated list of regex patterns, optionally with a
              name, to sort streams by. Streams will be sorted based on the
              order of matching patterns. Matching files will come first in
              descending order, and last in ascending order for each pattern.
              You can give each regex a name using the following syntax:
              <br />
              <br />
              <code>regexName{`<::>`}regexPattern</code>
              <br />
              <br />
              For example, <code>3L{`<::>`}\b(3L|BiZKiT)\b</code> will sort
              streams matching the regex <code>\b(3L|BiZKiT)\b</code> first and
              those streams will have the <code>{`regexMatched`}</code> property
              with the value <code>3L</code> in the custom formatter.
            </p>
            <input
              type="text"
              value={regexSortPatterns}
              onChange={(e) => setRegexSortPatterns(e.target.value)}
              placeholder="Example: \b(3L|BiZKiT)\b \b(FraMeSToR)\b"
              style={{
                width: '97.5%',
                padding: '5px',
                marginLeft: '5px',
              }}
              className={styles.input}
            />
            <p className={styles.helpText}>
              Example patterns:
              <br />- \b(3L|BiZKiT|BLURANiUM)\b \b(FraMeSToR)\b (sort
              3L/BiZKiT/BLURANiUM releases first, then FraMeSToR releases)
            </p>
          </div>
        )}
        <div className={styles.section}>
          <div className={styles.slidersSetting}>
            <div>
              <h2 style={{ padding: '5px' }}>Size Filter</h2>
              <p style={{ padding: '5px' }}>
                Filter streams by size. Leave the maximum and minimum size
                sliders at opposite ends to disable the filter.
              </p>
            </div>
            <div className={styles.slidersContainer}>
              <Slider
                maxValue={maxMovieSizeSlider}
                value={minMovieSize || 0}
                setValue={setMinMovieSize}
                defaultValue="min"
                id="minMovieSizeSlider"
              />
              <div className={styles.sliderValue}>
                Minimum movie size: {formatSize(minMovieSize || 0)}
              </div>
              <Slider
                maxValue={maxMovieSizeSlider}
                value={
                  maxMovieSize === null ? maxMovieSizeSlider : maxMovieSize
                }
                setValue={setMaxMovieSize}
                defaultValue="max"
                id="maxMovieSizeSlider"
              />
              <div className={styles.sliderValue}>
                Maximum movie size:{' '}
                {maxMovieSize === null ? 'Unlimited' : formatSize(maxMovieSize)}
              </div>
              <Slider
                maxValue={maxEpisodeSizeSlider}
                value={minEpisodeSize || 0}
                setValue={setMinEpisodeSize}
                defaultValue="min"
                id="minEpisodeSizeSlider"
              />
              <div className={styles.sliderValue}>
                Minimum episode size: {formatSize(minEpisodeSize || 0)}
              </div>
              <Slider
                maxValue={maxEpisodeSizeSlider}
                value={
                  maxEpisodeSize === null
                    ? maxEpisodeSizeSlider
                    : maxEpisodeSize
                }
                setValue={setMaxEpisodeSize}
                defaultValue="max"
                id="maxEpisodeSizeSlider"
              />
              <div className={styles.sliderValue}>
                Maximum episode size:{' '}
                {maxEpisodeSize === null
                  ? 'Unlimited'
                  : formatSize(maxEpisodeSize)}
              </div>
            </div>
          </div>
        </div>

        <div className={styles.section}>
          <div className={styles.setting}>
            <div className={styles.settingDescription}>
              <h2 style={{ padding: '5px' }}>Limit results per resolution</h2>
              <p style={{ padding: '5px' }}>
                Limit the number of results per resolution. Leave empty to show
                all results.
              </p>
            </div>
            <div className={styles.settingInput}>
              <input
                type="number"
                value={maxResultsPerResolution || ''}
                onChange={(e) =>
                  setMaxResultsPerResolution(
                    e.target.value ? parseInt(e.target.value) : null
                  )
                }
                style={{
                  width: '100px',
                  height: '30px',
                }}
              />
            </div>
          </div>
        </div>

        <div className={styles.section}>
          <div className={styles.setting}>
            <div className={styles.settingDescription}>
              <h2 style={{ padding: '5px' }}>Formatter</h2>
              <p style={{ padding: '5px' }}>
                Change how your stream results are f
                <span
                  onClick={() => {
                    if (formatterOptions.includes('imposter')) {
                      return;
                    }
                    showToast(
                      "What's this doing here....?",
                      'info',
                      'ImposterFormatter'
                    );
                    setFormatterOptions([...formatterOptions, 'imposter']);
                  }}
                >
                  ◌
                </span>
                rmatted.
              </p>
            </div>
            <div className={styles.settingInput}>
              <select
                value={formatter?.startsWith('custom') ? 'custom' : formatter}
                onChange={(e) => setFormatter(e.target.value)}
              >
                {formatterOptions.map((formatter) => (
                  <option key={formatter} value={formatter}>
                    {formatter}
                  </option>
                ))}
              </select>
            </div>
          </div>
          {formatter?.startsWith('custom') && (
            <CustomFormatter
              formatter={formatter}
              setFormatter={setFormatter}
            />
          )}
          <FormatterPreview formatter={formatter || 'gdrive'} />
        </div>

        <div className={styles.section}>
          <div className={styles.setting}>
            <div className={styles.ettingDescription}>
              <h2 style={{ padding: '5px' }}>Clean Results</h2>
              <p style={{ padding: '5px' }}>
                Attempt to remove duplicate results. For a given file with
                duplicate streams: one uncached stream from all uncached streams
                is selected per provider. One cached stream from only one
                provider is selected. For duplicates without a provider, one
                stream is selected at random.
              </p>
            </div>
            <div className={styles.checkboxSettingInput}>
              <input
                type="checkbox"
                checked={cleanResults}
                onChange={(e) => setCleanResults(e.target.checked)}
                // move to the right
                style={{
                  marginLeft: 'auto',
                  marginRight: '20px',
                  width: '25px',
                  height: '25px',
                }}
              />
            </div>
          </div>
        </div>

        <div className={styles.section}>
          <div className={styles.setting}>
            <div className={styles.settingDescription}>
              <h2 style={{ padding: '5px' }}>MediaFlow</h2>
              <p style={{ padding: '5px' }}>
                Use MediaFlow to proxy your streams
              </p>
            </div>
            <div className={styles.settingInput}>
              <input
                type="checkbox"
                checked={mediaFlowEnabled && !stremThruEnabled}
                disabled={stremThruEnabled}
                onChange={(e) => {
                  setMediaFlowEnabled(e.target.checked);
                }}
                style={{
                  width: '25px',
                  height: '25px',
                }}
              />
            </div>
          </div>
          {
            <div
              className={`${styles.mediaFlowConfig} ${mediaFlowEnabled ? '' : styles.hidden}`}
            >
              <div className={styles.mediaFlowSection}>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>Proxy URL</h3>
                    <p style={{ padding: '5px' }}>
                      The URL of the MediaFlow proxy server
                    </p>
                  </div>
                  <div>
                    <CredentialInput
                      credential={mediaFlowProxyUrl}
                      setCredential={setMediaFlowProxyUrl}
                      inputProps={{
                        placeholder: 'Enter your MediaFlow proxy URL',
                        disabled: !mediaFlowEnabled,
                      }}
                    />
                  </div>
                </div>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>API Password</h3>
                    <p style={{ padding: '5px' }}>
                      Your MediaFlow&apos;s API password
                    </p>
                  </div>
                  <div>
                    <CredentialInput
                      credential={mediaFlowApiPassword}
                      setCredential={setMediaFlowApiPassword}
                      inputProps={{
                        placeholder: 'Enter your MediaFlow API password',
                        disabled: !mediaFlowEnabled,
                      }}
                    />
                  </div>
                </div>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>Public IP (Optional)</h3>
                    <p style={{ padding: '5px' }}>
                      Configure this only when running MediaFlow locally with a
                      proxy service. Leave empty if MediaFlow is configured
                      locally without a proxy server or if it&apos;s hosted on a
                      remote server.
                    </p>
                  </div>
                  <div>
                    <CredentialInput
                      credential={mediaFlowPublicIp}
                      setCredential={setMediaFlowPublicIp}
                      inputProps={{
                        placeholder: 'Enter your MediaFlow public IP',
                        disabled: !mediaFlowEnabled,
                      }}
                    />
                  </div>
                </div>
              </div>
              <div className={styles.mediaFlowSection}>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>Proxy Addons (Optional)</h3>
                    <p style={{ padding: '5px' }}>
                      By default, all streams from every addon are proxied.
                      Choose specific addons here to proxy only their streams.
                    </p>
                  </div>
                  <div>
                    <MultiSelect
                      options={
                        addons.map((addon) => ({
                          value: `${addon.id}-${JSON.stringify(addon.options)}`,
                          label:
                            addon.options.addonName ||
                            addon.options.overrideName ||
                            addon.options.name ||
                            addon.id.charAt(0).toUpperCase() +
                              addon.id.slice(1),
                        })) || []
                      }
                      setValues={(selectedAddons) => {
                        setMediaFlowProxiedAddons(
                          selectedAddons.length === 0 ? null : selectedAddons
                        );
                      }}
                      values={mediaFlowProxiedAddons || undefined}
                    />
                  </div>
                </div>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>
                      Proxy Services (Optional)
                    </h3>
                    <p style={{ padding: '5px' }}>
                      By default, all streams whether they are from a serivce or
                      not are proxied. Choose which services you want to proxy
                      through MediaFlow. Selecting None will also proxy streams
                      that are not (detected to be) from a service.
                    </p>
                  </div>
                  <div>
                    <MultiSelect
                      options={[
                        { value: 'none', label: 'None' },
                        ...serviceDetails.map((service) => ({
                          value: service.id,
                          label: service.name,
                        })),
                      ]}
                      setValues={(selectedServices) => {
                        setMediaFlowProxiedServices(
                          selectedServices.length === 0
                            ? null
                            : selectedServices
                        );
                      }}
                      values={mediaFlowProxiedServices || undefined}
                    />
                  </div>
                </div>
              </div>
            </div>
          }
        </div>

        <div className={styles.section}>
          <div className={styles.setting}>
            <div className={styles.settingDescription}>
              <h2 style={{ padding: '5px' }}>StremThru</h2>
              <p style={{ padding: '5px' }}>
                Use StremThru to proxy your streams
              </p>
            </div>
            <div className={styles.settingInput}>
              <input
                type="checkbox"
                checked={stremThruEnabled && !mediaFlowEnabled}
                disabled={mediaFlowEnabled}
                onChange={(e) => {
                  setStremThruEnabled(e.target.checked);
                }}
                style={{
                  width: '25px',
                  height: '25px',
                }}
              />
            </div>
          </div>
          {
            <div
              className={`${styles.stremThruConfig} ${stremThruEnabled ? '' : styles.hidden}`}
            >
              <div className={styles.stremThruSection}>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>StremThru URL</h3>
                    <p style={{ padding: '5px' }}>
                      The URL of the StremThru server
                    </p>
                  </div>
                  <div>
                    <CredentialInput
                      credential={stremThruUrl}
                      setCredential={setStremThruUrl}
                      inputProps={{
                        placeholder: 'Enter your StremThru URL',
                        disabled: !stremThruEnabled,
                      }}
                    />
                  </div>
                </div>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>Credential</h3>
                    <p style={{ padding: '5px' }}>Your StremThru Credential</p>
                  </div>
                  <div>
                    <CredentialInput
                      credential={stremThruCredential}
                      setCredential={setStremThruCredential}
                      inputProps={{
                        placeholder: 'Enter your StremThru Credential',
                        disabled: !stremThruEnabled,
                      }}
                    />
                  </div>
                </div>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>Public IP (Optional)</h3>
                    <p style={{ padding: '5px' }}>
                      Set the publicly exposed IP for StremThru server.
                    </p>
                  </div>
                  <div>
                    <CredentialInput
                      credential={stremThruPublicIp}
                      setCredential={setStremThruPublicIp}
                      inputProps={{
                        placeholder: 'Enter your StremThru public IP',
                        disabled: !stremThruEnabled,
                      }}
                    />
                  </div>
                </div>
              </div>
              <div className={styles.stremThruSection}>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>Proxy Addons (Optional)</h3>
                    <p style={{ padding: '5px' }}>
                      By default, all streams from every addon are proxied.
                      Choose specific addons here to proxy only their streams.
                    </p>
                  </div>
                  <div>
                    <MultiSelect
                      options={
                        addons.map((addon) => ({
                          value: `${addon.id}-${JSON.stringify(addon.options)}`,
                          label:
                            addon.options.addonName ||
                            addon.options.overrideName ||
                            addon.options.name ||
                            addon.id.charAt(0).toUpperCase() +
                              addon.id.slice(1),
                        })) || []
                      }
                      setValues={(selectedAddons) => {
                        setStremThruProxiedAddons(
                          selectedAddons.length === 0 ? null : selectedAddons
                        );
                      }}
                      values={stremThruProxiedAddons || undefined}
                    />
                  </div>
                </div>
                <div>
                  <div>
                    <h3 style={{ padding: '5px' }}>
                      Proxy Services (Optional)
                    </h3>
                    <p style={{ padding: '5px' }}>
                      By default, all streams whether they are from a serivce or
                      not are proxied. Choose which services you want to proxy
                      through StremThru. Selecting None will also proxy streams
                      that are not (detected to be) from a service.
                    </p>
                  </div>
                  <div>
                    <MultiSelect
                      options={[
                        { value: 'none', label: 'None' },
                        ...serviceDetails.map((service) => ({
                          value: service.id,
                          label: service.name,
                        })),
                      ]}
                      setValues={(selectedServices) => {
                        setStremThruProxiedServices(
                          selectedServices.length === 0
                            ? null
                            : selectedServices
                        );
                      }}
                      values={stremThruProxiedServices || undefined}
                    />
                  </div>
                </div>
              </div>
            </div>
          }
        </div>

        {showApiKeyInput && (
          <div className={styles.section}>
            <div className={styles.setting}>
              <div className={styles.settingDescription}>
                <h2 style={{ padding: '5px' }}>API Key</h2>
                <p style={{ padding: '5px' }}>
                  Enter your AIOStreams API Key to install and use this addon.
                  You need to enter the one that is set in the{' '}
                  <code>API_KEY</code> environment variable.
                </p>
              </div>
              <div className={styles.settingInput}>
                <CredentialInput
                  credential={apiKey}
                  setCredential={setApiKey}
                  inputProps={{
                    placeholder: 'Enter your API key',
                  }}
                />
              </div>
            </div>
          </div>
        )}

        <div className={styles.installButtons}>
          <button
            className={styles.installButton}
            disabled={disableButtons}
            onClick={() => {
              setDisableButtons(true);
              const id = toast.loading('Generating manifest URL...', {
                ...toastOptions,
                toastId: 'generatingManifestUrl',
              });
              getManifestUrl()
                .then((value) => {
                  const { success, manifest, message } = value;
                  if (!success || !manifest) {
                    toast.update(id, {
                      render: message || 'Failed to generate manifest URL',
                      type: 'error',
                      autoClose: 5000,
                      isLoading: false,
                    });
                    setDisableButtons(false);
                    return;
                  }
                  toast.update(id, {
                    render: 'Manifest URL generated',
                    type: 'success',
                    autoClose: 5000,
                    isLoading: false,
                  });
                  setManifestUrl(manifest);
                  setDisableButtons(false);
                })
                .catch((error: any) => {
                  console.error(error);
                  toast.update(id, {
                    render:
                      'An unexpected error occurred while generating the manifest URL',
                    type: 'error',
                    autoClose: 5000,
                    isLoading: false,
                  });
                  setDisableButtons(false);
                });
            }}
          >
            Generate Manifest URL
          </button>
          <InstallWindow
            manifestUrl={manifestUrl}
            setManifestUrl={setManifestUrl}
          />
        </div>
      </div>
      <ToastContainer
        stacked
        position="top-center"
        transition={Slide}
        draggablePercent={30}
      />
    </div>
  );
}
