import { assembleQueryParams, FetchError } from '../utils/FetchHelper';
import { APIColumnDef } from '../utils/TableHelper';

// Define API endpoint for teams
export const TEAMS_API_URL = location.protocol + location.host + '/api/teams';

// Define TypeScript types for teams matching ShipME API schema
// NOTE: These cannot be used like classes, they are essentially just validators for JSON objects
export type Team = {
  // Uses POST body to determine required properties
  team_id?: number;
  name?: string;
  section_id: number;
  team_num: number;
  locker_num?: number;
};
export type TeamPatch = {
  name?: string;
  section_id?: number;
  locker_num?: number | 'unset';
};
export type TeamTableData = {
  team_id?: number | string;
  name?: string;
  section_id: number | string;
  team_num: number | string;
  locker_num?: number | string;
};

// Function returns a formatted string for a team's name based on whether the "name" property is null
// IN: team : team object that a formatted name string is needed for
//     excludeSection : optional boolean that determines whether to exclude the section number (used in cases where section is already displayed)
//     includeLockerNum : optional boolean that determines whether the team's locker number should be included in the output
// OUT: string like "[Team name] (S[section #], T[team num])" or "Section [section #], Team [team num]"
export function getFormattedTeamName(
  team: Team,
  excludeSection?: boolean,
  includeLockerNum?: boolean
): string {
  let name = '';
  if (team.name) {
    name += team.name + ' ';
    name += '(';
    if (!excludeSection) {
      name += 'S' + team.section_id + ', ';
    }
    name += 'T' + team.team_num;
    if (includeLockerNum && team.locker_num) {
      name += ', L' + team.locker_num;
    }
    name += ')';
  } else {
    if (!excludeSection) {
      name += 'Section ' + team.section_id + ', ';
    }
    name += 'Team ' + team.team_num;
    if (includeLockerNum && team.locker_num) {
      name += ' (L' + team.locker_num + ')';
    }
  }

  return name;
}

// Define ColumnDef array for Team type
export const defaultTeamColumns: APIColumnDef<TeamTableData, TeamPatch>[] = [
  {
    accessorKey: 'team_id',
    header: 'ID',
    enableColumnFilter: false,
  },
  {
    accessorKey: 'name',
    header: 'Name',
    sortDescFirst: false,
    filterFn: 'equalsString',
    meta: {
      update: updateTeam,
      element: 'input',
      allowedUserTypes: ['instructor', 'ta', 'student'],
    },
  },
  {
    accessorKey: 'section_id',
    header: 'Section #',
    sortDescFirst: false,
    filterFn: 'equalsString',
    meta: {
      update: updateTeam,
      type: 'number',
      element: 'input',
      allowedUserTypes: ['instructor', 'ta'],
    },
  },
  {
    accessorKey: 'team_num',
    header: 'Team #',
    sortDescFirst: false,
    filterFn: 'equalsString',
  },
  {
    accessorKey: 'locker_num',
    header: 'Locker #',
    sortDescFirst: false,
    filterFn: 'equalsString',
    meta: {
      update: updateTeam,
      type: 'number',
      element: 'input',
      allowedUserTypes: ['student', 'instructor', 'ta'],
    },
  },
];

// Function makes GET request to ShipME API /api/teams endpoint and returns an array of Teams
// To use only one filter, pass undefined (the word, not a string) for the ommitted parameter
// IN: section_id : optional section_id to filter teams by
// OUT: array of retrieved Teams
// THROWS: FetchError
export async function getTeams(section_id?: number): Promise<Team[]> {
  const _url = assembleQueryParams(TEAMS_API_URL, {
    section_id: section_id?.toString(),
  });

  // Attempt GET /api/teams
  try {
    const response = await fetch(_url.toString());
    // Catch response codes other than 200-class
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(errorText || 'Failed to retrieve teams', response);
    }

    // Return teams as array of type Team
    const teams: Team[] = await response.json();
    return teams;
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}

// Function makes POST request to ShipME API /api/teams endpoint and returns a single Team
// IN: team : Team object with team data to be submitted
// OUT: response Team object from API
// THROWS: FetchError
export async function createTeam(team: Team): Promise<Team> {
  const _url = new URL(TEAMS_API_URL);

  // Attempt POST /api/teams
  try {
    const response = await fetch(_url.toString(), {
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'POST',
      body: JSON.stringify(team),
    });
    // Catch response codes other than 200-class
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(errorText || 'Failed to create team', response);
    }

    // Return team as type Team
    team = await response.json();
    return team;
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}

// Function makes GET request to ShipME API /api/teams/{team_id} endpoint and returns a single Team
// IN: team_id : the team_id of the team to retrieve
// OUT: retrieved Team
// THROWS: FetchError
export async function getTeam(team_id: number): Promise<Team> {
  const _url = new URL(TEAMS_API_URL + '/' + team_id);

  // Attempt GET /api/teams/{team_id}
  try {
    const response = await fetch(_url.toString());
    // Catch response codes other than 200-class
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(errorText || 'Failed to retrieve team', response);
    }

    // Return team as type Team
    const team: Team = await response.json();
    return team;
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}

// Function makes PATCH request to ShipME API /api/teams/{team_id} endpoint and returns a single Team
// IN: team_id : the team_id of the team to update
//     patchObject : TeamPatch object with optional team data to be submitted
// OUT: response Team object from API
// THROWS: FetchError
export async function updateTeam(
  team_id: number,
  patchObject: TeamPatch
): Promise<Team> {
  const _url = new URL(TEAMS_API_URL + '/' + team_id);

  // Attempt PATCH /api/teams/{team_id}
  try {
    const response = await fetch(_url.toString(), {
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'PATCH',
      body: JSON.stringify(patchObject),
    });
    // Catch response codes other than 200-class
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(errorText || 'Failed to update team', response);
    }

    // Return team as type Team
    const team: Team = await response.json();
    return team;
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}

// Function makes DELETE request to ShipME API /api/teams/{team_id} endpoint and returns a Response
// IN: team_id : the team_id of the team to delete
// OUT: Fetch Response object
// THROWS: FetchError
export async function deleteTeam(team_id: number): Promise<Response> {
  const _url = new URL(TEAMS_API_URL + '/' + team_id);

  // Attempt DELETE /api/teams/{team_id}
  try {
    const response = await fetch(_url.toString(), {
      method: 'DELETE',
    });
    // Catch response codes other than 200-class
    if (!response.ok) {
      const errorText = await response.text();
      throw new FetchError(errorText || 'Failed to delete team', response);
    }

    return response;
  } catch (error) {
    throw new FetchError((error as Error).message);
  }
}
