import {
  NewWeeklyReport,
  NewUser,
  User,
  Project,
  NewProject,
  UserProjectMember,
} from "../Types/goTypes";

// This type of pattern should be hard to misuse
export interface APIResponse<T> {
  success: boolean;
  message?: string;
  data?: T;
}

// Note that all protected routes also require a token
// Defines all the methods that an instance of the API must implement
interface API {
  /** Register a new user */
  registerUser(user: NewUser): Promise<APIResponse<User>>;
  /** Remove a user */
  removeUser(username: string, token: string): Promise<APIResponse<User>>;
  /** Login */
  login(NewUser: NewUser): Promise<APIResponse<string>>;
  /** Renew the token */
  renewToken(token: string): Promise<APIResponse<string>>;
  /** Create a project */
  createProject(
    project: NewProject,
    token: string,
  ): Promise<APIResponse<Project>>;
  /** Submit a weekly report */
  submitWeeklyReport(
    project: NewWeeklyReport,
    token: string,
  ): Promise<APIResponse<NewWeeklyReport>>;
  /**Gets a weekly report*/
  getWeeklyReport(
    username: string,
    projectName: string,
    week: string,
    token: string,
  ): Promise<APIResponse<NewWeeklyReport>>;
  /** Gets all the projects of a user*/
  getUserProjects(token: string): Promise<APIResponse<Project[]>>;
  /** Gets a project from id*/
  getProject(id: number): Promise<APIResponse<Project>>;
  /** Gets all users*/
  getAllUsers(token: string): Promise<APIResponse<string[]>>;
  /** Gets all users in a project from name*/
  getAllUsersProject(
    projectName: string,
    token: string,
  ): Promise<APIResponse<UserProjectMember[]>>;
}

// Export an instance of the API
export const api: API = {
  async registerUser(user: NewUser): Promise<APIResponse<User>> {
    try {
      const response = await fetch("/api/register", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(user),
      });

      if (!response.ok) {
        return {
          success: false,
          message: "Failed to register user: " + response.status,
        };
      } else {
        // const data = (await response.json()) as User; // The API does not currently return the user
        return { success: true };
      }
    } catch (e) {
      return {
        success: false,
        message: "Unknown error while registering user",
      };
    }
  },

  async removeUser(
    username: string,
    token: string,
  ): Promise<APIResponse<User>> {
    try {
      const response = await fetch(`/api/userdelete/${username}`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
        body: JSON.stringify(username),
      });

      if (!response.ok) {
        return { success: false, message: "Failed to remove user" };
      } else {
        const data = (await response.json()) as User;
        return { success: true, data };
      }
    } catch (e) {
      return { success: false, message: "Failed to remove user" };
    }
  },

  async createProject(
    project: NewProject,
    token: string,
  ): Promise<APIResponse<Project>> {
    try {
      const response = await fetch("/api/project", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
        body: JSON.stringify(project),
      });

      if (!response.ok) {
        return { success: false, message: "Failed to create project" };
      } else {
        const data = (await response.json()) as Project;
        return { success: true, data };
      }
    } catch (e) {
      return { success: false, message: "Failed to create project" };
    }
  },

  async renewToken(token: string): Promise<APIResponse<string>> {
    try {
      const response = await fetch("/api/loginrenew", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
      });

      if (!response.ok) {
        return { success: false, message: "Failed to renew token" };
      } else {
        const data = (await response.json()) as string;
        return { success: true, data };
      }
    } catch (e) {
      return { success: false, message: "Failed to renew token" };
    }
  },

  async getUserProjects(token: string): Promise<APIResponse<Project[]>> {
    try {
      const response = await fetch("/api/getUserProjects", {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
      });

      if (!response.ok) {
        return Promise.resolve({
          success: false,
          message: "Failed to get user projects",
        });
      } else {
        const data = (await response.json()) as Project[];
        return Promise.resolve({ success: true, data });
      }
    } catch (e) {
      return Promise.resolve({
        success: false,
        message: "API fucked",
      });
    }
  },

  async submitWeeklyReport(
    weeklyReport: NewWeeklyReport,
    token: string,
  ): Promise<APIResponse<NewWeeklyReport>> {
    try {
      const response = await fetch("/api/submitWeeklyReport", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
        body: JSON.stringify(weeklyReport),
      });

      if (!response.ok) {
        return {
          success: false,
          message: "Failed to submit weekly report",
        };
      }

      const data = (await response.json()) as NewWeeklyReport;
      return { success: true, data };
    } catch (e) {
      return {
        success: false,
        message: "Failed to submit weekly report",
      };
    }
  },

  async getWeeklyReport(
    username: string,
    projectName: string,
    week: string,
    token: string,
  ): Promise<APIResponse<NewWeeklyReport>> {
    try {
      const response = await fetch("/api/getWeeklyReport", {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
        body: JSON.stringify({ username, projectName, week }),
      });

      if (!response.ok) {
        return { success: false, message: "Failed to get weekly report" };
      } else {
        const data = (await response.json()) as NewWeeklyReport;
        return { success: true, data };
      }
    } catch (e) {
      return { success: false, message: "Failed to get weekly report" };
    }
  },

  async login(NewUser: NewUser): Promise<APIResponse<string>> {
    try {
      const response = await fetch("/api/login", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(NewUser),
      });

      if (!response.ok) {
        return { success: false, message: "Failed to login" };
      } else {
        const data = (await response.json()) as { token: string }; // Update the type of 'data'
        return { success: true, data: data.token };
      }
    } catch (e) {
      return Promise.resolve({ success: false, message: "Failed to login" });
    }
  },

  // Gets a projet by id, currently untested since we have no javascript-based tests
  async getProject(id: number): Promise<APIResponse<Project>> {
    try {
      const response = await fetch(`/api/project/${id}`, {
        method: "GET",
      });

      if (!response.ok) {
        return {
          success: false,
          message: "Failed to get project: Response code " + response.status,
        };
      } else {
        const data = (await response.json()) as Project;
        return { success: true, data };
      }
      // The code below is garbage but satisfies the linter
      // This needs fixing, do not copy this pattern
    } catch (e: unknown) {
      return {
        success: false,
        message: "Failed to get project: " + (e as Error).toString(),
      };
    }
  },

  // Gets all users
  async getAllUsers(token: string): Promise<APIResponse<string[]>> {
    try {
      const response = await fetch("/api/users/all", {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
      });

      if (!response.ok) {
        return Promise.resolve({
          success: false,
          message: "Failed to get users",
        });
      } else {
        const data = (await response.json()) as string[];
        return Promise.resolve({ success: true, data });
      }
    } catch (e) {
      return Promise.resolve({
        success: false,
        message: "API is not ok",
      });
    }
  },
  //Gets all users in a project
  async getAllUsersProject(
    projectName: string,
    token: string,
  ): Promise<APIResponse<UserProjectMember[]>> {
    try {
      const response = await fetch(`/api/getUsersProject/${projectName}`, {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          Authorization: "Bearer " + token,
        },
      });

      if (!response.ok) {
        return Promise.resolve({
          success: false,
          message: "Failed to get users",
        });
      } else {
        const data = (await response.json()) as UserProjectMember[];
        return Promise.resolve({ success: true, data });
      }
    } catch (e) {
      return Promise.resolve({
        success: false,
        message: "API is not ok",
      });
    }
  },
};
