import type { Pronouns } from "~/db/tables";
import type { TierName } from "~/features/mmr/mmr-constants";
import type { TournamentManagerDataSet } from "~/features/tournament-bracket/core/engine/types";

/** GET /api/user/{userId|discordId} */

export interface GetUserResponse {
	id: number;
	/**
	 * @example "Sendou"
	 */
	name: string;
	/**
	 * @example "79237403620945920"
	 */
	discordId: string;
	/**
	 * @example "https://sendou.ink/u/sendou"
	 */
	url: string;
	/**
	 * @example "https://cdn.discordapp.com/avatars/79237403620945920/6fc41a44b069a0d2152ac06d1e496c6c.png"
	 */
	avatarUrl: string | null;
	/**
	 * @example "FI"
	 */
	country: string | null;
	socials: {
		twitch: string | null;
		// @deprecated
		twitter: null;
		battlefy: string | null;
		bsky: string | null;
	};
	plusServerTier: 1 | 2 | 3 | null;
	weaponPool: Array<ProfileWeapon>;
	badges: Array<Badge>;
	/** Teams user is member of. The main team is always first in the array. */
	teams: Array<GlobalTeamMembership>;
	/**
	 * Splatoon 3 splashtag name & ID, if one is set.
	 *
	 * @example "Sendou#2955"
	 */
	inGameName: string | null;
	/**
	 * User's pronouns.
	 *
	 * @example { "subject": "he", "object": "him" }
	 */
	pronouns: Pronouns | null;
	peakXp: number | null;
	/** Users current (or previous if it's off-season) ranked season (SendouQ & ranked tournaments) rank. Null if no rank for the season in question or the season does not have yet enough players on the leaderboard. */
	currentRank: SeasonalRank | null;
}

/** GET /api/user/{userId|discordId|customUrl}/ids */

export interface GetUserIdsResponse {
	id: number;
	/**
	 * @example "79237403620945920"
	 */
	discordId: string;
	/**
	 * @example "sendou"
	 */
	customUrl: string | null;
}

/** GET /api/team/{teamId} */

export interface GetTeamResponse {
	id: number;
	/**
	 * Name of the global team.
	 *
	 * @example "Moonlight"
	 */
	name: string;
	/**
	 * URL for the global team page.
	 *
	 * @example "https://sendou.ink/t/moonlight"
	 */
	teamPageUrl: string;
	/**
	 * URL for the global team logo.
	 *
	 * @example "https://sendou.nyc3.cdn.digitaloceanspaces.com/pickup-logo-uReSb1b1XS3TWGLCKMDUD-1719054364813.webp"
	 */
	logoUrl: string | null;
}

/** GET /api/calendar/{year}/{week} */

export type GetCalendarWeekResponse = Array<{
	/**
	 * @example "In The Zone 30"
	 */
	name: string;
	tournamentId: number | null;
	/**
	 * @example "https://sendou.ink/to/9/brackets"
	 */
	tournamentUrl: string | null;
	/**
	 * @example "2024-01-12T20:00:00.000Z"
	 */
	startTime: string;
}>;

/** GET /api/user/{userId}/active-match */

export interface GetUsersActiveMatchResponse {
	/** The user's current match ID or null if none */
	matchId: number | null;
	/** What kind of match the user is in right now */
	lobby: "sendouq" | "tournament" | null;
	/** The ID of the tournament (null for sendouq or no match) */
	tournamentId: number | null;
	/** The bracket index within the tournament (null for sendouq or no match). Can be used with GET /api/tournament/{tournamentId}/brackets/{bracketIdx} */
	bracketIdx: number | null;
}

/** GET /api/sendouq/active-match/{userId} */

export interface GetUsersActiveSendouqMatchResponse {
	/** The user's current match ID or null if none */
	matchId: number | null;
}

/** GET /api/sendouq/match/{matchId} */

export interface GetSendouqMatchResponse {
	teamAlpha: SendouqMatchTeam | null;
	teamBravo: SendouqMatchTeam | null;
	mapList: Array<MapListMap>;
}

type SendouqMatchTeam = {
	id: number;
	score: number;
	players: Array<SendouqMatchPlayer>;
};

type SendouqMatchPlayer = {
	userId: number;
	/** User's at the start time of the match */
	rank: SendouQRank | null;
};

type SendouQRank = { name: TierName; isPlus: boolean };

/** GET /api/tournament/{tournamentId} */

export interface GetTournamentResponse {
	/**
	 * @example "In The Zone 30"
	 */
	name: string;
	/**
	 * @example "https://sendou.ink/to/9/brackets"
	 */
	url: string;
	/**
	 * @example "https://sendou-assets.nyc3.cdn.digitaloceanspaces.com/img/tournament-logos/itz.avif"
	 */
	logoUrl: string | null;
	/**
	 * @example "2024-01-12T20:00:00.000Z"
	 */
	startTime: string;
	teams: {
		registeredCount: number;
		checkedInCount: number;
	};
	brackets: TournamentBracket[];
	organizationId: number | null;
	/** Has the tournament concluded (results added to user profiles & no editing possible anymore) */
	isFinalized: boolean;
}

/** GET /api/tournament/{tournamentId}/teams */

export type GetTournamentTeamsResponse = Array<{
	id: number;
	/**
	 * @example "Team Olive"
	 */
	name: string;
	/**
	 * @example "2024-01-12T20:00:00.000Z"
	 */
	registeredAt: string;
	checkedIn: boolean;
	/**
	 * URL for the tournament team page.
	 *
	 * @example "https://sendou.ink/to/9/teams/327"
	 */
	url: string;
	/**
	 * URL for the global team page.
	 *
	 * @example "https://sendou.ink/t/moonlight"
	 */
	teamPageUrl: string | null;
	/**
	 * @example "https://sendou.nyc3.cdn.digitaloceanspaces.com/pickup-logo-uReSb1b1XS3TWGLCKMDUD-1719054364813.webp"
	 */
	logoUrl: string | null;
	seed: number | null;
	mapPool: Array<StageWithMode> | null;
	/**
	 *  Seeding power is a non-resetting MMR value that is used for sendou.ink's autoseeding capabilities.
	 *  It is calculated as the average of the team's members' seeding power.
	 *  Ranked and unranked tournaments contribute to different seeding power values.
	 */
	seedingPower: {
		ranked: number | null;
		unranked: number | null;
	};
	members: Array<{
		userId: number;
		/**
		 * @example "Sendou"
		 */
		name: string;
		/**
		 * @example "79237403620945920"
		 */
		discordId: string;
		/**
		 * @example "sendouc"
		 */
		battlefy: string | null;
		/**
		 * @example "https://cdn.discordapp.com/avatars/79237403620945920/6fc41a44b069a0d2152ac06d1e496c6c.png"
		 */
		avatarUrl: string | null;
		/**
		 * @example "FI"
		 */
		country: string | null;
		captain: boolean;
		/**
		 * Splatoon 3 splashtag name & ID. Notice the value returned is the player's set name at the time of the tournament.
		 * Only available for tournaments with the "Require IGN's" option enabled.
		 *
		 * @example "Sendou#2955"
		 */
		inGameName: string | null;
		/**
		 * User's pronouns.
		 *
		 * @example { "subject": "he", "object": "him" }
		 */
		pronouns: Pronouns | null;
		/**
		 *  Switch friend code used for identification purposes.
		 *
		 * @example "1234-5678-9101"
		 */
		friendCode: string;
		/**
		 * @example "2024-01-12T20:00:00.000Z"
		 */
		joinedAt: string;
	}>;
}>;

/** GET /api/tournament/{tournamentId}/players */

export type GetTournamentPlayersResponse = Array<{
	userId: number;
	matchIds: number[];
}>;

/** GET /api/tournament/{tournamentId}/casted */

export interface GetCastedTournamentMatchesResponse {
	/*
	 * Matches that are currently being played and casted. Note: at the moment only one match can be casted at a time but this is an array for future proofing.
	 */
	current: Array<{
		matchId: number;
		channel: TournamentCastChannel;
	}>;
	/*
	 * Matches that are locked to be casted.
	 */
	future: Array<{
		matchId: number;
		channel: TournamentCastChannel;
	}>;
}

type TournamentCastChannel = {
	type: "TWITCH";
	/**
	 * @example "iplsplatoon"
	 */
	channelId: string;
};

/** GET /api/tournament/{tournamentId}/streams */

export type GetTournamentStreamsResponse = Array<
	{
		platform: "TWITCH";
		channelId: string;
		viewerCount: number;
	} & ({ type: "PLAYER"; userId: number } | { type: "CAST" })
>;

/** GET /api/tournament-match/{matchId} */

export interface GetTournamentMatchResponse {
	teamOne: TournamentMatchTeam | null;
	teamTwo: TournamentMatchTeam | null;
	/**
	 * Name of the bracket this match belongs to.
	 *
	 * @example "Alpha Bracket"
	 */
	bracketName: string | null;
	/**
	 * Name of the round this match belongs to.
	 *
	 * @example "Grand Finals"
	 */
	roundName: string | null;
	mapList: Array<MapListMap> | null;
	/**
	 * @example "https://sendou.ink/to/9/matches/695"
	 */
	url: string;
}

/** GET /api/tournament/{tournamentId}/brackets/{bracketIndex} */

export interface GetTournamentBracketResponse {
	data: TournamentBracketData;
	teams: Array<{
		id: number;
		checkedIn: boolean;
	}>;
	meta: {
		/** How many teams per group? (round robin only) */
		teamsPerGroup?: number;
		/** How many groups? (swiss only) */
		groupCount?: number;
		/** How many rounds? (swiss only) */
		roundCount?: number;
	};
}

/** GET /api/tournament/{tournamentId}/brackets/{bracketIndex}/standings */

export interface GetTournamentBracketStandingsResponse {
	standings: Array<{
		tournamentTeamId: number;
		placement: number;
		stats?: {
			setWins: number;
			setLosses: number;
			mapWins: number;
			mapLosses: number;
			/** @deprecated points are no longer tracked, see koCount instead */
			points?: number;
			/** (round robin only) how many knockout wins the team has */
			koCount?: number;
			winsAgainstTied: number;
			lossesAgainstTied?: number;
			buchholzSets?: number;
			buchholzMaps?: number;
		};
	}>;
}

/** GET /api/org/{organizationId} */

export interface GetTournamentOrganizationResponse {
	id: number;
	/**
	 * @example "Dapple Productions"
	 */
	name: string;
	description: string | null;
	/**
	 * @example "https://sendou.ink/org/dapple-productions"
	 */
	url: string;
	/**
	 * @example "https://sendou.nyc3.cdn.digitaloceanspaces.com/gBn45bbUMXM6359ZDQS5_-1722059432073.webp"
	 */
	logoUrl: string | null;
	members: Array<TournamentOrganizationMember>;
	socialLinkUrls: Array<string>;
}

interface TournamentOrganizationMember {
	userId: number;
	/**
	 * @example "Sendou"
	 */
	name: string;
	/**
	 * @example "79237403620945920"
	 */
	discordId: string;
	/**
	 * User's pronouns.
	 *
	 * @example { "subject": "he", "object": "him" }
	 */
	pronouns: Pronouns | null;
	role: "ADMIN" | "MEMBER" | "ORGANIZER" | "STREAMER";
	roleDisplayName: string | null;
}

/* ----------------------------------------- */

type Weapon = {
	id: number;
	name: string;
};

type ProfileWeapon = Weapon & { isFiveStar: boolean };

interface GlobalTeamMembership {
	/**
	 * ID for the global team page.
	 */
	id: number;
	/**
	 * Role of the user in the team.
	 */
	role: TeamMemberRole | null;
}

type TeamMemberRole =
	| "CAPTAIN"
	| "CO_CAPTAIN"
	| "FRONTLINE"
	| "SLAYER"
	| "SKIRMISHER"
	| "SUPPORT"
	| "MIDLINE"
	| "BACKLINE"
	| "FLEX"
	| "SUB"
	| "COACH"
	| "CHEERLEADER";

interface SeasonalRank {
	tier: {
		name: RankTierName;
		isPlus: boolean;
	};
	/**
	 * Which season this rank is for.
	 *
	 * @example 7
	 */
	season: number;
}

type RankTierName =
	| "LEVIATHAN"
	| "DIAMOND"
	| "PLATINUM"
	| "GOLD"
	| "SILVER"
	| "BRONZE"
	| "IRON";

type Badge = {
	/**
	 * @example "Monday Afterparty"
	 */
	name: string;
	count: number;
	/**
	 * @example "https://sendou-assets.nyc3.cdn.digitaloceanspaces.com/badges/monday.avif"
	 */
	imageUrl: string;
	/**
	 * @example "https://sendou-assets.nyc3.cdn.digitaloceanspaces.com/badges/monday.gif"
	 */
	gifUrl: string;
};

type ModeShort = "TW" | "SZ" | "TC" | "RM" | "CB";
type Stage = {
	id: number;
	name: string;
};

type StageWithMode = {
	mode: ModeShort;
	stage: Stage;
};

export type MapListMap = {
	map: StageWithMode;
	/**
	 * One of the following:
	 * - id of the team that picked the map
	 * - "DEFAULT" if it was a default map, something went wrong with the algorithm typically
	 * - "TIEBREAKER" if it was a tiebreaker map (selected by the TO)
	 * - "BOTH" both teams picked the map
	 * - "TO" if it was a TO pick (from predefined maplist)
	 * - "COUNTERPICK" if it was a counterpick
	 * - "ROLL" if it was randomly selected
	 */
	source:
		| number
		| "DEFAULT"
		| "TIEBREAKER"
		| "BOTH"
		| "TO"
		| "COUNTERPICK"
		| "ROLL";
	winnerTeamId: number | null;
	participatedUserIds: Array<number> | null;
	/** (round robin only) points of the match used for tiebreaker purposes. e.g. [100, 0] indicates a knockout. */
	points: [number, number] | null;
};

type TournamentMatchTeam = {
	id: number;
	score: number;
};

type TournamentBracket = {
	type: "double_elimination" | "single_elimination" | "round_robin" | "swiss";
	name: string;
};

type TournamentBracketData = TournamentManagerDataSet;

/** POST /api/tournament/{id}/seeds */

/** @lintignore */
export interface TournamentSeedsBody {
	tournamentTeamIds: number[];
}

/** POST /api/tournament/{id}/starting-brackets */

/** @lintignore */
export interface TournamentStartingBracketsBody {
	startingBrackets: Array<{
		tournamentTeamId: number;
		startingBracketIdx: number;
	}>;
}

/** POST /api/tournament/{id}/teams/{tournamentTeamId}/add-member */
/** POST /api/tournament/{id}/teams/{tournamentTeamId}/remove-member */

/** @lintignore */
export interface TournamentTeamMemberBody {
	userId: number;
}

/** POST /api/tournament/{id}/teams/{tournamentTeamId}/update-member-ign */

/** @lintignore */
export interface TournamentUpdateMemberIgnBody {
	userId: number;
	inGameName: string;
}
