import type {
  TeamSnapAssignment,
  TeamSnapAvailability,
  TeamSnapEvent,
  TeamSnapEventLineupEntry,
  TeamSnapMember,
  TeamSnapTeam,
} from "../api/types";

function asDisplayText(value: unknown): string {
  return typeof value === "string" ? value.trim() : "";
}

export function toDate(value: Date | string | undefined | null): Date | null {
  if (!value) {
    return null;
  }
  if (value instanceof Date) {
    return Number.isNaN(value.getTime()) ? null : value;
  }
  const parsed = new Date(value);
  return Number.isNaN(parsed.getTime()) ? null : parsed;
}

export function formatMemberName(member: TeamSnapMember | null | undefined): string {
  if (!member) {
    return "Unknown player";
  }
  const sdkName = typeof window !== "undefined" ? window.teamsnap?.memberName?.(member) : "";
  if (typeof sdkName === "string" && sdkName.trim()) {
    return sdkName.trim();
  }

  const name =
    asDisplayText(member.name) ||
    asDisplayText(member.fullName) ||
    asDisplayText(member.displayName) ||
    [asDisplayText(member.firstName), asDisplayText(member.lastName)].filter(Boolean).join(" ").trim();
  return name || `Player ${member.id}`;
}

export function formatMemberJerseyNumber(member: TeamSnapMember | null | undefined): string {
  if (!member) {
    return "";
  }
  const value =
    member.number ??
    member.jerseyNumber ??
    member.jersey_number;
  const text = asDisplayText(value);
  return text ? `#${text}` : "";
}

export function formatTeamLabel(team: TeamSnapTeam | null | undefined): string {
  if (!team) {
    return "No team selected";
  }
  const teamName = asDisplayText(team.name) || `Team ${team.id}`;
  const seasonName = asDisplayText(team.seasonName);
  return seasonName ? `${teamName} (${seasonName})` : teamName;
}

export function findCurrentPlayer(externalUserId: string | number | null | undefined, members: TeamSnapMember[]): TeamSnapMember | null {
  if (externalUserId == null || externalUserId === "") {
    return null;
  }

  const meId = String(externalUserId);

  return (
    members.find((member) => member.userId != null && String(member.userId) === meId) ??
    null
  );
}

export function formatGameTitle(game: TeamSnapEvent): string {
  const name = asDisplayText(game.name);
  const dayLabel = formatGameDayLabel(game);
  if (name) {
    return `${name}${dayLabel}`;
  }
  const opponentName = asDisplayText(game.opponentName);
  if (opponentName) {
    return `vs ${opponentName}${dayLabel}`;
  }
  return `Game ${game.id}${dayLabel}`;
}

export function formatGameDate(game: TeamSnapEvent): string {
  const date = toDate(game.startDate);
  if (!date) {
    return "Date TBD";
  }
  return date.toLocaleString([], {
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit",
  });
}

function formatGameDayLabel(game: TeamSnapEvent): string {
  const date = toDate(game.startDate);
  if (!date) {
    return "";
  }

  const weekday = date
    .toLocaleDateString("en-US", { weekday: "short" })
    .replace(/\./g, "")
    .replace(/^./, (character) => character.toUpperCase());
  const monthDay = date.toLocaleDateString("en-US", { month: "numeric", day: "numeric" });
  return ` (${weekday} ${monthDay})`;
}

export function sortGames(events: TeamSnapEvent[]): TeamSnapEvent[] {
  return [...events]
    .filter((event) => event.isGame)
    .sort((left, right) => {
      const leftTime = toDate(left.startDate)?.getTime() ?? Number.MAX_SAFE_INTEGER;
      const rightTime = toDate(right.startDate)?.getTime() ?? Number.MAX_SAFE_INTEGER;
      return leftTime - rightTime;
    });
}

export function findNextGame(games: TeamSnapEvent[]): TeamSnapEvent | null {
  const now = Date.now();
  return games.find((game) => {
    const start = toDate(game.startDate);
    return start ? start.getTime() >= now : false;
  }) ?? games[0] ?? null;
}

function toId(value: number | string | undefined | null): string {
  return value == null ? "" : String(value);
}

function toSequence(value: number | string | null | undefined): number {
  if (value == null || value === "") {
    return Number.MAX_SAFE_INTEGER;
  }
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : Number.MAX_SAFE_INTEGER;
}

function getMemberLookupName(member: TeamSnapMember): string {
  return [asDisplayText(member.firstName), asDisplayText(member.lastName)].filter(Boolean).join(" ").trim();
}

function getLineupEntryMemberName(entry: TeamSnapEventLineupEntry): string {
  return asDisplayText(entry.memberName);
}

function getMemberLastName(member: TeamSnapMember): string {
  return asDisplayText(member.lastName);
}

function matchesLineupEntry(member: TeamSnapMember, entry: TeamSnapEventLineupEntry): boolean {
  const memberId = toId(member.id);
  if (memberId && toId(entry.memberId) === memberId) {
    return true;
  }

  const memberName = getMemberLookupName(member);
  const entryMemberName = getLineupEntryMemberName(entry);
  return memberName !== "" && entryMemberName !== "" && memberName === entryMemberName;
}

export function findLineupEntryForMember(
  member: TeamSnapMember,
  lineupEntries: TeamSnapEventLineupEntry[],
): TeamSnapEventLineupEntry | null {
  const matchingEntries = lineupEntries
    .filter((entry) => matchesLineupEntry(member, entry))
    .sort((left, right) => {
      const leftSequence = toSequence(left.sequence);
      const rightSequence = toSequence(right.sequence);
      if (leftSequence !== rightSequence) {
        return leftSequence - rightSequence;
      }
      return toId(left.id).localeCompare(toId(right.id));
    });

  return matchingEntries[0] ?? null;
}

export function orderMembersByAssignments(
  members: TeamSnapMember[],
  assignments: TeamSnapAssignment[],
): TeamSnapMember[] {
  if (!assignments.length) {
    return members;
  }

  const byId = new Map(members.map((member) => [toId(member.id), member] as const));
  const ordered: TeamSnapMember[] = [];
  const seen = new Set<string>();

  for (const assignment of assignments) {
    const memberId = toId(assignment.memberId);
    if (!memberId || seen.has(memberId)) {
      continue;
    }
    const member = byId.get(memberId);
    if (!member) {
      continue;
    }
    ordered.push(member);
    seen.add(memberId);
  }

  for (const member of members) {
    const memberId = toId(member.id);
    if (seen.has(memberId)) {
      continue;
    }
    ordered.push(member);
  }

  return ordered;
}

export function isPlayerMember(member: TeamSnapMember): boolean {
  return member.isNonPlayer !== true;
}

export function getAvailabilityRank(statusCode: number | null | undefined): number {
  if (statusCode === 1) {
    return 0;
  }
  if (statusCode === 2) {
    return 1;
  }
  if (statusCode == null) {
    return 2;
  }
  return 3;
}

export function getAvailabilityLabel(statusCode: number | null | undefined): string {
  if (statusCode === 1) {
    return "Yes";
  }
  if (statusCode === 2) {
    return "Maybe";
  }
  if (statusCode === 0) {
    return "No";
  }
  return "Unknown";
}

export function orderMembersByLineupAndRsvps(
  members: TeamSnapMember[],
  lineupEntries: TeamSnapEventLineupEntry[],
  availabilities: TeamSnapAvailability[],
): TeamSnapMember[] {
  if (!members.length) {
    return [];
  }

  const ordered: TeamSnapMember[] = [];
  const seen = new Set<string>();
  const availabilityByMemberId = new Map<string, TeamSnapAvailability>();

  for (const availability of availabilities) {
    const memberId = toId(availability.memberId);
    if (memberId) {
      availabilityByMemberId.set(memberId, availability);
    }
  }

  const lineupMembers: TeamSnapMember[] = [];
  const lineupMembersSeen = new Set<string>();

  for (const entry of lineupEntries.slice().sort((left, right) => {
    const leftSequence = toSequence(left.sequence);
    const rightSequence = toSequence(right.sequence);
    if (leftSequence !== rightSequence) {
      return leftSequence - rightSequence;
    }
    return toId(left.id).localeCompare(toId(right.id));
  })) {
    const member = members.find((candidate) => matchesLineupEntry(candidate, entry));
    if (!member) {
      continue;
    }
    const memberId = toId(member.id);
    if (lineupMembersSeen.has(memberId)) {
      continue;
    }
    lineupMembers.push(member);
    lineupMembersSeen.add(memberId);
  }

  for (const member of lineupMembers) {
    const memberId = toId(member.id);
    if (seen.has(memberId)) {
      continue;
    }
    ordered.push(member);
    seen.add(memberId);
  }

  const rankedMembers = members
    .filter((member) => isPlayerMember(member))
    .filter((member) => !seen.has(toId(member.id)))
    .map((member, index) => {
      const memberId = toId(member.id);
      const availability = availabilityByMemberId.get(memberId);
      return {
        member,
        rank: getAvailabilityRank(availability?.statusCode as number | null | undefined),
        index,
      };
    })
    .sort((left, right) => {
      if (left.rank !== right.rank) {
        return left.rank - right.rank;
      }
      const leftLastName = getMemberLastName(left.member).toLowerCase();
      const rightLastName = getMemberLastName(right.member).toLowerCase();
      if (leftLastName !== rightLastName) {
        return leftLastName.localeCompare(rightLastName);
      }
      const leftFirstName = asDisplayText(left.member.firstName).toLowerCase();
      const rightFirstName = asDisplayText(right.member.firstName).toLowerCase();
      if (leftFirstName !== rightFirstName) {
        return leftFirstName.localeCompare(rightFirstName);
      }
      return left.index - right.index;
    })
    ;

  for (const entry of rankedMembers) {
    ordered.push(entry.member);
    seen.add(toId(entry.member.id));
  }

  for (const member of members) {
    const memberId = toId(member.id);
    if (seen.has(memberId)) {
      continue;
    }
    ordered.push(member);
  }

  return ordered;
}
