import { z } from "zod";
import { ACTION_TYPES, WHO_SIDES } from "~/db/tables";
import {
	_action,
	checkboxValueToBoolean,
	id,
	modeShort,
	nullLiteraltoNull,
	numericEnum,
	safeJSONParse,
	stageId,
	weaponSplId,
} from "~/utils/zod";
import { TOURNAMENT } from "../tournament/tournament-constants";
import * as PickBan from "./core/PickBan";
import * as PreparedMaps from "./core/PreparedMaps";

const activeRosterPlayerIds = z.preprocess(safeJSONParse, z.array(id));

const bothTeamPlayerIds = z.preprocess(
	safeJSONParse,
	z.tuple([z.array(id), z.array(id)]),
);

const reportedMatchPosition = z.preprocess(
	Number,
	z
		.number()
		.int()
		.min(0)
		.max(Math.max(...TOURNAMENT.AVAILABLE_BEST_OF) - 1),
);

const point = z.number().int().min(0).max(100);
const points = z.preprocess(
	safeJSONParse,
	z
		.tuple([point, point])
		.nullish()
		.refine(
			(val) => {
				if (!val) return true;
				const [p1, p2] = val;

				// KO
				if (p1 === 100 && p2 === 0) return true;
				if (p2 === 100 && p1 === 0) return true;
				// ...or no points sent at all (TODO: if we decide that this KO only approach is solid then we can do a proper data model migration)
				if (p1 === 0 && p2 === 0) return true;

				return false;
			},
			{
				message: "Invalid points. Valid: 100-0, 0-100 or 0-0.",
			},
		),
);
export const matchSchema = z.union([
	z.object({
		_action: _action("REPORT_SCORE"),
		winnerTeamId: id,
		position: reportedMatchPosition,
		points,
	}),
	z.object({
		_action: _action("SET_ACTIVE_ROSTER"),
		roster: activeRosterPlayerIds,
		teamId: id,
	}),
	z.object({
		_action: _action("BAN_PICK"),
		stageId: stageId.optional(),
		mode: modeShort.optional(),
	}),
	z.object({
		_action: _action("UNDO_REPORT_SCORE"),
		position: reportedMatchPosition,
	}),
	z.object({
		_action: _action("UPDATE_REPORTED_SCORE"),
		rosters: bothTeamPlayerIds,
		resultId: id,
		points,
	}),
	z.object({
		_action: _action("REOPEN_MATCH"),
	}),
	z.object({
		_action: _action("SET_AS_CASTED"),
		twitchAccount: z.preprocess(
			nullLiteraltoNull,
			z.string().min(1).max(100).nullable(),
		),
	}),
	z.object({
		_action: _action("LOCK"),
		twitchAccount: z.string().min(1).max(100),
	}),
	z.object({
		_action: _action("UNLOCK"),
	}),
	z.object({
		_action: _action("END_SET"),
		winnerTeamId: z.preprocess(nullLiteraltoNull, id.nullable()),
	}),
	z.object({
		_action: _action("REPORT_WEAPON"),
		weaponSplId,
		mapIndex: z.coerce.number().int().nonnegative(),
	}),
	z.object({
		_action: _action("UNDO_WEAPON_REPORT"),
		mapIndex: z.coerce.number().int().nonnegative(),
	}),
]);

export const bracketIdx = z.coerce.number().int().min(0).max(100);

const customPickBanStep = z.object({
	action: z.enum(ACTION_TYPES),
	side: z.enum(WHO_SIDES).optional(),
});

const customPickBanFlow = z
	.object({
		preSet: z.array(customPickBanStep),
		postGame: z.array(customPickBanStep),
	})
	.nullish();

const tournamentRoundMaps = z.object({
	roundId: z.number().int().min(0),
	groupId: z.number().int().min(0),
	list: z
		.array(
			z.object({
				mode: modeShort,
				stageId,
			}),
		)
		.nullish(),
	count: numericEnum(TOURNAMENT.AVAILABLE_BEST_OF),
	type: z.enum(["BEST_OF", "PLAY_ALL"]),
	pickBan: z.enum(PickBan.types).nullish(),
	customFlow: customPickBanFlow,
});
export const bracketSchema = z.union([
	z.object({
		_action: _action("START_BRACKET"),
		bracketIdx,
		thirdPlaceMatchLinked: z.preprocess(checkboxValueToBoolean, z.boolean()),
		maps: z.preprocess(safeJSONParse, z.array(tournamentRoundMaps)),
	}),
	z.object({
		_action: _action("PREPARE_MAPS"),
		bracketIdx,
		maps: z.preprocess(safeJSONParse, z.array(tournamentRoundMaps)),
		thirdPlaceMatchLinked: z.preprocess(checkboxValueToBoolean, z.boolean()),
		eliminationTeamCount: z.coerce
			.number()
			.optional()
			.refine(
				(val) => !val || PreparedMaps.isValidMaxEliminationTeamCount(val),
			),
	}),
	z.object({
		_action: _action("ADVANCE_BRACKET"),
		groupId: id,
		bracketIdx,
	}),
	z.object({
		_action: _action("UNADVANCE_BRACKET"),
		groupId: id,
		roundId: id,
		bracketIdx,
	}),
	z.object({
		_action: _action("BRACKET_CHECK_IN"),
		bracketIdx,
	}),
	z.object({
		_action: _action("OVERRIDE_BRACKET_PROGRESSION"),
		tournamentTeamId: id,
		sourceBracketIdx: bracketIdx,
		destinationBracketIdx: z.union([bracketIdx, z.literal(-1)]),
	}),
]);

export const matchPageParamsSchema = z.object({ id, mid: id });

export const tournamentTeamPageParamsSchema = z.object({
	id,
	tid: id,
});

export type TournamentBadgeReceivers = z.infer<typeof badgeReceivers>;

const badgeReceivers = z.array(
	z.object({
		badgeId: id,
		tournamentTeamId: id,
		userIds: z.array(id).min(1).max(50),
	}),
);

export const finalizeTournamentActionSchema = z.object({
	badgeReceivers: z.preprocess(safeJSONParse, badgeReceivers.nullish()),
});
