from collections.abc import Awaitable
from hashlib import sha256
from itertools import zip_longest
from logging import getLogger
from typing import Callable, Literal, cast

from ..models.videos import Guess, Video
from .anilist import get_anilist_data, identify_anilist
from .guess.guess import guessit

logger = getLogger(__name__)

pipeline: list[Callable[[str, Guess], Awaitable[Guess]]] = [
	identify_anilist,
	# TODO: add nfo scanner
	# TODO: add thexem
]


async def identify(path: str) -> Video:
	try:
		titles = list((await get_anilist_data()).titles.keys())
	except Exception as e:
		logger.error("Failed to get expected titles", exc_info=e)
		titles = []
	raw = guessit(path, expected_titles=titles)

	# guessit should only return one (according to the doc)
	title = raw.get("title", [])[0]
	kind = raw.get("type", [])[0]
	version = next(iter(raw.get("version", [])), None)
	# apparently guessit can return multiples but tbh idk what to do with
	# multiples part. we'll just ignore them for now
	part = next(iter(raw.get("part", [])), None)

	years = raw.get("year", [])
	seasons = list(set(cast(int, x.value) for x in raw.get("season", [])))
	episodes = list(set(cast(int, x.value) for x in raw.get("episode", [])))

	# just strip the version & part number from the path
	rendering_path = "".join(
		c
		for i, c in enumerate(path)
		if not (version and version.start <= i < version.end)
		and not (part and part.start <= i < part.end)
	)

	guess = Guess(
		title=cast(str, title.value),
		kind=cast(Literal["episode", "movie"], kind.value),
		extra_kind=None,
		years=list(set(cast(int, y.value) for y in years)),
		episodes=list(
			set(
				Guess.Episode(season=s, episode=e)
				for s, e in zip_longest(
					seasons,
					episodes,
					fillvalue=seasons[-1] if any(seasons) else 1,
				)
			)
		),
		external_id={},
		from_="guessit",
		raw={
			k: set(x.value if x.value is int else str(x.value) for x in v)
			for k, v in raw.items()
		},
	)

	for step in pipeline:
		try:
			guess = await step(path, guess)
		except Exception as e:
			logger.error("Couldn't run %s.", step.__name__, exc_info=e)

	return Video(
		path=path,
		rendering=sha256(rendering_path.encode()).hexdigest(),
		part=cast(int, part.value) if part else None,
		version=cast(int, version.value) if version else 1,
		guess=guess,
	)


if __name__ == "__main__":
	import asyncio
	import sys

	async def main():
		ret = await identify(sys.argv[1])
		print(ret.model_dump_json(indent=4, by_alias=True))

	asyncio.run(main())
	# use this with `uv run python3 -m scanner.identifiers.identify "path"`
