/*
	GoToSocial
	Copyright (C) GoToSocial Authors admin@gotosocial.org
	SPDX-License-Identifier: AGPL-3.0-or-later

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import type { ReactNode } from "react";
import React from "react";
import { useLocation } from "wouter";
import { Error } from "./error";
import type { SerializedError } from "@reduxjs/toolkit";
import type { FetchBaseQueryError } from "@reduxjs/toolkit/query";
import type { Links } from "parse-link-header";
import Loading from "./loading";

export interface PageableListProps<T> {
	isSuccess: boolean;
	items?: T[];
	itemToEntry: (_item: T) => ReactNode;
	isLoading: boolean;
	isFetching: boolean;
	isError: boolean;
	error: FetchBaseQueryError | SerializedError | undefined;
	emptyMessage: ReactNode;
	prevNextLinks?: Links | null | undefined;
}

export function PageableList<T>({
	isLoading,
	isFetching,
	isSuccess,
	items,
	itemToEntry,
	isError,
	error,
	emptyMessage,
	prevNextLinks,
}: PageableListProps<T>) {
	const [ location, setLocation ] = useLocation();
	
	if (!(isSuccess || isError)) {
		// Hasn't been called yet.
		return null;
	}

	if (isLoading || isFetching) {
		return <Loading />;
	}

	if (error) {
		return <Error error={error} />;
	}

	// Map response to items if possible.
	let content: ReactNode;
	if (items == undefined || items.length == 0) {
		content = <b>{emptyMessage}</b>;
	} else {
		content = (
			<div className="entries">
				{items.map(item => itemToEntry(item))}
			</div>
		);
	}

	// If it's possible to page to next and previous
	// pages, instantiate button handlers for this.
	let prevClick: (() => void) | undefined;
	let nextClick: (() => void) | undefined;
	if (prevNextLinks) {
		const prev = prevNextLinks["prev"];
		if (prev) {
			const prevUrl = new URL(prev.url);
			const prevParams = prevUrl.search;
			prevClick = () => {
				setLocation(location + prevParams.toString());
			};
		}

		const next = prevNextLinks["next"];
		if (next) {
			const nextUrl = new URL(next.url);
			const nextParams = nextUrl.search;
			nextClick = () => {
				setLocation(location + nextParams.toString());
			};
		}
	}

	return (
		<div className="list pageable-list">
			{ content }
			{ prevNextLinks &&
				<div className="prev-next">
					{ prevClick && <button onClick={prevClick}>Previous page</button> }
					{ nextClick && <button onClick={nextClick}>Next page</button> }
				</div>
			}
		</div>
	);
}
