/**
 * Discover store for PodTUI
 * Manages trending/popular podcasts and category filtering.
 *
 * The featured-shows list is fetched at runtime from a JSON file hosted in the
 * GitHub repo (discover/featured.json on the `master` branch), so the list
 * can be updated without shipping a new release. The feed URL, de-duped set,
 * and version field act as the cache key — a fresh fetch only happens when the
 * version bumps or the cache window (24h) expires.
 */

import { createSignal } from "solid-js";
import type { Podcast } from "../types/podcast";
import { useFeedStore } from "./feed";

export interface DiscoverCategory {
	id: string;
	name: string;
	icon: string;
}

export const DISCOVER_CATEGORIES: DiscoverCategory[] = [
	{ id: "all", name: "All", icon: "\uF0CA" },
	{ id: "technology", name: "Technology", icon: "\uF2DB" },
	{ id: "science", name: "Science", icon: "\uF0C3" },
	{ id: "comedy", name: "Comedy", icon: "\uF118" },
	{ id: "news", name: "News", icon: "\uF1EA" },
	{ id: "business", name: "Business", icon: "\uF0B1" },
	{ id: "health", name: "Health", icon: "\uF21E" },
	{ id: "education", name: "Education", icon: "\uF19D" },
	{ id: "sports", name: "Sports", icon: "\uF1E3" },
	{ id: "true-crime", name: "True Crime", icon: "\uF00E" },
	{ id: "arts", name: "Arts", icon: "\uF1FC" },
];

// ── Remote featured-shows manifest ───────────────────────────────────────────
// The raw GitHub URL serving discover/featured.json from the master branch.
// Update this file in the repo (no release needed) to refresh the list.
const FEATURED_JSON_URL =
	"https://raw.githubusercontent.com/mikefreno/PodTui/master/discover/featured.json";

/** Cache window for the remote featured list (24 hours) */
const FEATURED_CACHE_TTL_MS = 24 * 60 * 60 * 1000;

/** Shape of a single entry in the remote JSON */
interface FeaturedEntry {
	id: string;
	title: string;
	description: string;
	feedUrl: string;
	author?: string;
	categories?: string[];
}

/** Shape of the remote JSON manifest */
interface FeaturedManifest {
	version: number;
	podcasts: FeaturedEntry[];
}

/** Convert a JSON entry to a runtime Podcast (adding derived fields) */
function entryToPodcast(entry: FeaturedEntry): Podcast {
	return {
		id: entry.id,
		title: entry.title,
		description: entry.description,
		feedUrl: entry.feedUrl,
		author: entry.author,
		categories: entry.categories ?? [],
		coverUrl: undefined,
		lastUpdated: new Date(),
		isSubscribed: false,
	};
}

/** Reconcile isSubscribed state across the discover list against the feed store */
function syncSubscriptionState(
	podcasts: Podcast[],
	subscribedUrls: Set<string>,
	subscribedIds: Set<string>,
): Podcast[] {
	return podcasts.map((p) => ({
		...p,
		isSubscribed: subscribedUrls.has(p.feedUrl) || subscribedIds.has(p.id),
	}));
}

/** Create discover store */
export function createDiscoverStore() {
	const [selectedCategory, setSelectedCategory] = createSignal<string>("all");
	const [isLoading, setIsLoading] = createSignal(false);
	const [podcasts, setPodcasts] = createSignal<Podcast[]>([]);

	// In-memory cache timestamp for the remote manifest (within 24h, skip refetch)
	let cachedAt = 0;

	/** Reconcile local isSubscribed flags with the feed store */
	const syncSubscriptions = () => {
		const feedStore = useFeedStore();
		const feeds = feedStore.feeds();
		const urls = new Set(feeds.map((f) => f.podcast.feedUrl));
		const ids = new Set(feeds.map((f) => f.podcast.id));
		setPodcasts((prev) => syncSubscriptionState(prev, urls, ids));
	};

	/** Fetch the featured-shows manifest from GitHub if stale */
	const refresh = async () => {
		setIsLoading(true);
		try {
			// Skip if cache is still fresh
			const now = Date.now();
			if (now - cachedAt < FEATURED_CACHE_TTL_MS) {
				syncSubscriptions();
				return;
			}

			const resp = await fetch(FEATURED_JSON_URL, {
				headers: { "User-Agent": "PodTUI/1.0" },
			});
			if (!resp.ok) {
				syncSubscriptions();
				return;
			}
			const manifest = (await resp.json()) as FeaturedManifest;
			if (!manifest?.podcasts?.length) {
				syncSubscriptions();
				return;
			}

			const fetched = manifest.podcasts.map(entryToPodcast);
			cachedAt = now;
			setPodcasts(fetched);

			// Reflect current feed-store subscriptions
			syncSubscriptions();
		} catch {
			// Network failure — keep whatever we have (stale or empty)
		} finally {
			setIsLoading(false);
		}
	};

	/** Get filtered podcasts by category */
	const filteredPodcasts = () => {
		const category = selectedCategory();
		if (category === "all") {
			return podcasts();
		}

		return podcasts().filter((p) => {
			const cats = p.categories?.map((c) => c.toLowerCase()) ?? [];
			return cats.some((c) =>
				c.includes(category.toLowerCase().replace("-", " ")),
			);
		});
	};

	/** Subscribe to a podcast */
	const subscribe = (podcastId: string) => {
		const podcast = podcasts().find((p) => p.id === podcastId);
		if (podcast) {
			// Actually add the feed to the feed store
			const feedStore = useFeedStore();
			feedStore.addFeed(podcast, "discover").catch(() => {});
		}
		setPodcasts((prev) =>
			prev.map((p) => (p.id === podcastId ? { ...p, isSubscribed: true } : p)),
		);
	};

	/** Unsubscribe from a podcast */
	const unsubscribe = (podcastId: string) => {
		const podcast = podcasts().find((p) => p.id === podcastId);
		if (podcast) {
			const feedStore = useFeedStore();
			feedStore.removeFeedByUrl(podcast.feedUrl);
		}
		setPodcasts((prev) =>
			prev.map((p) => (p.id === podcastId ? { ...p, isSubscribed: false } : p)),
		);
	};

	/** Toggle subscription */
	const toggleSubscription = (podcastId: string) => {
		const podcast = podcasts().find((p) => p.id === podcastId);
		if (podcast?.isSubscribed) {
			unsubscribe(podcastId);
		} else {
			subscribe(podcastId);
		}
	};

	return {
		// State
		selectedCategory,
		isLoading,
		podcasts,
		filteredPodcasts,
		categories: DISCOVER_CATEGORIES,

		// Actions
		setSelectedCategory,
		subscribe,
		unsubscribe,
		toggleSubscription,
		refresh,
	};
}

/** Singleton discover store */
let discoverStoreInstance: ReturnType<typeof createDiscoverStore> | null = null;

export function useDiscoverStore() {
	if (!discoverStoreInstance) {
		discoverStoreInstance = createDiscoverStore();
	}
	return discoverStoreInstance;
}
