/// <reference path=".#ts/client.d.ts" />
module Betting {

	export interface BetSlipState {
		type: string;
		message?: string;
		positive?: boolean;
	}

	export interface BetSlipBet {
		id: string;
		time: Date;
		eventId: string;
		eventName: string;
		marketId: string;
		marketName: string;
		selection: string;
		selectionId: number;
		selectionName: string;
		specialBetValue: string;
		odds: number;
		state: BetSlipState
		stake: number;
		oldOdds?: number;
		changed?: boolean;
	}

	Tracker.autorun(function () {

		var bets = getBetSlip();
		bets.forEach(function (bet: BetSlipBet) {
			if (bet.state && ['closing', 'success', 'processing'].indexOf(bet.state.type) !== -1) {
				if (bet.state.type === 'processing') {
					var selection = Betting.Selection.db.findOne(bet.selection);
					if (selection.backOddsMultiplied() !== bet.odds) {
						bet.odds = selection.backOddsMultiplied();
						setBetSlip(bets);
					}
				}
				return;
			}

			// status change
			var event = Betting.Event.db.findOne(bet.eventId);
			var market = Betting.Market.db.findOne(bet.marketId);
			var selection = Betting.Selection.db.findOne(bet.selection);
			if ((!event || !market || !market.active || !selection || !selection.active)) {
				var subject: string;
				if (!event || event.status !== EventStatus.OPEN) {
					subject = 'event';
				}
				else if (!market || !market.active) {
					subject = 'market'
				}
				else if (!selection || !selection.active) {
					subject = 'selection'
				}
				var message = `This ${subject} is suspended`;
				if (!bet.state || bet.state.type !== 'suspended' || bet.state.message !== message) {
					bet.state = {
						type: 'suspended',
						message: message
					};
					setBetSlip(bets);
				}
			}
			else if (selection && selection.active && (bet.state && bet.state.type === 'suspended')) {
				bet.state = null;
				setBetSlip(bets);
			}

			// odds change
			if (!selection) {
				return;
			}
			//if (selection.backOdds !== bet.odds) {
			if (selection.backOddsMultiplied() !== bet.odds) {
				if (!bet.state || bet.state.type !== 'suspended') {
					bet.state = {
						type: 'changed',
						//positive: selection.backOdds > bet.odds
						positive: selection.backOddsMultiplied() > bet.odds
					};
				}
				bet.oldOdds = bet.odds;
				//bet.odds = selection.backOdds;
				bet.odds = selection.backOddsMultiplied();
				setBetSlip(bets);
			}
		});
	});

	export function addToRejectedBets(selections, betId) {
		var rejectedBets = Session.get('rejectedBets');
		if (!rejectedBets) {
			rejectedBets = {};
		}
		var matchedBetIndex = '';
		var matchedBet = _.find(rejectedBets, function (betSelections: any, rejectedBet: any) {
			matchedBetIndex = rejectedBet;
			var compared = compareSelections(selections, betSelections);
			return compared;
		});
		if (matchedBet) {
			delete rejectedBets[matchedBetIndex];
		}
		if (!rejectedBets[betId]) {
			rejectedBets[betId] = selections;
		}
		Session.set('rejectedBets', rejectedBets);
	}

	export function getBetSlip(): BetSlipBet[] {
		return Session.get('sportsBetSlip') || [];
	}

	export function setBetSlip(bets: BetSlipBet[]) {
		Session.set('sportsBetSlip', bets);
	}

	export function isMultibet(): boolean {
		return Session.get('sportsIsMultiBet');
	}

	export function setMultibet(value: boolean) {
		Session.set('sportsIsMultiBet', value);
	}

	export function getMultiStake(): number {
		return Session.get('sportsMultiStake') || 0;
	}

	export function setMultiStake(stake: number) {
		stake = +(stake / 1000).toFixed(5);
		Session.set('sportsMultiStake', stake || '');
	}

	export function getBetSlipState(): BetSlipState {
		return Session.get('sportsBetSlipState');
	}

	export function setBetSlipState(value: BetSlipState) {
		Session.set('sportsBetSlipState', value);
	}

	export function setBetSlipBetState(value: string) {
		var bets = getBetSlip();
		bets.forEach(function(bet){
			bet.state.type = value;
		});
		setBetSlip(bets);
	}

	export function addToBetSlip(selection: Selection) {

		var event = selection.event.fetch();
		var market = selection.market.fetch();
		var bets = getBetSlip();

		// user cant set two bets in one market when selected multiple type
		for (let betItem of bets) {
			if (isMultibet() && market._id === betItem.marketId) {
				closeBet(betItem.selection);
			}

			if (isMultibet() && event._id === betItem.eventId) {
				closeBet(betItem.selection);
			}

		}

		var bet = <BetSlipBet>{
			time: event.startTime,
			eventId: event._id,
			eventName: event.name,
			marketId: market._id,
			marketName: market.name,
			selection: selection._id,
			selectionId: selection.id,
			selectionName: selection.text(),
			specialBetValue: selection.specialBetValueText(),
			odds: selection.backOddsMultiplied(),
			state: {
				type: 'opening'
			},
			stake: null
		};

		bets.unshift(bet);
		setBetSlip(bets);
		selection.select();

		// disabling alert message on more than one bet when switched to "multiple" type from "single"
		if (isMultibet() && bets.length > 1) {
			setBetSlipState(null);
		}

		var selectionId = bet.selection;
		Meteor.setTimeout(function () {
			var bets = getBetSlip();
			var bet = _.find(bets, (bet: any) => {
				return bet.selection === selectionId;
			});
			if (!bet) {
				return;
			}
			bet.state = null;
			setBetSlip(bets);
		}, 430);
	}

	export function acceptOdds(selectionId: string) {
		var bets = getBetSlip();
		var bet = _.find(bets, (bet: any) => {
			return bet.selection === selectionId;
		});
		bet.state = null;
		setBetSlip(bets);
	}

	export function deleteBet(selectionId: string): boolean {
		var bets = getBetSlip();
		return !!_.find(bets, (bet: any) => {
			var found = bet.selection === selectionId;
			if (found) {
				bets.splice(bets.indexOf(bet), 1);
				setBetSlip(bets);
				Selection.db.unSelect(selectionId);
				//validateBets();
			}
			return found;
		});
	}

	export function getBet(selectionId: string) {
		return _.find(getBetSlip(), (bet: any) => {
			return bet.selection === selectionId;
		});
	}

	export function closeBet(selectionId: string): boolean {
		var bets = getBetSlip();
		var bet = _.find(bets, (bet: any) => {
			return bet.selection === selectionId;
		});
		if (!bet) {
			return false;
		}
		Selection.db.unSelect(selectionId);
		bet.state = {
			type: 'closing'
		};
		setBetSlip(bets);
		Meteor.setTimeout(function () {
			deleteBet(selectionId);
		}, 430);
		return true;
	}

	export function clearBetSlip() {
		setBetSlip([]);
		setBetSlipState(null);
		Selection.db.clearSelection();
	}

	export function toggleBet(selection: Selection) {
		if (!closeBet(selection._id)) {
			addToBetSlip(selection);
		}
	}

	export function setBetStake(selectionId: string, stake: number) {
		stake = +(stake / 1000).toFixed(5);
		var bets = getBetSlip();
		var bet = _.find(bets, (bet: any) => bet.selection === selectionId);
		if (!bet) {
			throw new Error('Bet not found from betslip: ' + selectionId);
		}
		bet.stake = stake;
		setBetSlip(bets);
	}

	export function getTotalStake() {
		var isMulti = isMultibet();
		if (isMulti) {
			return getMultiStake();
		}
		else {
			return getBetSlip().reduce((prev, cur) => prev + cur.stake, 0);
		}
	}

	export function placeBetAllowed() {
		if (!Meteor.userId()) {
			return false;
		}

		if (Session.get('clientCountry') == 'GB' || Session.get('clientCountry') == 'AU') {
			return false;
		}

		var state = getBetSlipState();
		if (state && ['processing', 'closing', 'success', 'changed', 'suspended'].indexOf(state.type) !== -1) {
			return false;
		}
		var bets = getBetSlip();
		if (!bets.length) {
			return false;
		}
		var isMulti = isMultibet();
		var allStakesOk = !_.find(bets, function (bet: BetSlipBet) {
			return bet.stake > 0 && bet.stake < 0.0001;
		});
		if (!isMulti && !allStakesOk) {
			return false;
		}
		if (isMulti && (getTotalStake() < 0.0001 || bets.length < 2)) {
			return false;
		}
		else {
			var last = !_.find(bets, function (bet: BetSlipBet) {
				if (!isMulti && !(bet.stake > 0)) {
					return true;
				}
				if (!bet.state) {
					return false;
				}
				return ['processing', 'closing', 'success', 'changed', 'pending'].indexOf(bet.state.type) !== -1;
			});
			return last;
		}
	}

	export function clearAllowed() {
		var bets = getBetSlip();
		if (!bets.length) {
			return false;
		}
		var state = getBetSlipState();
		return (!state || state.type !== 'processing') || getBetSlip().length > 0;
	}

	export function getPotentialPayout() {
		var isMulti = isMultibet();
		if (isMulti) {
			return getTotalOdds() * getMultiStake();
		}
		else {
			return getBetSlip().reduce((prev, cur) => prev + cur.stake * cur.odds, 0)
		}
	}

	export function getTotalOdds() {
		return getBetSlip().reduce((prev, cur) => prev * cur.odds, 1);
	}

	function isFatal(err) {
		return ['odds_changed', 'over_limit', 'under_limit'].indexOf(err.reason) === -1;
	}

	export function placeBet() {
		if (!placeBetAllowed()) {
			return;
		}
		var isMulti = isMultibet();
		var bets = getBetSlip();
		if (!bets.length) {
			return;
		}
		setBetSlipState({
			type: 'processing'
		});
		bets.forEach(function (bet: BetSlipBet) {
			bet.state = {type: 'processing'};
		});
		setBetSlip(bets);

		if (isMulti) {
			var selectionIds = bets.map((x) => x.selectionId);
			Betting.Ticket.placeBetClient(
				{
					selectionIds,
					odds: bets.reduce((prev, cur) => prev * cur.odds, 1),
					stake: getMultiStake(),
					isMulti,
					altStakeRefTicketId: getAltStakeRefTicketId(selectionIds)
				},
				function callback(err, res) {
					if (err) {
						localeError(err, 'betting');
						bets.forEach(function (bet: BetSlipBet) {
							bet.state = null;
						});
						setBetSlip(bets);
						setBetSlipState({
							type: isFatal(err) ? 'error' : 'warning',
							message: err.localeMessage || removeErrCode(err.message)
						});
						return;
					}
					bets.forEach(function (bet: BetSlipBet) {
						bet.id = res;
						bet.state = {type: 'success'};
					});
					setBetSlip(bets);

					$('.bet-done').show('slow', 'swing');
					Meteor.setTimeout(function () {
						$('.bet-done').hide('slow', 'swing');
					}, 6000);

					Meteor.setTimeout(function () {
						bets.forEach(function (bet: BetSlipBet) {
							closeBet(bet.selection);
						});
						setMultiStake(0);
						clearBetSlip();
					}, 100);
				}
			);
		}
		else {
			var count = bets.length;
			bets.forEach(function (bet: BetSlipBet) {
				var selectionIds = [bet.selectionId];
				Betting.Ticket.placeBetClient(
					{
						selectionIds,
						odds: bet.odds,
						stake: bet.stake,
						isMulti,
						altStakeRefTicketId: getAltStakeRefTicketId(selectionIds)
					},
					function (err, res) {
						if (!--count) {
							setBetSlipState(null);
						}
						if (err) {
							localeError(err, 'betting');
							setBetSlipState({
								// type: isFatal(err) ? 'error' : 'warning',
								type: 'error',
								message: err.localeMessage || removeErrCode(err.message)
							});
							bet.state = {
								// type: isFatal(err) ? 'error' : 'warning',
								type: 'error',
								message: err.localeMessage || removeErrCode(err.message)
							};
						}
						else {
							bet.id = res;
							bet.state = {
								type: 'pending'
							};

							$('.bet-done').show('slow', 'swing');
							Meteor.setTimeout(function () {
								$('.bet-done').hide('slow', 'swing');
							}, 6000);

							Meteor.setTimeout(function () {
								closeBet(bet.selection);
								clearBetSlip();
							}, 100);
						}
						setBetSlip(bets);
					}
				)
				;
			});
		}

	}

	function getAltStakeRefTicketId(selectionIds) {
		var rejectedBets = Session.get('rejectedBets');
		var matchedBetIndex = '';
		var matchedBet = _.find(rejectedBets, function (betSelections: any, rejectedBet: any) {
			matchedBetIndex = rejectedBet;
			return compareSelections(betSelections, selectionIds);
		});
		return matchedBet ? matchedBetIndex.toString() : undefined;
	}

	function compareSelections(selections1, selections2) {
		return selections1.length === selections2.length && _.every(selections1, function (selection: any) {
				return selections2.indexOf(selection) !== -1;
			});
	}

	function removeErrCode(message) {
		return message.replace(/\s+\[[45][0-9]{2}\]/, '');
	}

}
