import {
  ArrowDownIcon,
  ArrowLongDownIcon,
  ArrowLongUpIcon,
  ClockIcon,
  StarIcon,
} from '@heroicons/react/24/outline';
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid';
import { PluginCard, SectionHeading } from '@nx/nx-dev/ui-common';
import { useState } from 'react';

interface Plugin {
  description: string;
  name: string;
  url: string;
  isOfficial: boolean;
  lastPublishedDate?: string;
  npmDownloads?: string;
  githubStars?: string;
  nxVersion?: string;
}

type OrderByStatus =
  | 'lastPublishDate'
  | 'npmDownloads'
  | 'githubStars'
  | 'nxVersion'
  | undefined;
interface Modifiers {
  term: string;
  officialStatus: 'official' | 'community' | undefined;
  minimumDownloads: number | undefined;
  minimumStars: number | undefined;
  minimumNxVersion: string | undefined;
  orderBy: OrderByStatus;
  orderDirection: 'ASC' | 'DESC';
}

export function PluginDirectory({
  pluginList,
}: {
  pluginList: Plugin[];
}): JSX.Element {
  const [modifiers, setModifiers] = useState<Modifiers>({
    term: '',
    officialStatus: undefined,
    minimumDownloads: undefined,
    minimumStars: undefined,
    minimumNxVersion: undefined,
    orderBy: undefined,
    orderDirection: 'ASC',
  });
  function setOrderBy(status: OrderByStatus) {
    if (modifiers.orderBy === status) {
      setModifiers({
        ...modifiers,
        orderDirection: modifiers.orderDirection === 'ASC' ? 'DESC' : 'ASC',
      });
    } else {
      setModifiers({
        ...modifiers,
        orderBy: status,
      });
    }
  }
  return (
    <div id="plugin-directory">
      <div className="flex w-full flex-col justify-between gap-8 md:flex-row ">
        <SectionHeading as="h2" variant="display" id="plugins-registry">
          <span className="whitespace-nowrap">Nx Plugins</span> Registry
        </SectionHeading>
        <div>
          <label htmlFor="search" className="sr-only">
            Quick search
          </label>
          <div className="relative">
            <div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
              <MagnifyingGlassIcon className="h-5 w-5 text-slate-500" />
            </div>
            <input
              id="search"
              name="search"
              className="block w-full rounded-md border border-slate-300 bg-white py-2 pl-10 pr-3 text-sm placeholder-slate-500 transition focus:placeholder-slate-400 dark:border-slate-900 dark:bg-slate-700"
              placeholder="Quick search"
              onChange={(event) =>
                setModifiers({ ...modifiers, term: event.target.value })
              }
              type="search"
            />
          </div>
          <div className="text-xs my-2 flex whitespace-nowrap">
            <div className="py-1 mr-1">Order by:</div>
            <div className="flex flex-wrap gap-1">
              <button
                className="rounded-sm border border-slate-200 bg-white py-1 px-1 font-semibold transition hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
                onClick={() => setOrderBy('lastPublishDate')}
              >
                <ClockIcon className="h-4 w-4 inline-block mr-0.5 align-bottom"></ClockIcon>
                Release Date
                {modifiers.orderBy === 'lastPublishDate' &&
                modifiers.orderDirection === 'DESC' ? (
                  <ArrowLongUpIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongUpIcon>
                ) : null}
                {modifiers.orderBy === 'lastPublishDate' &&
                modifiers.orderDirection === 'ASC' ? (
                  <ArrowLongDownIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongDownIcon>
                ) : null}
              </button>
              <button
                className="rounded-sm border border-slate-200 bg-white py-1 px-1 font-semibold transition hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
                onClick={() => setOrderBy('npmDownloads')}
              >
                <ArrowDownIcon className="h-4 w-4 inline-block mr-0.5 align-bottom"></ArrowDownIcon>
                Downloads
                {modifiers.orderBy === 'npmDownloads' &&
                modifiers.orderDirection === 'DESC' ? (
                  <ArrowLongUpIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongUpIcon>
                ) : null}
                {modifiers.orderBy === 'npmDownloads' &&
                modifiers.orderDirection === 'ASC' ? (
                  <ArrowLongDownIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongDownIcon>
                ) : null}
              </button>
              <button
                className="rounded-sm border border-slate-200 bg-white py-1 px-1 font-semibold transition hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
                onClick={() => setOrderBy('githubStars')}
              >
                <StarIcon className="h-4 w-4 inline-block mr-0.5 align-bottom"></StarIcon>
                GH Stars
                {modifiers.orderBy === 'githubStars' &&
                modifiers.orderDirection === 'DESC' ? (
                  <ArrowLongUpIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongUpIcon>
                ) : null}
                {modifiers.orderBy === 'githubStars' &&
                modifiers.orderDirection === 'ASC' ? (
                  <ArrowLongDownIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongDownIcon>
                ) : null}
              </button>
              <button
                className="rounded-sm border border-slate-200 bg-white py-1 px-1 font-semibold transition hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-800 dark:hover:bg-slate-700"
                onClick={() => setOrderBy('nxVersion')}
              >
                {/* Nx Logo */}
                <svg
                  role="img"
                  viewBox="0 0 24 24"
                  xmlns="http://www.w3.org/2000/svg"
                  className="h-4 w-4 inline-block mx-0.5 align-bottom"
                  fill="currentColor"
                >
                  <title>Nx</title>
                  <path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z" />
                </svg>
                Nx Version
                {modifiers.orderBy === 'nxVersion' &&
                modifiers.orderDirection === 'DESC' ? (
                  <ArrowLongUpIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongUpIcon>
                ) : null}
                {modifiers.orderBy === 'nxVersion' &&
                modifiers.orderDirection === 'ASC' ? (
                  <ArrowLongDownIcon className="h-4 w-4 inline-block ml-0.5 align-bottom"></ArrowLongDownIcon>
                ) : null}
              </button>
            </div>
          </div>
        </div>
      </div>
      <div className="my-12 grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
        {pluginList
          .filter((plugin) =>
            !!modifiers.term
              ? plugin.name
                  .toLowerCase()
                  .includes(modifiers.term.toLowerCase()) ||
                plugin.description
                  .toLowerCase()
                  .includes(modifiers.term.toLowerCase())
              : true
          )
          .sort((a, b) => {
            if (modifiers.orderBy === 'lastPublishDate') {
              return (
                (modifiers.orderDirection === 'ASC' ? 1 : -1) *
                (new Date(a.lastPublishedDate || '').getTime() -
                  new Date(b.lastPublishedDate || '').getTime())
              );
            } else if (modifiers.orderBy === 'npmDownloads') {
              return (
                (modifiers.orderDirection === 'ASC' ? 1 : -1) *
                (Number.parseInt(a.npmDownloads || '0') -
                  Number.parseInt(b.npmDownloads || '0'))
              );
            } else if (modifiers.orderBy === 'githubStars') {
              return (
                (modifiers.orderDirection === 'ASC' ? 1 : -1) *
                (Number.parseInt(a.githubStars || '0') -
                  Number.parseInt(b.githubStars || '0'))
              );
            } else if (modifiers.orderBy === 'nxVersion') {
              const versionValueMap: Record<string, number> = {
                unknown: 0,
                '12': 12,
                '13': 13,
                '14': 14,
                '15': 15,
                '16': 16,
                '17': 17,
                '18': 18,
                '>= 15': 17,
                '>= 14': 16,
                '>= 13': 15,
                official: 1000,
              };
              function getValueFromVersion(version: string = 'unknown') {
                const mapKey =
                  Object.keys(versionValueMap).find((key) =>
                    version.startsWith(key)
                  ) || 'unknown';
                return versionValueMap[mapKey];
              }
              return (
                (modifiers.orderDirection === 'ASC' ? 1 : -1) *
                (getValueFromVersion(a.nxVersion) -
                  getValueFromVersion(b.nxVersion))
              );
            }
            return 0;
          })
          .map((plugin) => (
            <PluginCard
              key={plugin.name}
              name={plugin.name}
              description={plugin.description}
              isOfficial={plugin.isOfficial}
              lastPublishedDate={plugin.lastPublishedDate}
              npmDownloads={plugin.npmDownloads}
              githubStars={plugin.githubStars}
              nxVersion={plugin.nxVersion}
              url={plugin.url}
            />
          ))}
      </div>
    </div>
  );
}
