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,
  Subscription,
  Tutor, UserAccount
} from '@/models'

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

let ax: AxiosInstance = axios.create({
  baseURL: getInstanceBaseUrl()
})
{
  let token = window.sessionStorage.getItem('token')
  if (token) {
    ax.defaults.headers['Authorization'] = `JWT ${token}`
  }
}

export async function registerTutor (credentials: Credentials): Promise<AxiosResponse<Tutor>> {
  return ax.post<Tutor>('/api/tutor/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 fetchJWTTimeDelta (): Promise<number> {
  return (await ax.get('/api/jwt-time-delta/')).data.timeDelta
}

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 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/tutor/'
  return (await ax.get(url)).data
}

export async function fetchSubscriptions (): Promise<Array<Subscription>> {
  return (await ax.get('/api/subscription/')).data
}

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

export async function fetchSubscription (subscriptionPk: string): Promise<Subscription> {
  return (await ax.get(`/api/subscription/${subscriptionPk}/`)).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
}

interface SubscriptionCreatePayload {
    queryType: Subscription.QueryTypeEnum
    queryKey?: string
    feedbackStage?: Subscription.FeedbackStageEnum
}

export async function subscribeTo (type: Subscription.QueryTypeEnum,
  key?: string,
  stage?: Subscription.FeedbackStageEnum): Promise<Subscription> {
  let data: SubscriptionCreatePayload = {
    queryType: type
  }

  if (key) {
    data.queryKey = key
  }
  if (stage) {
    data.feedbackStage = stage
  }

  return (await ax.post('/api/subscription/', data)).data
}

export async function createAssignment (
  { subscription = undefined, subscriptionPk = '' }:
  {subscription?: Subscription, subscriptionPk?: string}): Promise<Assignment> {
  const data = {
    subscription: subscription ? subscription.pk : subscriptionPk
  }
  return (await ax.post(`/api/assignment/`, data)).data
}

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

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

export async function fetchSubmissionTypes (): Promise<Array<SubmissionType>> {
  const url = '/api/submissiontype/'
  return (await ax.get(url)).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 patchComment (comment: FeedbackComment): Promise<FeedbackComment> {
  const url = `/api/feedback-comment/${comment.pk}/`
  return (await ax.patch(url, comment)).data
}

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