import axios, { AxiosInstance, AxiosResponse } from 'axios'
import { errorInterceptor } from '@/util/interceptor'
import { Credentials } from '@/store/modules/authentication'
import {
  Assignment,
  Exam,
  Feedback, FeedbackComment,
  JSONWebToken, Statistics,
  StudentInfo,
  StudentInfoForListView,
  Submission,
  SubmissionNoType, SubmissionType,
  Tutor, UserAccount, LabelStatisticsForSubType,
  FeedbackLabel, SolutionComment,
  CreateUpdateFeedback,
  AvailableSubmissionCounts,
  Group,
  Config,
  GitlabRelease,
} from '@/models'
import { CreateAssignment } from './models'

export function getInstanceBaseUrl (): string {
  if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') {
    return `${window.location.protocol}//${window.location.host}${window.location.pathname}`.replace(/\/+$/, '')
  } else {
    return 'http://localhost:8000/'
  }
}

let ax: AxiosInstance = axios.create({
  baseURL: getInstanceBaseUrl()
})

export async function registerTutor (credentials: Credentials): Promise<AxiosResponse<Tutor>> {
  return ax.post<Tutor>('/api/corrector/register/', credentials)
}

export async function fetchJWT (credentials: Credentials): Promise<JSONWebToken> {
  const token: string = (await ax.post('/api/get-token/', credentials)).data.token
  ax.defaults.headers['Authorization'] = `JWT ${token}`
  return { token }
}

export async function refreshJWT (oldToken: string): Promise<JSONWebToken> {
  const token: string = (await ax.post('/api/refresh-token/', { token: oldToken })).data.token
  ax.defaults.headers['Authorization'] = `JWT ${token}`
  return { token }
}

export async function fetchConfig (): Promise<Config> {
  return (await ax.get('/api/config/')).data
}

export async function fetchStudentSelfData (): Promise<StudentInfo> {
  return (await ax.get('/api/student-page/')).data
}

export async function fetchStudentSubmissions (): Promise<Array<Submission>> {
  return (await ax.get('/api/student-submissions/')).data
}

export async function fetchSubmissionFeedbackTests ({ pk }: {pk: string}): Promise<SubmissionNoType> {
  return (await ax.get(`/api/submission/${pk}/`)).data
}

export async function fetchSubmissionSourceCode(pk: string): Promise<{sourceCode: string}> {
  return (await ax.get(`/api/submission/${pk}/source_code/`)).data
}

export async function fetchNotebookSubmissionAsHtml(pk: string): Promise<any> {
  return (await ax.get(`/api/submission/${pk}/html/`)).data
}

export async function fetchAllStudents (): Promise<Array<StudentInfoForListView>> {
  const url = '/api/student/'
  return (await ax.get(url)).data
}

export async function fetchStudent ({ pk }:
{pk: string}): Promise<StudentInfoForListView> {
  const url = `/api/student/${pk}/`
  return (await ax.get(url)).data
}

export async function fetchAllTutors (): Promise<Array<Tutor>> {
  const url = '/api/corrector/'
  return (await ax.get(url)).data
}

export async function fetchAllFeedback (): Promise<Array<Feedback>> {
  const url = '/api/feedback/'
  return (await ax.get(url)).data
}

export async function fetchFeedback ({ ofSubmission }: {ofSubmission: string}): Promise<Feedback> {
  const url = `/api/feedback/${ofSubmission}/`
  return (await ax.get(url)).data
}

export async function fetchExamTypes (): Promise<Array<Exam>> {
  const url = '/api/examtype/'
  return (await ax.get(url)).data
}

export async function fetchStatistics (): Promise<Statistics> {
  const url = '/api/statistics/'
  return (await ax.get(url)).data
}

export async function fetchLabelStatistics (): Promise<LabelStatisticsForSubType[]> {
  const url = '/api/label-statistics'
  return (await ax.get(url)).data
}


export async function createAssignment (data: CreateAssignment): Promise<Assignment> {
  return (await ax.post('/api/assignment/', data)).data
}

export async function submitFeedbackForAssignment ({ feedback, assignment }:
  { feedback: Partial<CreateUpdateFeedback>, assignment: Assignment}): Promise<CreateUpdateFeedback> {
  return (await ax.post(`/api/assignment/${assignment.pk}/finish/`, feedback)).data
}

export async function submitUpdatedFeedback ({ feedback }:
  {feedback: CreateUpdateFeedback}): Promise<CreateUpdateFeedback> {
  return (await ax.patch(`/api/feedback/${feedback.ofSubmission}/`, feedback)).data
}

export async function submitFeedback ({ feedback }: {feedback: CreateUpdateFeedback}): Promise<Feedback> {
  return (await ax.post('/api/feedback/', feedback)).data
}

export async function fetchSubmissionTypes (): Promise<Array<SubmissionType>> {
  const url = '/api/submissiontype/'
  return (await ax.get(url)).data
}

export async function fetchSubmissionType (pk: string): Promise<SubmissionType> {
  const url = `/api/submissiontype/${pk}/`
  return (await ax.get(url)).data
}

export async function fetchAvailableSubmissionCounts(group: Group | undefined): Promise<AvailableSubmissionCounts> {
  const query = group ? '?group=' + group.pk : ''
  const url = '/api/submissiontype/available/' + query
  return (await ax.get(url)).data
}

export async function fetchGroups(): Promise<Group[]> {
  const url = '/api/group/'
  return (await ax.get(url)).data
}

export async function deleteSolutionComment (pk: number): Promise<AxiosResponse<void>> {
  const url = `/api/solution-comment/${pk}/`
  return ax.delete(url)
}

export async function createSolutionComment(comment: Partial<SolutionComment>): Promise<SolutionComment> {
  const url = '/api/solution-comment/'
  return (await ax.post(url, comment)).data
}

export async function patchSolutionComment(comment: Partial<SolutionComment>): Promise<SolutionComment> {
  const url = `/api/solution-comment/${comment.pk}/`
  return (await ax.patch(url, comment)).data
}

export async function fetchAllAssignments (): Promise<Array<Assignment>> {
  const url = '/api/assignment/'
  return (await ax.get(url)).data
}

export async function fetchActiveAssignments (): Promise<Assignment[]> {
  const url = '/api/assignment/active/'
  return (await ax.get(url)).data
}

export async function deleteAssignment ({ assignment }: {assignment: Assignment}): Promise<AxiosResponse<void>> {
  const url = `/api/assignment/${assignment.pk}/`
  return ax.delete(url)
}

export async function deleteAllActiveAssignments () {
  const url = '/api/assignment/active/'
  return ax.delete(url)
}

export async function deleteComment (comment: FeedbackComment): Promise<AxiosResponse<void>> {
  const url = `/api/feedback-comment/${comment.pk}/`
  return ax.delete(url)
}

export async function activateAllStudentAccess (): Promise<AxiosResponse<void>> {
  return ax.post('/api/student/activate/')
}

export async function deactivateAllStudentAccess (): Promise<AxiosResponse<void>> {
  return ax.post('/api/student/deactivate/')
}

export async function changePassword (userPk: string, data: {password: string}): Promise<UserAccount> {
  return (await ax.patch(`/api/user/${userPk}/change_password/`, data)).data
}

export async function getOwnUser (): Promise<UserAccount> {
  return (await ax.get('/api/user/me/')).data
}

export async function changeActiveForUser (userPk: string, active: boolean): Promise<UserAccount> {
  return (await ax.patch(`/api/user/${userPk}/change_active/`, { 'is_active': active })).data
}

export async function getLabels (): Promise<FeedbackLabel[]> {
  return (await ax.get('/api/label/')).data
}

export async function createLabel (payload: Partial<FeedbackLabel>) {
  return (await ax.post('/api/label/', payload)).data
}

export async function updateLabel (payload: FeedbackLabel) {
  return (await ax.put('/api/label/' + payload.pk + '/', payload)).data
}

export async function fetchSubmissionCounts () {
  return (await ax.get('/api/submissiontype/available_counts/')).data
}

export async function releaseUndoneAssignments () {
  return (await ax.delete('/api/assignment/release')).data
}

export async function patchInstanceSettings(config: { [config: string]: boolean }) {
  return (await ax.patch('/api/config/change_config/', config)).data
}

/**
 * Issues a synchronized request to release all undone assignments of given user
 * @param accessToken The access token to authenticate against the backend
 */
export function releaseUndoneAssignmentsSynchronized (accessToken: string) {
  var request = new XMLHttpRequest()
  request.open('DELETE', getInstanceBaseUrl() + 'api/assignment/release/', false)
  request.setRequestHeader('Authorization', 'JWT ' + accessToken)
  request.send()
}

/**
 * Issues a synchronized request to disable the account for the given details
 * @param accessToken The access token to authenticate against the backend
 * @param userPk The pk of the user account to disable
 */
export function disableAccount (accessToken: string, userPk: string) {
  var request = new XMLHttpRequest()
  request.open('PATCH', getInstanceBaseUrl() + `api/user/${userPk}/change_active/`, false)
  request.setRequestHeader('Authorization', 'JWT ' + accessToken)
  request.setRequestHeader('Content-Type', 'application/json')
  request.send('{ "is_active": false }')
}

export async function fetchReleases () {
  const id = 'j.michal%2Fgrady'
  const url = `https://gitlab.gwdg.de/api/v4/projects/${id}/releases`
  return (await ax.get(url)).data as GitlabRelease[]
}

export interface StudentExportOptions { setPasswords?: boolean }
export interface StudentExportItem {
  Matrikel: string,
  Name: string,
  Username: string,
  Sum: number,
  Exam: string,
  Password: string,
  Email: string,
  Scores: { type: string, score: number }[]
}
export async function fetchStudentExportData (options: StudentExportOptions): Promise<StudentExportItem[]> {
  return (await ax.post('/api/export/json/', options)).data
}

export async function importData (data: Object): Promise<AxiosResponse<void>> {
  return ax.post('/api/import/', data)
}

// Note, this interface does not represent all of the returned data,
// but only the fields which have to be transformed for deanonymisation
export interface InstanceExportData {
  students: {
    name: string,
    matrikelNo: string
  }[]
}
export async function fetchInstanceExportData (): Promise<InstanceExportData> {
  return (await ax.get('/api/instance/export')).data
}

ax.interceptors.response.use(undefined, errorInterceptor)

export default ax