package flashscore

import (
	"bytes"
	"fmt"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"gordenko.dev/dima/flashscore/model"
	webspider "gordenko.dev/dima/spider"
)

// ListOfScheduledMatches - загружает списки матчей на сегодня и завтра
func (s *Flashscore) ListOfScheduledMatches(sportID int) (result []ScheduledMatch, err error) {

	spider, err := webspider.NewSpider(nil)
	if err != nil {
		panic(err)
	}

	var (
		referer string
		urls    []string
	)

	switch sportID {
	case Football:
		urls = []string{
			footballAllGamesTodayURL,
			footballAllGamesTomorrowURL,
		}
		referer = "https://www.flashscore.com/football/"

	case Handball:
		urls = []string{
			handballAllGamesTodayURL,
			handballAllGamesTomorrowURL,
		}
		referer = "https://www.flashscore.com/handball/"

	case Tennis:
		urls = []string{
			tennisAllGamesTodayURL,
			tennisAllGamesTomorrowURL,
		}
		referer = "https://www.flashscore.com/tennis/"

	}

	// Загружаем AJAX страницы со списками матчей

	for _, allGamesURL := range urls {
		var (
			buf  []byte
			list []ScheduledMatch
		)

		buf, err = spider.Get(allGamesURL, map[string]string{
			"referer":          "https://d.flashscore.com/x/feed/proxy-local",
			"x-requested-with": "XMLHttpRequest",
			// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
			// сервер отдает 401 ошибку
			"x-fsign":   feedSign,
			"x-referer": referer,
			"x-geoip":   "1",
		})
		if err != nil {
			err = fmt.Errorf("Load all Games feed: %s", err)
			return
		}

		//fmt.Printf("%s\n\n", buf)

		list, err = parseFlashscoreEncodedAllGames(string(buf), sportID)
		if err != nil {
			err = fmt.Errorf("parseListOfScheduledMatches: %s", err)
			return
		}

		result = append(result, list...)
	}
	return
}

func (s *Flashscore) SyncUpcomingMatchReport(report *UpcomingMatchReport) (err error) {
	spider, err := webspider.NewSpider(nil)
	if err != nil {
		panic(err)
	}

	// Проверяем зону
	storedZone, err := s.model.GetTeamZone(report.Zone.ZoneID)
	if err != nil {
		return
	}

	if storedZone == nil {
		err = s.model.AddTeamZone(report.Zone)
		if err != nil {
			return
		}
	}

	// Проверяем чемпионат
	storedChamp, err := s.model.GetTeamChamp(report.Champ.ChampID)
	if err != nil {
		return
	}

	if storedChamp == nil {
		// Загружаем страницу чемпионата
		var champ model.TeamChamp

		champ, err = s.loadTeamChamp(report.Champ.ChampID)
		if err != nil {
			return
		}

		err = s.model.AddTeamChamp(champ)
		if err != nil {
			return
		}
		report.Champ = champ
	} else {
		report.Champ = *storedChamp
	}

	if !report.IsTeamsLoaded {
		var storedHome, storedAway *model.Team

		report.Home, report.Away, err = loadHomeAndAwayTeams(spider, report.MatchID, report.SportID)
		if err != nil {
			err = fmt.Errorf("loadHomeAndAwayTeams: %s", err)
			return
		}

		// Не забываем добавить зону!
		report.Home.ZoneID = report.Zone.ZoneID
		report.Away.ZoneID = report.Zone.ZoneID

		storedHome, err = s.model.GetTeam(report.Home.TeamID)
		if err != nil {
			return
		}

		if storedHome == nil {
			err = s.model.AddTeam(report.Home)
			if err != nil {
				return
			}
		}

		storedAway, err = s.model.GetTeam(report.Away.TeamID)
		if err != nil {
			return
		}

		if storedAway == nil {
			err = s.model.AddTeam(report.Away)
			if err != nil {
				return
			}
		}

		// Команды успешно загружены
		report.IsTeamsLoaded = true
	}

	if !report.IsHistoryLoaded {
		// ВАЖНО!
		// На странице AllGames можно получить названия команд, но там нет TeamID!
		// Находим на странице названия и TeamID.

		report.HomeTeamMatches, report.AwayTeamMatches, report.H2H, err = loadH2H(
			spider, report.MatchID, report.SportID,
			report.Home.CanonicalName, report.Away.CanonicalName)

		if err != nil {
			err = fmt.Errorf("Load H2H: %s", err)
			return
		}

		// STATS

		switch report.SportID {
		case Handball, Football:
			report.HomeTeamMatches = s.addScoreStatToTeamMatches(report.HomeTeamMatches, report.SportID)
			if err != nil {
				return
			}
			report.AwayTeamMatches = s.addScoreStatToTeamMatches(report.AwayTeamMatches, report.SportID)
			if err != nil {
				return
			}

			report.H2H = s.addScoreStatToTeamMatches(report.H2H, report.SportID)
			if err != nil {
				return
			}

			//buf, _ = json.MarshalIndent(report.HomeTeamMatches, "", "    ")
			//fmt.Printf("HOME:\n\n%s\n\n", buf)

			//buf, _ = json.MarshalIndent(report.AwayTeamMatches, "", "    ")
			//fmt.Printf("AWAY:\n\n%s\n\n", buf)

			//buf, _ = json.MarshalIndent(report.H2H, "", "    ")
			//fmt.Printf("H2H:\n\n%s\n\n", buf)

		case Tennis:
			// passs
		}

		report.IsHistoryLoaded = true
	}

	// ODDS (Загружаем всегда, чтобы иметь свежие данные! )
	var (
		someErr  error
		funcName string
	)

	switch report.SportID {
	case Football:
		funcName = "loadFootballOdds"
		report.Odds, someErr = loadFootballOdds(spider, report.MatchID)

	case Handball:
		funcName = "loadHandballOdds"
		report.Odds, someErr = loadHandballOdds(spider, report.MatchID)

	case Tennis:
		funcName = "loadTennisOdds"
		report.Odds, someErr = loadHandballOdds(spider, report.MatchID)

	default:
		panic(fmt.Sprintf("Bug: odds loader for the sportID %d is undefined", report.SportID))
	}

	// Если не загрузились odds - это не страшно. Ошибку возвращать нельзя,
	// ибо IsCompleted станет false
	if someErr != nil {
		s.logger.Printf("%s: %s", funcName, someErr)
		return
	}
	return
}

/*
func (s *Flashscore) parseMatchInfo(doc *goquery.Document, sportID int) (zone model.TeamZone, champ model.TeamChamp, home, away model.Team, err error) {
	// Home team
	home, err = parseTeam(doc.Find(".tname-home"))
	if err != nil {
		err = fmt.Errorf("Parse home team: %s", err)
		return
	}
	// Away team
	away, err = parseTeam(doc.Find(".tname-away"))
	if err != nil {
		err = fmt.Errorf("Parse away team: %s", err)
		return
	}

	///////////////////////////

	// Проверяем чемпионат
	storedChamp, err := s.model.GetTeamChamp(champ.ChampID)
	if err != nil {
		return
	}

	if storedChamp == nil {
		// Проверяем зону
		var storedZone *model.TeamZone

		storedZone, err = s.model.GetTeamZone(zone.ZoneID)
		if err != nil {
			return
		}

		if storedZone == nil {
			err = s.model.AddTeamZone(zone)
			if err != nil {
				return
			}
		}

		err = s.model.AddTeamChamp(champ)
		if err != nil {
			return
		}
	} else {
		// Сайт меняет названия чемпионатов по ходу чемпионата. Например:
		// Лига Европы 1/128
		// Лига Европы. Плэй-офф 1/4
		//
		// Поэтому названия нужно обновлять
		champ.Name = strings.TrimSpace(champ.Name)

		if storedChamp.Name != champ.Name {
			err = s.model.UpdateTeamChampName(champ)
			if err != nil {
				return
			}
		}
	}

	storedHome, err := s.model.GetTeam(home.TeamID)
	if err != nil {
		return
	}

	if storedHome == nil {
		home.ZoneID, err = s.loadTeamZoneID(home.TeamID, sportID)
		if err != nil {
			err = fmt.Errorf("loadTeamZoneID: %s; teamID=%s", err, home.TeamID)
			return
		}

		err = s.model.AddTeam(home)
		if err != nil {
			return
		}
	} else {
		home.ZoneID = storedHome.ZoneID
	}

	storedAway, err := s.model.GetTeam(away.TeamID)
	if err != nil {
		return
	}

	if storedAway == nil {
		away.ZoneID, err = s.loadTeamZoneID(away.TeamID, sportID)
		if err != nil {
			err = fmt.Errorf("loadTeamZoneID: %s; teamID=%s", err, away.TeamID)
			return
		}

		err = s.model.AddTeam(away)
		if err != nil {
			return
		}
	} else {
		away.ZoneID = storedAway.ZoneID
	}

	return
}
*/

func (s *Flashscore) loadTeamZoneID(teamID string, sportID int) (_ string, err error) {
	teamURL := fmt.Sprintf(teamURLPattern, teamID)

	var buf []byte
	buf, err = s.spider.Get(teamURL, nil)
	if err != nil {
		err = fmt.Errorf("Load footballTeamURL: %s; url=%s", err, teamURL)
		return
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		err = fmt.Errorf("NewDocumentFromReader: %s", err)
		return
	}

	zone, err := parseZoneOnTeamPage(doc, sportID)
	if err != nil {
		err = fmt.Errorf("parseZoneIDOnTeamPage: %s; teamID=%s", err, teamID)
		return
	}

	storedZone, err := s.model.GetTeamZone(zone.ZoneID)
	if err != nil {
		return
	}

	if storedZone == nil {
		err = s.model.AddTeamZone(zone)
		if err != nil {
			return
		}
	}

	return zone.ZoneID, nil
}

func (s *Flashscore) addScoreStatToTeamMatches(before []model.TeamMatch, sportID int) (after []model.TeamMatch) {
	for idx, result := range before {
		// Сокращаем количество матчей для анализа
		if idx >= 10 {
			return
		}

		var (
			loadErr error
			// Для логов
			loadFuncName         string
			homeGoals, awayGoals int
			// Чтобы узнать что какие-то данные были загружены и можно сохранить
			// результат в БД
			isFlagsChanged bool
		)

		storedMatch, err := s.model.GetTeamMatch(result.MatchID)
		if err != nil {
			s.logger.Printf("GetTeamMatch: %s\n", err)
		} else {
			if storedMatch != nil {
				result = *storedMatch
			}
		}

		// СУПЕР ВАЖНО!
		// Если счет в матче отсканирован заранее, например в одной из таблиц H2H,
		// важно выставить флаг IsMatchScoreLoaded = true. Иначе, например, для
		// футбола в следующем блоке кода счет будет перезаписан пустыми значениями.
		if !result.IsMatchScoreLoaded {
			// Основной счет
			switch sportID {
			case Football:
				//loadFuncName = "loadFootballTeamScoreByPeriods"
				//stat, loadErr = loadFootballTeamScoreByPeriods(s.spider, result.MatchID)

			case Handball:
				loadFuncName = "loadHandballMatchScore"
				homeGoals, awayGoals, loadErr = loadHandballMatchScore(s.spider, result.MatchID)

			default:
				panic(fmt.Sprintf("Bug: missing data loader for the sportID %d is undefined", sportID))
			}

			if loadErr != nil {
				// Только логгируем
				s.logger.Printf("%s: %s; matchID=%s\n", loadFuncName, loadErr, result.MatchID)

				// Переходим к следующему матчу
				continue
			}

			result.HomeGoals = homeGoals
			result.AwayGoals = awayGoals

			// Флаг, который покажет, что матч нужно пересканировать, если счет не загружен
			result.IsMatchScoreLoaded = true
			isFlagsChanged = true
		}

		if !result.IsScoreByPeriodsLoaded {
			var (
				stat              teamScoreByPeriods
				hasScoreByPeriods bool
			)

			// Отдельные функции потому что, статистика по футболу возвращается
			// на HTML странице, по гандболу - в "фирменном" Flashscore формате.
			// И те и другие данные нужно загружать по разным адресам.

			// Счет по периодам/таймам
			switch sportID {
			case Football:
				loadFuncName = "loadFootballTeamScoreByPeriods"
				stat, hasScoreByPeriods, loadErr = loadFootballTeamScoreByPeriods(s.spider, result.MatchID)

			case Handball:
				loadFuncName = "loadHandballTeamScoreByPeriods"
				stat, hasScoreByPeriods, loadErr = loadHandballTeamScoreByPeriods(s.spider, result.MatchID)

			default:
				panic(fmt.Sprintf("Bug: missing data loader for the sportID %d is undefined", sportID))
			}

			if loadErr != nil {
				// Только логгируем
				s.logger.Printf("%s: %s; matchID=%s\n", loadFuncName, loadErr, result.MatchID)
				// Переходим к следующему матчу
				continue
			}

			if hasScoreByPeriods {
				result.P1HomeGoals = stat.P1HomeGoals
				result.P1AwayGoals = stat.P1AwayGoals
				result.P2HomeGoals = stat.P2HomeGoals
				result.P2AwayGoals = stat.P2AwayGoals

				result.HasScoreByPeriods = true
			}

			// Частый вариант в гандболе - когда статистика по периодам успешно
			// загружена, но данных нет. Поэтому ставим отдельно 2 флага:
			// - нужно ли загружать снова
			// - статистика по периодам есть/нет
			result.IsScoreByPeriodsLoaded = true
			isFlagsChanged = true
		}

		if isFlagsChanged && result.IsMatchScoreLoaded && result.IsScoreByPeriodsLoaded {
			// На ошибку не проверяем ибо это просто кэш
			s.model.AddTeamMatch(result)
		}

		after = append(after, result)

		// Засыпаем на 0.5 сек.
		time.Sleep(500 * time.Millisecond)
	}

	return
}

func loadFootballTeamScoreByPeriods(spider *webspider.Spider, matchID string) (_ teamScoreByPeriods, hasScoreByPeriods bool, err error) {
	// Загружаем AJAX страницу со статистикой
	url := fmt.Sprintf(footballMatchSummaryURLPattern, matchID)

	var (
		buf []byte
	)

	buf, err = spider.Get(url, map[string]string{
		"x-requested-with": "XMLHttpRequest",
		"x-fsign":          feedSign,
		"x-geoip":          "1",
		"x-referer":        fmt.Sprintf(h2hLandingURLPattern, matchID),
	})
	if err != nil {
		return
	}

	// Страничка имеет вид
	//
	// <div class="detailMS"><div class="detailMS__incidentsHeader stage-12"><div class="detailMS__headerText">1st Half</div><div class="detailMS__headerScore"><span
	// class="p1_home"
	// title=""
	// >3
	// </span> -
	// <span
	// class="p1_away"
	// title=""
	// >0
	// </span></div></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">1'</div><div title="Heung-Min Son (Tottenham) with a wonderful&lt;br /&gt;piece of finishing. He makes a yard for&lt;br /&gt;himself after a well-taken pass inside the&lt;br /&gt;box and nets into the bottom right corner." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball">&nbsp;</span></div><span class="participant-name"><a href="#" onclick="window.open('/player/son-heung-min/Mgg9oPeM/'); return false;">Son Heung-Min</a></span><span class="assist note-name">(<a href="#" onclick="window.open('/player/kane-harry/v5HSlEAa/'); return false;">Kane H.</a>)</span></div><div class="detailMS__incidentRow incidentRow--home even"><div class="time-box">8'</div><div title="Harry Kane (Tottenham) breaks through before&lt;br /&gt;collecting a brilliant pass and producing&lt;br /&gt;a precise strike to beat the goalkeeper.&lt;br /&gt;The ball went into the bottom left corner." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball">&nbsp;</span></div><span class="participant-name"><a href="#" onclick="window.open('/player/kane-harry/v5HSlEAa/'); return false;">Kane H.</a></span><span class="assist note-name">(<a href="#" onclick="window.open('/player/son-heung-min/Mgg9oPeM/'); return false;">Son Heung-Min</a>)</span></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">16'</div><div title="A perfect lofted pass from Sergio Reguilon&lt;br /&gt;finds Harry Kane (Tottenham), who jumps&lt;br /&gt;highest and steers a close-range header&lt;br /&gt;into the left side of the goal. His effort&lt;br /&gt;was both strong and precise, and Lukasz&lt;br /&gt;Fabianski had no chance of stopping that.&lt;br /&gt;3:0." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball">&nbsp;</span></div><span class="participant-name"><a href="#" onclick="window.open('/player/kane-harry/v5HSlEAa/'); return false;">Kane H.</a></span><span class="assist note-name">(<a href="#" onclick="window.open('/player/reguilon-sergio/l0lnJteM/'); return false;">Reguilon S.</a>)</span></div><div class="detailMS__incidentRow incidentRow--away even"><div class="time-box">20'</div><div title="This yellow card was deserved. The tackle&lt;br /&gt;by Michail Antonio (West Ham) was quite&lt;br /&gt;harsh and Paul Tierney didn't hesitate to&lt;br /&gt;show him a yellow card." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box y-card"><span class="icon y-card">&nbsp;</span></div><span class="subincident-penalty subincident-name">(Tripping)</span><span class="participant-name"><a href="#" onclick="window.open('/player/antonio-michail/p8tt85Xn/'); return false;">Antonio M.</a></span></div><div class="detailMS__incidentsHeader stage-13"><div class="detailMS__headerText">2nd Half</div><div class="detailMS__headerScore"><span
	// class="p2_home"
	// title=""
	// >0
	// </span> -
	// <span
	// class="p2_away"
	// title=""
	// >3
	// </span></div></div><div class="detailMS__incidentRow incidentRow--away odd"><div class="time-box">72'</div><div title="Angelo Ogbonna (West Ham) is booked after&lt;br /&gt;bringing down an opponent. Paul Tierney&lt;br /&gt;had an easy-decision to make." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box y-card"><span class="icon y-card">&nbsp;</span></div><span class="subincident-penalty subincident-name">(Roughing)</span><span class="participant-name"><a href="#" onclick="window.open('/player/ogbonna-angelo/xtEnn3rQ/'); return false;">Ogbonna A.</a></span></div><div class="detailMS__incidentRow incidentRow--home even"><div class="time-box">72'</div><div title="The referee stops play so that a substitution&lt;br /&gt;can be made and Gareth Bale (Tottenham)&lt;br /&gt;replaces Steven Bergwijn." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in">&nbsp;</span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/bale-gareth/4SPjLLiR/'); return false;">Bale G.</a></span><span class="substitution-out-name"><span class="icon substitution-out">&nbsp;</span><a href="#" onclick="window.open('/player/bergwijn-steven/Kdj2xxJR/'); return false;">Bergwijn S.</a></span></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">73'</div><div title="Jose Mourinho has decided to make a change.&lt;br /&gt;Harry Winks (Tottenham) replaces Tanguy&lt;br /&gt;Ndombele." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in">&nbsp;</span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/winks-harry/MRebAusr/'); return false;">Winks H.</a></span><span class="substitution-out-name"><span class="icon substitution-out">&nbsp;</span><a href="#" onclick="window.open('/player/ndombele-tanguy/GEIWE1pD/'); return false;">Ndombele T.</a></span></div><div class="detailMS__incidentRow incidentRow--away even"><div class="time-box">77'</div><div title="The foul by Tomas Soucek (West Ham) is worthy&lt;br /&gt;of a card and a yellow is duly shown by&lt;br /&gt;Paul Tierney." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box y-card"><span class="icon y-card">&nbsp;</span></div><span class="subincident-penalty subincident-name">(Roughing)</span><span class="participant-name"><a href="#" onclick="window.open('/player/soucek-tomas/8zWzYFRF/'); return false;">Soucek T.</a></span></div><div class="detailMS__incidentRow incidentRow--away odd"><div class="time-box">77'</div><div title="Substitution. Andriy Yarmolenko (West Ham)&lt;br /&gt;has come on for Michail Antonio." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in">&nbsp;</span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/yarmolenko-andriy/O00J9ZLN/'); return false;">Yarmolenko A.</a></span><span class="substitution-out-name"><a href="#" onclick="window.open('/player/antonio-michail/p8tt85Xn/'); return false;">Antonio M.</a><span class="icon substitution-out">&nbsp;</span></span></div><div class="detailMS__incidentRow incidentRow--away even"><div class="time-box">77'</div><div title="Here is a change. Pablo Fornals is going&lt;br /&gt;off and David William Moyes gives the last&lt;br /&gt;tactical orders to Manuel Lanzini (West&lt;br /&gt;Ham)." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in">&nbsp;</span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/lanzini-manuel/QT3lIj4N/'); return false;">Lanzini M.</a></span><span class="substitution-out-name"><a href="#" onclick="window.open('/player/fornals-pablo/CfghB0gf/'); return false;">Fornals P.</a><span class="icon substitution-out">&nbsp;</span></span></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">80'</div><div title="The referee stops play so that a substitution&lt;br /&gt;can be made and Lucas (Tottenham) comes&lt;br /&gt;onto the pitch for Heung-Min Son." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in">&nbsp;</span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/lucas/EcmXnnoo/'); return false;">Lucas</a></span><span class="substitution-out-name"><span class="icon substitution-out">&nbsp;</span><a href="#" onclick="window.open('/player/son-heung-min/Mgg9oPeM/'); return false;">Son Heung-Min</a></span></div><div class="detailMS__incidentRow incidentRow--away even"><div class="time-box">82'</div><div title="Perfectly taken free kick! Aaron Cresswell&lt;br /&gt;sends a cross in and Fabian Balbuena (West&lt;br /&gt;Ham) leaps high to connect with it on the&lt;br /&gt;edge of the 6-yard box. His close-range&lt;br /&gt;header goes inside the right post and he&lt;br /&gt;makes it 3:1." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball">&nbsp;</span></div><span class="assist note-name">(<a href="#" onclick="window.open('/player/cresswell-aaron/fcMqoMcH/'); return false;">Cresswell A.</a>)</span><span class="participant-name"><a href="#" onclick="window.open('/player/balbuena-fabian/ELMR4PLj/'); return false;">Balbuena F.</a></span></div><div class="detailMS__incidentRow incidentRow--away odd"><div class="time-box">85'</div><div title="It's an own goal! An unlucky moment for&lt;br /&gt;Davinson Sanchez (Tottenham), who tries&lt;br /&gt;to clear a cross into the six-yard box but&lt;br /&gt;heads the ball behind his own keeper. The&lt;br /&gt;score is 3:2." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box soccer-ball-own"><span class="icon soccer-ball-own">&nbsp;</span></div><span class=" note-name">(Own goal)</span><span class="participant-name"><a href="#" onclick="window.open('/player/sanchez-davinson/dbPhv0VQ/'); return false;">Sanchez D.</a></span></div><div class="detailMS__incidentRow incidentRow--away even"><div class="time-box">89'</div><div title="Paul Tierney shows the yellow card to Arthur&lt;br /&gt;Masuaku (West Ham) for a heavy challenge." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box y-card"><span class="icon y-card">&nbsp;</span></div><span class="subincident-penalty subincident-name">(Holding)</span><span class="participant-name"><a href="#" onclick="window.open('/player/masuaku-arthur/4YoV0hmC/'); return false;">Masuaku A.</a></span></div><div class="detailMS__incidentRow incidentRow--away odd"><div class="time-box">90'</div><div title="The referee signals a substitution. Robert&lt;br /&gt;Snodgrass (West Ham) is brought on as a&lt;br /&gt;substitute for Arthur Masuaku." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in">&nbsp;</span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/snodgrass-robert/6XPsH2mg/'); return false;">Snodgrass R.</a></span><span class="substitution-out-name"><a href="#" onclick="window.open('/player/masuaku-arthur/4YoV0hmC/'); return false;">Masuaku A.</a><span class="icon substitution-out">&nbsp;</span></span></div><div class="detailMS__incidentRow incidentRow--away even"><div class="time-box-wide">90+4'</div><div title="Manuel Lanzini (West Ham) finishes with&lt;br /&gt;a fabulous long-range hammer that bounces&lt;br /&gt;off the post and into the top right corner.&lt;br /&gt;What a wonderful goal. The keeper was completely&lt;br /&gt;helpless." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball">&nbsp;</span></div><span class="participant-name"><a href="#" onclick="window.open('/player/lanzini-manuel/QT3lIj4N/'); return false;">Lanzini M.</a></span></div><div class="detailMS__incidentRow incidentRow--away odd"><div class="time-box-wide">90+5'</div><div class="icon-box y-card"><span class="icon y-card">&nbsp;</span></div><span class="subincident-penalty subincident-name">(Unsportsmanlike conduct)</span><span class="participant-name"><a href="#" onclick="window.open('/player/lanzini-manuel/QT3lIj4N/'); return false;">Lanzini M.</a></span></div></div><div class="match-information-wrapper"><div class="parts match-information"><div><div class="stage-header"><div class="h-part">Match Information</div></div><div class="match-information-data"><div class="content">Referee: Tierney P. (Eng),&nbsp;</div><div class="content">Venue: Tottenham Hotspur Stadium (London)</div></div></div></div></div><div id="secret_hash"></div>

	//fmt.Printf("SCORE BY PERIODS:\n%s\n\n", buf)

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
		return
	}

	return parseFootballTeamScoreByPeriods(doc)
}

// Football

func loadFootballOdds(spider *webspider.Spider, matchID string) (odds Odds, err error) {
	url := fmt.Sprintf(footballOddsURLPattern, matchID)

	buf, err := spider.Get(url, map[string]string{
		"x-requested-with": "XMLHttpRequest",
		"x-fsign":          feedSign,
		"x-geoip":          "1",
		"referer":          fmt.Sprintf(h2hLandingURLPattern, matchID),
	})
	if err != nil {
		err = fmt.Errorf("Load football odds: %s; url=%s", err, url)
		return
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
		return
	}

	odds.FullTimeTotal, err = parseOddsTotal(doc)
	if err != nil {
		err = fmt.Errorf("parseOddsTotal: %s", err)
		return
	}

	// odds 1x2 full time
	divOdds1x2FT := doc.Find("#block-1x2-ft")
	if divOdds1x2FT.Length() == 0 {
		err = fmt.Errorf(`Div #block-1x2-ft not found`)
		return
	}

	tableOdds1x2FT := divOdds1x2FT.First().Find("#odds_1x2")
	if tableOdds1x2FT.Length() == 0 {
		err = fmt.Errorf(`<table id="odds_1x2"> not found`)
		return
	}

	odds.Winner, err = parseOdds1x2(tableOdds1x2FT)
	if err != nil {
		err = fmt.Errorf("parseOdds1x2: %s", err)
		return
	}

	// /fmt.Printf("%# v\n", pretty.Formatter(offers))
	return
}

// Handball
func loadHandballOdds(spider *webspider.Spider, matchID string) (odds Odds, err error) {
	url := fmt.Sprintf(handballOddsURLPattern, matchID)

	buf, err := spider.Get(url, map[string]string{
		"x-requested-with": "XMLHttpRequest",
		"x-fsign":          feedSign,
		"x-geoip":          "1",
		"referer":          fmt.Sprintf(h2hLandingURLPattern, matchID),
	})
	if err != nil {
		err = fmt.Errorf("Load handball odds: %s; url=%s", err, url)
		return
	}

	odds, err = parseFlashscoreEncodedOdds(string(buf))
	if err != nil {
		return
	}
	return
}

func loadHandballMatchScore(spider *webspider.Spider, matchID string) (homeGoals, awayGoals int, err error) {
	url := fmt.Sprintf(handballMatchScoreURLPattern, matchID)

	buf, err := spider.Get(url, map[string]string{
		"x-requested-with": "XMLHttpRequest",
		"x-fsign":          feedSign,
		"x-geoip":          "1",
		"x-referer":        fmt.Sprintf(h2hLandingURLPattern, matchID),
	})
	if err != nil {
		return
	}

	return parseHandballMatchScore(string(buf))
}

func loadHandballTeamScoreByPeriods(spider *webspider.Spider, matchID string) (_ teamScoreByPeriods, hasScoreByPeriods bool, err error) {
	// Загружаем AJAX страницу со статистикой
	url := fmt.Sprintf(handballScoreByPeriodsURLPattern, matchID)

	buf, err := spider.Get(url, map[string]string{
		"x-requested-with": "XMLHttpRequest",
		"x-fsign":          feedSign,
		"x-geoip":          "1",
		"x-referer":        fmt.Sprintf(h2hLandingURLPattern, matchID),
	})
	if err != nil {
		return
	}

	return parseHandballTeamScoreByPeriods(string(buf))
}

func loadFootballH2H(spider *webspider.Spider, matchID string) (homeTeamMatches, awayTeamMatches, h2hMatches []model.TeamMatch, err error) {
	// Загружаем AJAX страницу с таблицами матчей
	url := fmt.Sprintf(footballH2HTablesURLPattern, matchID)

	buf, err := spider.Get(url, map[string]string{
		"referer":          "https://d.flashscore.com/x/feed/proxy-local",
		"x-requested-with": "XMLHttpRequest",
		// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
		// сервер отдает 401 ошибку
		"x-fsign":   feedSign,
		"x-referer": fmt.Sprintf(h2hLandingURLPattern, matchID),
		"x-geoip":   "1",
	})
	if err != nil {
		err = fmt.Errorf("load h2hTables: %s", err)
		return
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
		return
	}
	//	var (dirtyTeamID string

	// OVERALL - последние матчи команд (хозяев и гостей)
	// Внутри блока <div id="tab-h2h-overall" будут 2 таблицы:
	//     <table class="head_to_head h2h_home"
	//     <table class="head_to_head h2h_away"
	//
	// Строки (tr) внутри таблицы имеют вид:
	//      <tr class="odd highlight"
	//      <tr class="even highlight"

	overall := doc.Find("#tab-h2h-overall")
	if overall.Length() == 0 {
		err = fmt.Errorf("Div #tab-h2h-overall not found")
		return
	}

	overall = overall.First()

	// Последние матчи хозяев (во всех турнирах, на выезде и в гостях)
	home := overall.Find(".h2h_home")
	if home.Length() == 0 {
		err = fmt.Errorf("Table .h2h_home not found")
		return
	}

	homeTeamMatches, err = parseMatchesResults(home.First().Find(".highlight"))
	if err != nil {
		return
	}

	// Последние матчи AWAY команды  (во всех турнирах, на выезде и в гостях)
	away := overall.Find(".h2h_away")
	if away.Length() == 0 {
		err = fmt.Errorf("Table .h2h_away not found")
		return
	}

	awayTeamMatches, err = parseMatchesResults(away.First().Find(".highlight"))
	if err != nil {
		return
	}

	// Последние личные встречи (во всех турнирах, на выезде и в гостях)
	h2h := overall.Find(".h2h_mutual")
	if home.Length() == 0 {
		err = fmt.Errorf("Table .h2h_mutual not found")
		return
	}

	h2hMatches, err = parseMatchesResults(h2h.First().Find(".highlight"))
	if err != nil {
		return
	}
	return
}

func loadHandballH2H(spider *webspider.Spider, matchID, homeTeamName, awayTeamName string) (homeTeamMatches, awayTeamMatches, h2hMatches []model.TeamMatch, err error) {
	// Загружаем AJAX страницу с историей, закодированную в формате Flashscore
	url := fmt.Sprintf(handballH2HTablesURLPattern, matchID)

	buf, err := spider.Get(url, map[string]string{
		"referer":          "https://d.flashscore.com/x/feed/proxy-local",
		"x-requested-with": "XMLHttpRequest",
		// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
		// сервер отдает 401 ошибку
		"x-fsign":   feedSign,
		"x-referer": fmt.Sprintf(h2hLandingURLPattern, matchID),
		"x-geoip":   "1",
	})
	if err != nil {
		err = fmt.Errorf("load h2hTables: %s", err)
		return
	}

	//fmt.Printf("%s\n\n", buf)

	homeTeamMatches, awayTeamMatches, h2hMatches, err = parseFlashscoreEncodedH2H(string(buf), homeTeamName, awayTeamName)
	return
}

func (s *Flashscore) loadTeamChamp(champID string) (champ model.TeamChamp, err error) {
	// Вычисляем zoneID из champID
	parts := strings.Split(champID, "/")
	if len(parts) < 3 {
		err = fmt.Errorf("Wrong champID %q", champID)
		return
	}
	zoneID := strings.Join(parts[:3], "/")

	// Загружаем AJAX страницу с историей, закодированную в формате Flashscore
	url := fmt.Sprintf(teamChampURLPattern, champID)

	buf, err := s.spider.Get(url, map[string]string{
		//"referer":          "https://d.flashscore.com/x/feed/proxy-local",
		//"x-requested-with": "XMLHttpRequest",
		// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
		// сервер отдает 401 ошибку
		//"x-fsign":   feedSign,
		//"x-referer": fmt.Sprintf(h2hLandingURLPattern, matchID),
		//"x-geoip":   "1",
	})
	if err != nil {
		err = fmt.Errorf("Load team champ page: %s", err)
		return
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		return
	}

	// First - на всякий случай (если будет более 1 элемента на странице)
	f := doc.Find("div.teamHeader__name").First()

	champName := f.Text()
	champName = strings.TrimSpace(champName)

	return model.TeamChamp{
		ChampID: champID,
		Name:    champName,
		ZoneID:  zoneID,
	}, nil
}

/*
func (s *Flashscore) ListFootballChampNextRoundFixtures(champID string) (_ FootballRound, err error) {
	url := fmt.Sprintf(fixturesURLPattern, champID)

	buf, err := s.spider.Get(url, nil)
	if err != nil {
		return
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		return
	}

	divEncodedFixtures := doc.Find("#tournament-page-data-fixtures")

	if divEncodedFixtures.Length() == 0 {
		err = fmt.Errorf("tag #tournament-page-data-fixtures not found")
		return
	}

	node := divEncodedFixtures.Get(0).FirstChild

	if node == nil {
		// ВАЖНО!
		// Это не ошибка - div пустой - матчей нет!
		//err = fmt.Errorf("First child of #tournament-page-data-fixtures tag is nil")
		return
	}

	if node.Type != xhtml.TextNode {
		err = fmt.Errorf("First child of #tournament-page-data-fixtures tag is not TextNode, but %s",
			nodeTypes[node.Type])
		return
	}

	round, err := parseNextRoundFixtures(html.UnescapeString(node.Data))
	if err != nil {
		err = fmt.Errorf("parseNextRoundFixtures: %s", err)
		return
	}
	return round, nil
}
*/

/*
func (s *Flashscore) ListFootballChampFixtures(champID string) (rounds []FootballRound, err error) {
	url := fmt.Sprintf(fixturesURLPattern, champID)

	buf, err := s.spider.Get(url, nil)
	if err != nil {
		return
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
	if err != nil {
		return
	}

	divEncodedFixtures := doc.Find("#tournament-page-data-fixtures")

	if divEncodedFixtures.Length() == 0 {
		err = fmt.Errorf("tag #tournament-page-data-fixtures not found")
		return
	}

	node := divEncodedFixtures.Get(0).FirstChild

	if node.Type != xhtml.TextNode {
		err = fmt.Errorf("First child of #tournament-page-data-fixtures tag is not TextNode, but %s",
			nodeTypes[node.Type])
		return
	}

	rounds, err = parseEncodedFixtures(html.UnescapeString(node.Data))
	if err != nil {
		err = fmt.Errorf("parseEncodedFixtures: %s", err)
		return
	}
	return
}
*/

/*
func (s *Flashscore) parseMatchInfo(doc *goquery.Document, sportID int) (zone model.TeamZone, champ model.TeamChamp, home, away model.Team, err error) {
	zone, champ, home, away, err = parseTeamMatchInfo(doc, sportID)
	if err != nil {
		return
	}

	// Проверяем чемпионат
	storedChamp, err := s.model.GetTeamChamp(champ.ChampID)
	if err != nil {
		return
	}

	if storedChamp == nil {
		// Проверяем зону
		var storedZone *model.TeamZone

		storedZone, err = s.model.GetTeamZone(zone.ZoneID)
		if err != nil {
			return
		}

		if storedZone == nil {
			err = s.model.AddTeamZone(zone)
			if err != nil {
				return
			}
		}

		err = s.model.AddTeamChamp(champ)
		if err != nil {
			return
		}
	} else {
		// Сайт меняет названия чемпионатов по ходу чемпионата. Например:
		// Лига Европы 1/128
		// Лига Европы. Плэй-офф 1/4
		//
		// Поэтому названия нужно обновлять
		champ.Name = strings.TrimSpace(champ.Name)

		if storedChamp.Name != champ.Name {
			err = s.model.UpdateTeamChampName(champ)
			if err != nil {
				return
			}
		}
	}

	storedHome, err := s.model.GetTeam(home.TeamID)
	if err != nil {
		return
	}

	if storedHome == nil {
		home.ZoneID, err = s.loadTeamZoneID(home.TeamID, sportID)
		if err != nil {
			err = fmt.Errorf("loadTeamZoneID: %s; teamID=%s", err, home.TeamID)
			return
		}

		err = s.model.AddTeam(home)
		if err != nil {
			return
		}
	} else {
		home.ZoneID = storedHome.ZoneID
	}

	storedAway, err := s.model.GetTeam(away.TeamID)
	if err != nil {
		return
	}

	if storedAway == nil {
		away.ZoneID, err = s.loadTeamZoneID(away.TeamID, sportID)
		if err != nil {
			err = fmt.Errorf("loadTeamZoneID: %s; teamID=%s", err, away.TeamID)
			return
		}

		err = s.model.AddTeam(away)
		if err != nil {
			return
		}
	} else {
		away.ZoneID = storedAway.ZoneID
	}

	return
}
*/

/*

func (s *Flashscore) addSummaryToFootballMatches(before []model.TeamMatch, sportID int) (hasLostMatch bool, after []model.TeamMatch, err error) {
	for idx, result := range before {
		// Сокращаем количество матчей для анализа
		if idx >= 10 {
			return
		}

		// Проверяем в БД
		var storedMatch *model.TeamMatch

		storedMatch, err = s.model.GetTeamMatch(result.MatchID)
		if err != nil {
			return
		}

		if storedMatch != nil {
			// Матч найден в БД, заменяем неполную версию в памяти на полную версию из БД
			after = append(after, *storedMatch)
		} else {
			// Матч не найден в БД. Нужно загрузить недостающую информацию:
			// - статистика голов по таймам
			// - teamID для хозяев и гостей
			// - zoneID, champID

			// Загружаем Landing
			landingURL := fmt.Sprintf(h2hLandingURLPattern, result.MatchID)

			var (
				buf []byte
				doc *goquery.Document
			)

			buf, err = s.spider.Get(landingURL, nil)
			if err != nil {
				err = fmt.Errorf("load h2hLanding: %s", err)
				return
			}

			doc, err = goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
			if err != nil {
				err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
				return
			}

			var (
				champ      model.TeamChamp
				home, away model.Team
			)
			// Зона не нужна, ибо матч относится к чемпионату
			_, champ, home, away, err = s.parseMatchInfo(doc, Football)
			if err != nil {
				err = fmt.Errorf("parseFootballMatchInfo: %s", err)
				return
			}
			// Важно, если сохраняем результат матча в БД

			var (
				stat         teamScoreByPeriods
				loadErr      error
				loadFuncName string
			)

			// Отдельные функции потому что, статистика по футболу возвращается
			// на HTML странице, по гандболу - в "фирменном" Flashscore формате.
			// И те и другие данные нужно загружать по разным адресам.

			switch sportID {
			case Football:
				loadFuncName = "loadFootballTeamScoreByPeriods"
				stat, loadErr = loadFootballTeamScoreByPeriods(s.spider, result.MatchID)

			case Handball:
				loadFuncName = "loadHandballTeamScoreByPeriods"
				stat, loadErr = loadHandballTeamScoreByPeriods(s.spider, result.MatchID)

			default:
				panic(fmt.Sprintf("Bug: missing data loader for the sportID %d is undefined", sportID))
			}

			if loadErr != nil {
				// Только логгируем
				s.logger.Printf("%s: %s; matchID=%s\n", loadFuncName, loadErr, result.MatchID)
				// Флаг, который покажет, что матч нужно пересканировать, из-за того, что
				// не все матчи загружены
				hasLostMatch = true
			} else {

				result.P1HomeGoals = stat.P1HomeGoals
				result.P1AwayGoals = stat.P1AwayGoals
				result.P2HomeGoals = stat.P2HomeGoals
				result.P2AwayGoals = stat.P2AwayGoals

				result.HasStats = true

				err = s.model.AddTeamMatch(result)
				if err != nil {
					return
				}

				after = append(after, result)

			}
			time.Sleep(1 * time.Second)
		}
	}
	return
}
*/
