from flask import session
from lewy_db import baza as ldb
from lewy_globals import colors as c
from lewy_globals import safe_traverse
import json
import lewy_globals
import requests
import time
from sqlalchemy import func

class scraper:

	headers = {
		'x-fsign': 'SW9D1eZo'
	}

	db = None

	def __init__(self):
		self.db = lewy_globals.getDb()
		pass

	def pobierzDaneNajlepszegoSportowcaNaSwiecie(self) -> dict:
		response = requests.get('https://3.flashscore.ninja/3/x/feed/plm_MVC8zHZD_0', headers=headers)
		return json.loads(response.text)

	def pobierz_pojedyncza_strone(self, zewnetrzne_id_sportowca: str = "MVC8zHZD", nr_strony: int = 0) -> dict:
		if len(zewnetrzne_id_sportowca) != 8:
			raise ValueError("Zewnętrzne ID sportowca powinno być długości 8!")
		response = requests.get(f'https://3.flashscore.ninja/3/x/feed/plm_{zewnetrzne_id_sportowca}_{nr_strony}', headers=self.headers)
		return json.loads(response.text)

	def __czy_x_istnieje(self, typ, **id):
		rekord = self.db.simple_select_all(typ, **id)
		if rekord is not None and rekord != []:
			return True
		else:
			return False

	def to_iso_compatible_date(self, dmy_date: str):
		"""
		Zamienia datę z formatu DD.MM.YY na YYYY-MM-DD
		
		:param      dmy_date:  Data w formacie DD.MM.YY
		:type       dmy_date:  str
		"""
		day, month, year = dmy_date.split(".")
		return f"{2000 + int(year)}-{month}-{day}"


	def czy_mecz_istnieje(self, zewnetrzne_id_meczu: str):
		return self.__czy_x_istnieje("mecze", zewnetrzne_id_meczu=zewnetrzne_id_meczu)

	def czy_klub_istnieje(self, id_klubu: str):
		return self.__czy_x_istnieje("kluby", id_klubu=id_klubu)

	def czy_nie_trzeba_pobierac_nowych_meczy_zawodnika(self, id_zawodnika: int, zewnetrzne_id_meczu: str):
		return self.__czy_x_istnieje("sportowcy_w_meczach", id_zawodnika=id_zawodnika, zewnetrzne_id_meczu=zewnetrzne_id_meczu)

	def id_na_imie_nazwisko_urodziny(self, zewnetrzne_id_sportowca: str = "MVC8zHZD"):
		"""
		Scraper z dykty xD
		Pobiera imiona, nazwiska i dni urodzin sportowców z zewnętrznego id.
		Działa na słowo honoru.
		
		:param      zewnetrzne_id_sportowca:  Zewnętrzne id sportowca
		:type       zewnetrzne_id_sportowca:  str
		"""
		if len(zewnetrzne_id_sportowca) != 8:
			raise ValueError("Zewnętrzne ID sportowca powinno być długości 8!")
		r = requests.get(f'https://www.flashscore.pl/?r=4:{zewnetrzne_id_sportowca}')
		page = r.text

		name_start_pos = page.find("data-testid=\"wcl-scores-heading-02\">") + 36
		name_end_pos   = page.find("</", name_start_pos)
		name = page[name_start_pos:name_end_pos].strip().split(' ')

		# Tak wiem... można by było użyć beautifulsoup4, ale nie ma sensu dodawać nowych zależności dla tylko jednej metody.
		birthday_start_pos_1 = page.find("data-testid=\"wcl-scores-simpleText-01\">", name_end_pos) + 39
		birthday_start_pos_2 = page.find("data-testid=\"wcl-scores-simpleText-01\">", birthday_start_pos_1) + 39
		birthday_start_pos_3 = page.find("data-testid=\"wcl-scores-simpleText-01\">", birthday_start_pos_2) + 39
		birthday_start_pos   = page.find("data-testid=\"wcl-scores-simpleText-01\">", birthday_start_pos_3) + 39
		birthday_end_pos     = page.find("</", birthday_start_pos) - 1
		birthday = None if birthday_end_pos - birthday_start_pos > 20 else page[birthday_start_pos:birthday_end_pos].strip(" ()")

		return name, birthday

	def aktualizuj_dane_sportowca(self, zewnetrzne_id_sportowca: str = "MVC8zHZD"):
		stop_scraping = False
		matches_to_add = []

		# TODO: Sprawdź, czy sportowiec istnieje w bazie.
		# Jeśli nie, dodaj go w podobny sposób, jak
		# w sample_data_init() (w lewy_db.py).
		# -- Jednak tego tak nie robimy, ponieważ nie uzyskamy wielu informacji.

		id_zawodnika = self.db.get_id_zawodnika_by_zewnetrzne_id(zewnetrzne_id_sportowca)
		zawodnik     = self.db.simple_select_all("sportowcy", zewnetrzne_id_zawodnika=zewnetrzne_id_sportowca)[0]

		page = 0
		match_num = 0
		while not stop_scraping:

			retrieved_page = self.pobierz_pojedyncza_strone(zewnetrzne_id_sportowca=zewnetrzne_id_sportowca, nr_strony=page)
			if not safe_traverse(retrieved_page, ["hasMoreLastMatches"], default=False):
				stop_scraping = True
				break

			print(f"{c.WARNING}Pobrano nową stronę {page}{c.ENDC}:\n{retrieved_page}")

			retrieved_matches = safe_traverse(retrieved_page, ["lastMatches"], default=[])
			for match in retrieved_matches:

				match_id      = safe_traverse(match,     ["eventEncodedId"], default="non-existent-match-id")
				home_club_id  = safe_traverse(match, ["homeParticipantUrl"], default="non-existent-club-id")
				away_club_id  = safe_traverse(match, ["awayParticipantUrl"], default="non-existent-club-id")

				# ~Sprawdź, czy mecz nie znajduje się już w bazie~
				# 
				# UWAGA! Nie powinniśmy tego sprawdzać jak w komentarzu poniżej!
				# To sprawdzenie powinno jedynie służyć zapobieganiu dodania istniejących meczy,
				# natomiast istniejący mecz nie oznacza, że sportowiec ma już statystykę z niego!
				# Przerwać scrapowanie należy wtedy, gdy znajdzie się statystykę sportowca
				# z bieżącego meczu, a nie kiedy znajdzie się bieżący mecz w bazie!
				# 
				# if self.czy_mecz_istnieje(zewnetrzne_id_meczu=match_id):
				# 	stop_scraping = True
				# 	break
				# 
				# Rozwiązanie jest mocno nieefektywne przy scrapowaniu całej bazy od zera,
				# ale rozwiąże przypadki, w których zawodnicy, których śledzimy, grali przeciwko sobie.
				if self.czy_nie_trzeba_pobierac_nowych_meczy_zawodnika(id_zawodnika=id_zawodnika, zewnetrzne_id_meczu=match_id):
					stop_scraping = True
					break

				if self.czy_mecz_istnieje(zewnetrzne_id_meczu=match_id):
					# Nie scrapuj istniejących meczy.
					# Naturalnie, istniejący mecz nie musi oznaczać potrzeby zakończenia scrapowania.
					continue

				# Sprawdź, czy klub znajduje się już w bazie. Jeśli nie,
				# trzeba go dodać przed meczem.
				if not self.czy_klub_istnieje(id_klubu=home_club_id):
					print(f"{c.OKCYAN}Nowy klub{c.ENDC}: {home_club_id}")
					self.db.simple_insert_one("kluby", 
						id_klubu=home_club_id, 
						pelna_nazwa=safe_traverse(match, ["homeParticipantName"]), 
						skrocona_nazwa=safe_traverse(match, ["homeParticipant3CharName"]))
				if not self.czy_klub_istnieje(id_klubu=away_club_id):
					print(f"{c.OKCYAN}Nowy klub{c.ENDC}: {away_club_id}")
					self.db.simple_insert_one("kluby", 
						id_klubu=away_club_id, 
						pelna_nazwa=safe_traverse(match, ["awayParticipantName"]), 
						skrocona_nazwa=safe_traverse(match, ["awayParticipant3CharName"]))

				# TODO: dodaj obiekt mecz do bazy (simple_insert_one(), simple_insert_many())
				print(f"{c.OKCYAN}Nowy mecz ({match_num}){c.ENDC}: {match}")
				
				iso_converted_date = self.to_iso_compatible_date(safe_traverse(match, ["eventStartTime"], default="1970-01-01"))

				self.db.simple_insert_one("mecze", 
						zewnetrzne_id_meczu     = safe_traverse(match, ["eventEncodedId"],              default=""), 
						data                    = iso_converted_date, 
						gospodarze_id           = home_club_id, 
						gospodarze              = self.db.simple_select_all("kluby", id_klubu=home_club_id)[0], 
						goscie_id               = away_club_id, 
						goscie                  = self.db.simple_select_all("kluby", id_klubu=away_club_id)[0], 
						gosp_wynik              = safe_traverse(match, ["homeScore"],                   default=0), 
						gosc_wynik              = safe_traverse(match, ["awayScore"],                   default=0), 
						sezon                   = safe_traverse(match, ["tournamentSeason"],            default=""), 
						nazwa_turnieju          = safe_traverse(match, ["tournamentTitle"],             default=""), 
						skrocona_nazwa_turnieju = safe_traverse(match, ["tournamentTemplateShortCode"], default=""), 
						flaga                   = safe_traverse(match, ["flagId"],                      default=0), 
				)
				match_num += 1

				stats = safe_traverse(match, ["stats"], default="")
				zewnetrzne_id_meczu = safe_traverse(match, ["eventEncodedId"], default="")

				if stats != False: # gdy sportowiec był aktywny w meczu
					# print("todo :)")
					self.db.simple_insert_one("sportowcy_w_meczach", 
						id_zawodnika                   = id_zawodnika, 
						zawodnik                       = zawodnik,
						zewnetrzne_id_meczu            = zewnetrzne_id_meczu,
						# Uwaga! Czasami przygłupy z flashscore zwracają puste pole '' zamiast zera, np. do liczby strzelonych goli.
						# Dlatego int("0" + "") = int("0"), co zapobiegnie wysypaniu się przy int("").
						czas_gry                       = int("0" + safe_traverse(stats, ["595", "value"], default="0").rstrip("'?")),
						goli                           = int("0" + safe_traverse(stats, ["596", "value"], default="0")),
						asyst                          = int("0" + safe_traverse(stats, ["541", "value"], default="0")),
						interwencje_bramkarza          = 0,
						suma_interwencji_na_bramke     = 0,
						zolte_kartki                   = int("0" + safe_traverse(stats, ["599", "value"], default="0")),
						czerwone_kartki                = int("0" + safe_traverse(stats, ["600", "value"], default="0")),
						wygrana                        = {"Z": 1, "R": 0, "P": -1}.get(safe_traverse(match, ["winLoseShort"], default=""), 0),
						wynik                          = safe_traverse(match, ["rating"], default=0) or 0
					)

					# # analogicznie zinkrementuj statystyki_sportowcow:
					# # aby to zrobić, najpierw pobierz najnowszą statystykę sportowca
					# # ...
					# 
					# 
					# # a następnie użyj funkcji w lewy_db do inkrementowania danych
					# # (póki co jeszcze takiej nie ma)
					# self.db.simple_increment_data("statystyki_sportowcow",
					# 	id = ... # pewnie id się przyda 
					# 	sportowiec                     = zawodnik,
					# 	ostatni_mecz                   = self.db.get_id_meczu_by_zewnetrzne_id(zewnetrzne_id_meczu), 
					# 	ilosc_wystapien                = 1 if int(safe_traverse(stats, ["595", "value"], default="0").rstrip("'")) > 0 else 0, 
					# 	minut_gry                      =      int(safe_traverse(stats, ["595", "value"], default="0").rstrip("'")), 
					# 	gier_sum                       = 1 if int(safe_traverse(stats, ["595", "value"], default="0").rstrip("'")) > 0 else 0, 
					# 	goli_sum                       =      int(safe_traverse(stats, ["596", "value"], default="0")), 
					# 	asyst_sum                      =      int(safe_traverse(stats, ["541", "value"], default="0")), 
					# 	interwencji_sum                = 0, 
					# 	nieobronionych_interwencji_sum = 0, 
					# 	zoltych_kartek_sum             =      int(safe_traverse(stats, ["599", "value"], default="0")), 
					# 	czerwonych_kartek_sum          =      int(safe_traverse(stats, ["600", "value"], default="0")), 
					# 	wygranych_sum                  = 1 if     safe_traverse(match, ["winloseshort"], default="") == "z" else 0, 
					# 	wynik_sum                      =          safe_traverse(match, ["rating"],       default=0), 
					# 	meczow_do_wynikow_sum          = 1 if     safe_traverse(match, ["rating"],       default=0) not in (0, none) else none
					# )

				else:
					# print("też todo :)")
					# # TODO: TU TEŻ TRZEBA POPRAWIĆ ANALOGICZNIE DO TEGO, CO JEST WEWNĄTRZ IF'A
					self.db.simple_insert_one("sportowcy_w_meczach", 
						id_zawodnika                   = id_zawodnika, 
						zawodnik                       = zawodnik,
						zewnetrzne_id_meczu            = zewnetrzne_id_meczu,		
						czas_gry                       = 0, #(lambda v: int(str(v).rstrip("'")) if isinstance(v, (str, int, float)) and str(v).rstrip("'").lstrip("-").isdigit() else 0)(safe_traverse(stats if isinstance(stats, dict) else {}, ["595", "value"], default="0")),
						goli                           = int("0" + safe_traverse(stats, ["596", "value"], default="0")),
						asyst                          = int("0" + safe_traverse(stats, ["541", "value"], default="0")),
						interwencje_bramkarza          = 0,
						suma_interwencji_na_bramke     = 0,
						zolte_kartki                   = int("0" + safe_traverse(stats, ["599", "value"], default="0")),
						czerwone_kartki                = int("0" + safe_traverse(stats, ["600", "value"], default="0")),
						wygrana                        = {"Z": 1, "R": 0, "P": -1}.get(safe_traverse(match, ["winLoseShort"], default=""), 0),
						wynik                          = safe_traverse(match, ["rating"], default=0) or 0
						)

				# TODO: Zaktualizuj statystyki sportowca


				# Opcjonalnie: odczekaj kilka sekund (?)
				# Problem w tym, że time.sleep() jest blokujące,
				# a asyncio i flask nie idą ze sobą w parze.
				# Można to załatwić osobnym skryptem, ale
				# martwmy się tym dopiero, gdy dostaniemy
				# rate limita. - sherl

			page += 1
			#time.sleep(5)


	def aktualizuj_dane(self):
		"""
		Pobiera mecze dla każdego sportowca wymienionego
		w pliku konfiguracyjnym.
		"""
		start_time = time.time()

		for id_sportowca in lewy_globals.config['sportsmen']['tracked_ids']:
			self.aktualizuj_dane_sportowca(zewnetrzne_id_sportowca=id_sportowca)
			time.sleep(5)

		end_time = time.time()

		print(f"Scrapowanie trwało {end_time - start_time}s.")
