import { LoaderFunctionArgs, useLoaderData } from "react-router";
import { createPopularPostsPageURL } from "#lib/urls";
import { parseOffset } from "#lib/pagination";
import { fetchPopularPosts } from "#api/posts";
import { PageSkeleton } from "#components/pages";
import { Paginator } from "#components/pagination";
import { FooterAd, HeaderAd, SliderAd } from "#components/advs";
import { CardList, PostCard } from "#components/cards";
import {
  IPopularPostsPeriod,
  IPost,
  validatePeriod,
} from "#entities/posts";
import { KemonoLink } from "#components/links";
import { findFavouritePosts, findFavouriteProfiles } from "#entities/account";

interface IProps {
  /**
   * Datetime string.
   */
  minDate: string;
  /**
   * Datetime string.
   */
  maxDate: string;
  /**
   * Human description of range.
   */
  rangeDescription: string;
  /**
   * Date string.
   */
  earliestDateForPopular: string;
  /**
   * Value is a tuple of date strings.
   */
  navigationDates: Record<IPopularPostsPeriod, INavigationDate>;
  scale?: IPopularPostsPeriod;
  /**
   * Date string.
   */
  today: string;
  count: number;
  posts: IPost[];
  offset?: number;
  /**
   * Datetime string.
   */
  date?: string;
}

/**
 * A tuple of date strings.
 */
type INavigationDate = [prev: string, next: string, current: string];

export function PopularPostsPage() {
  const {
    minDate,
    maxDate,
    rangeDescription,
    navigationDates,
    earliestDateForPopular,
    scale,
    today,
    count,
    offset,
    date,
    posts,
  } = useLoaderData() as IProps;
  const title = `Popular posts for ${rangeDescription}`;

  return (
    <PageSkeleton
      name="popular-posts"
      title={title}
      heading={
        <>
          Popular Posts for{" "}
          <span title={`${minDate} to ${maxDate}`}>{rangeDescription}</span>
        </>
      }
    >
      <SliderAd />

      <div className="paginator" id="paginator-dates">
        <DailySelector
          earliestDateForPopular={earliestDateForPopular}
          today={today}
          scale={scale}
          navigationDate={navigationDates.day}
        />

        <WeeklySelector
          earliestDateForPopular={earliestDateForPopular}
          today={today}
          scale={scale}
          navigationDate={navigationDates.week}
        />

        <MonthlySelector
          earliestDateForPopular={earliestDateForPopular}
          today={today}
          scale={scale}
          navigationDate={navigationDates.month}
        />

        <div className="paginator" id="paginator-top">
          <Paginator
            count={count}
            offset={offset}
            constructURL={(offset) =>
              String(createPopularPostsPageURL(date, scale, offset))
            }
          />

          <HeaderAd />

          <CardList>
            {count === 0 ? (
              <div className="card-list__item--no-results">
                <h2 className="subtitle">Nobody here but us chickens!</h2>
                <p className="subtitle">There are no posts for your query.</p>
              </div>
            ) : (
              posts.map((post) => (
                <PostCard
                  key={`${post.id}-${post.service}`}
                  post={post}
                  isFavourite={post.isFavourite}
                  isFavouriteProfile={post.isFavouriteProfile}
                  showFavCount={true}
                />
              ))
            )}
          </CardList>

          <FooterAd />

          <div className="paginator" id="paginator-bottom">
            <Paginator
              count={count}
              offset={offset}
              constructURL={(offset) =>
                String(createPopularPostsPageURL(date, scale, offset))
              }
            />
          </div>
        </div>
      </div>
    </PageSkeleton>
  );
}

interface IDailySelectorProps
  extends Pick<IProps, "earliestDateForPopular" | "scale" | "today"> {
  navigationDate: INavigationDate;
}

function DailySelector({
  earliestDateForPopular,
  scale,
  today,
  navigationDate,
}: IDailySelectorProps) {
  const [prev, next, current] = navigationDate;

  return (
    <div id="daily">
      <span>
        {prev < earliestDateForPopular ? (
          <span>next »</span>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(prev, "day"))}>
            « prev
          </KemonoLink>
        )}
      </span>{" "}
      <span>
        {scale === "day" ? (
          <strong>Day</strong>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(current, "day"))}>
            Day
          </KemonoLink>
        )}
      </span>{" "}
      <span>
        {next > today ? (
          <span>next »</span>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(next, "day"))}>
            next »
          </KemonoLink>
        )}
      </span>
    </div>
  );
}

interface IWeeklySelectorProps
  extends Pick<IProps, "earliestDateForPopular" | "scale" | "today"> {
  navigationDate: [string, string, string];
}

function WeeklySelector({
  earliestDateForPopular,
  scale,
  today,
  navigationDate,
}: IWeeklySelectorProps) {
  const [prev, next, current] = navigationDate;

  return (
    <div id="weekly">
      <span>
        {prev < earliestDateForPopular ? (
          <span>next »</span>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(prev, "week"))}>
            « prev
          </KemonoLink>
        )}
      </span>{" "}
      <span>
        {scale === "week" ? (
          <strong>Week</strong>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(current, "week"))}>
            Week
          </KemonoLink>
        )}
      </span>{" "}
      <span>
        {next > today ? (
          <span>next »</span>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(next, "week"))}>
            next »
          </KemonoLink>
        )}
      </span>
    </div>
  );
}

interface IMonthlySelectorProps
  extends Pick<IProps, "earliestDateForPopular" | "scale" | "today"> {
  navigationDate: [string, string, string];
}

function MonthlySelector({
  earliestDateForPopular,
  scale,
  today,
  navigationDate,
}: IMonthlySelectorProps) {
  const [prev, next, current] = navigationDate;

  return (
    <div id="monthly">
      <span>
        {prev < earliestDateForPopular ? (
          <span>next »</span>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(prev, "month"))}>
            « prev
          </KemonoLink>
        )}
      </span>{" "}
      <span>
        {scale === "month" ? (
          <strong>Month</strong>
        ) : (
          <KemonoLink url={String(createPopularPostsPageURL(current, "month"))}>
            Month
          </KemonoLink>
        )}
      </span>{" "}
      <span>
        <span>
          {next > today ? (
            <span>next »</span>
          ) : (
            <KemonoLink url={String(createPopularPostsPageURL(next, "month"))}>
              next »
            </KemonoLink>
          )}
        </span>
      </span>
    </div>
  );
}

export async function loader({ request }: LoaderFunctionArgs): Promise<IProps> {
  const searchParams = new URL(request.url).searchParams;

  const inputDate = searchParams.get("date")?.trim();

  const scale = searchParams.get("period")?.trim() ?? "recent";
  validatePeriod(scale);

  let offset: number | undefined;
  {
    const inputOffset = searchParams.get("o")?.trim();

    if (inputOffset) {
      offset = parseOffset(inputOffset);
    }
  }

  const {
    info,
    props,
    results: posts,
  } = await fetchPopularPosts(inputDate, scale, offset);
  const { count, earliest_date_for_popular, today } = props;
  const { date, range_desc, min_date, max_date, navigation_dates } = info;
  const postsData = posts.map(({ service, user, id }) => ({
    service,
    user,
    id,
  }));
  const profilesData = posts.reduce<{ service: string; id: string }[]>(
    (profilesData, post) => {
      const match = profilesData.find(
        (profileData) =>
          profileData.id === post.user && profileData.service === post.service
      );

      if (!match) {
        profilesData.push({ service: post.service, id: post.user });
      }

      return profilesData;
    },
    []
  );
  const favPosts = await findFavouritePosts(postsData);
  const favProfiles = await findFavouriteProfiles(profilesData);
  const postsWithFavs = posts.map((post) => {
    const isFavPost = Boolean(
      favPosts.find(
        ({ service, user, id }) =>
          id === post.id && user === post.user && service === post.service
      )
    );
    const isFavProfile = Boolean(
      favProfiles.find(
        ({ service, id }) => id === post.user && service === post.service
      )
    );

    if (!isFavPost && !isFavProfile) {
      return post;
    }

    return {
      ...post,
      isFavourite: isFavPost,
      isFavouriteProfile: isFavProfile,
    };
  });

  return {
    date,
    count,
    scale,
    offset,
    posts: postsWithFavs,
    today,
    earliestDateForPopular: earliest_date_for_popular,
    rangeDescription: range_desc,
    minDate: min_date,
    maxDate: max_date,
    navigationDates: navigation_dates,
  };
}
