import { GetterTree, MutationTree, ActionTree } from 'vuex'
import {
  CREATE_PROJECT_ENDPOINT,
  CREATE_REPEATABLE_TASK_ENDPOINT,
  CREATE_TASK_ENDPOINT,
  DUPLICATE_PROJECT_ENDPOINT,
  GET_ALL_PROJECTS_ENDPOINT,
  GET_ALL_TASKS_ENDPOINT,
  GET_SINGLE_PROJECT_ENDPOINT,
  GET_SINGLE_REPEATABLE_TASK_ENDPOINT,
  GET_SINGLE_TASK_ENDPOINT,
  Project,
  RepeatableTask,
  Task,
  TaskCreationFormType,
  taskIncludesUser,
  UPDATE_PROJECT_ENDPOINT,
  UPDATE_REPEATABLE_TASK_ENDPOINT,
  UPDATE_TASK_ENDPOINT,
} from '~/types/tasks'

export const state = () => ({
  tasks: [] as Task[],
  repeatableTasks: [] as RepeatableTask[],
  projects: [] as Project[],
  currentlyOpenProject: null as Project | null,
})

export type RootState = ReturnType<typeof state>

export const getters: GetterTree<RootState, RootState> = {
  tasks: state => state.tasks,
  repeatableTasks: state => state.repeatableTasks,
  taskById: state => (id: number) => {
    return state.tasks.find((task: Task) => task.id === id)
  },
  tasksDueToday: state => {
    return state.tasks.filter((task: Task) => {
      if (!task.dueDate) return false

      const today = new Date()
      const dueDate = new Date(task.dueDate)
      return (
        dueDate.getDate() === today.getDate() &&
        dueDate.getMonth() === today.getMonth() &&
        dueDate.getFullYear() === today.getFullYear()
      )
    })
  },
  openTasks: state => {
    return state.tasks.filter((task: Task) => !task.completed)
  },
  openTasksForUser: state => (userId: number) => {
    return state.tasks.filter(
      (task: Task) => !task.completed && taskIncludesUser(task, userId),
    )
  },
  completedTasks: state => {
    return state.tasks.filter((task: Task) => task.completed)
  },
  completedTasksForUser: state => (userId: number) => {
    return state.tasks.filter(
      (task: Task) => task.completed && taskIncludesUser(task, userId),
    )
  },
  repeatableTasksForUser: state => (userId: number) => {
    return state.repeatableTasks.filter((task: RepeatableTask) =>
      taskIncludesUser(task, userId),
    )
  },
  tasksByProject: state => (projectId: number) => {
    return state.tasks.filter((task: Task) => task.projectId === projectId)
  },
  openTasksByProject: state => (projectId: number) => {
    return (
      state.tasks?.filter(
        (task: Task) => !task.completed && task.projectId === projectId,
      ) ?? ([] as Project[])
    )
  },
  completedTasksByProject: state => (projectId: number) => {
    return (
      state.tasks?.filter(
        (task: Task) => task.completed && task.projectId === projectId,
      ) ?? ([] as Project[])
    )
  },
  completedTasksByProjectPercentage: state => (projectId: number) => {
    const tasks =
      state.tasks.filter((task: Task) => task.projectId === projectId) ??
      ([] as Project[])
    const completedTasks = tasks.filter((task: Task) => task.completed)
    return (completedTasks.length / (tasks.length ?? 1)) * 100 ?? 0
  },
  projects: state => state.projects,
  project: state => state.currentlyOpenProject,
  // TODO: Add projectByTask getter
}

export const mutations: MutationTree<RootState> = {
  ADD_TASK(state: RootState, task: Task) {
    state.tasks.push(task)
  },
  ADD_TASKS(state: RootState, tasks: Task[]) {
    state.tasks.push(...tasks)
  },
  SET_TASKS(state: RootState, tasks: Task[]) {
    state.tasks = tasks
  },
  SET_REPEATABLE_TASKS(state: RootState, tasks: RepeatableTask[]) {
    state.repeatableTasks = tasks
  },
  REMOVE_TASK(state: RootState, task: Task) {
    state.tasks.splice(state.tasks.indexOf(task), 1)
  },
  REMOVE_REPEATABLE_TASK(state: RootState, task: RepeatableTask) {
    state.repeatableTasks.splice(state.repeatableTasks.indexOf(task), 1)
  },
  REPLACE_TASK(state: RootState, task: Task) {
    const index = state.tasks.findIndex((t: Task) => t.id === task.id)
    state.tasks.splice(index, 1, task)
  },
  REPLACE_REPEATABLE_TASK(state: RootState, task: RepeatableTask) {
    const index = state.repeatableTasks.findIndex(
      (t: RepeatableTask) => t.id === task.id,
    )
    state.repeatableTasks.splice(index, 1, task)
  },
  ADD_PROJECT(state: RootState, project: Project) {
    state.projects.push(project)
  },
  ADD_PROJECTS(state: RootState, projects: Project[]) {
    state.projects.push(...projects)
  },
  SET_PROJECTS(state: RootState, projects: Project[]) {
    state.projects = projects
  },
  REMOVE_PROJECT(state: RootState, project: Project) {
    state.projects.splice(state.projects.indexOf(project), 1)
  },
  REPLACE_PROJECT(state: RootState, project: Project) {
    const index = state.projects.findIndex((t: Project) => t.id === project.id)
    state.projects.splice(index, 1, project)
  },
  SET_CURRENTLY_OPEN_PROJECT(state: RootState, project: Project) {
    state.currentlyOpenProject = project
  },
}

export const actions: ActionTree<RootState, RootState> = {
  async fetchTasks({ commit }) {
    const tasks = await this.$axios.$get(GET_ALL_TASKS_ENDPOINT)
    const repeatableTasks = await this.$axios.$get(
      '/dashboard/api/v1/projects/tasks/repeatable',
    )

    const sortByDueDate = (a: Task, b: Task) => {
      if (a.dueDate && b.dueDate) {
        return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime()
      } else if (a.dueDate) {
        return -1
      } else if (b.dueDate) {
        return 1
      } else {
        return 0
      }
    }

    const nonCompletedTasks = tasks.filter((task: Task) => !task.completed)
    const completedTasks = tasks.filter((task: Task) => task.completed)

    nonCompletedTasks.sort(sortByDueDate)
    completedTasks.sort((a: Task, b: Task) => sortByDueDate(b, a))

    commit('SET_TASKS', [...nonCompletedTasks, ...completedTasks])
    commit(
      'SET_REPEATABLE_TASKS',
      repeatableTasks.sort((a: RepeatableTask, b: RepeatableTask) => {
        // Sort by due date
        if (a.next && b.next) {
          return new Date(a.next).getTime() - new Date(b.next).getTime()
        } else if (a.next) {
          return -1
        } else if (b.next) {
          return 1
        } else {
          return 0
        }
      }),
    )
  },
  async createTask({ commit }, taskCreationForm: TaskCreationFormType) {
    const newTask = await this.$axios.$post(
      CREATE_TASK_ENDPOINT,
      taskCreationForm,
    )

    commit('ADD_TASK', newTask)

    return newTask
  },
  async createRepeatableTask(
    { commit },
    taskCreationForm: TaskCreationFormType,
  ) {
    const newTask = await this.$axios.$post(
      CREATE_REPEATABLE_TASK_ENDPOINT,
      taskCreationForm,
    )

    commit('ADD_TASK', newTask)

    return newTask
  },
  async updateTask({ commit }, form: { changes: any; task: Task }) {
    if (!form.task?.id) {
      console.log('No task provided')
      return
    }

    const updatedTask = await this.$axios.$put(
      UPDATE_TASK_ENDPOINT(form.task.id),
      form.changes,
    )

    if (updatedTask.projectId === this.state.currentlyOpenProject?.id) {
      this.dispatch('fetchProjects')

      const project = await this.$axios.$get(
        GET_SINGLE_PROJECT_ENDPOINT(updatedTask.projectId),
      )
      commit('SET_CURRENTLY_OPEN_PROJECT', project)
    }

    commit('REPLACE_TASK', updatedTask)
  },
  async updateRepeatableTask(
    { commit },
    form: { changes: any; task: RepeatableTask },
  ) {
    if (!form.task?.id) {
      console.log('No task provided')
      return
    }

    const updatedTask = await this.$axios.$put(
      UPDATE_REPEATABLE_TASK_ENDPOINT(form.task.id),
      form.changes,
    )

    commit('REPLACE_REPEATABLE_TASK', updatedTask)
  },
  async toggleTaskCompletion({ commit }, task: Task) {
    const updatedTask = await this.$axios.$put(UPDATE_TASK_ENDPOINT(task.id), {
      completed: task.completed,
    })
    commit('REPLACE_TASK', updatedTask)
  },
  async fetchProjects({ commit }) {
    const projects: Project[] = await this.$axios.$get(
      GET_ALL_PROJECTS_ENDPOINT,
    )

    // const sortByDueDate = (a: Task, b: Task) => {
    //   if (a.dueDate && b.dueDate) {
    //     return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime()
    //   } else if (a.dueDate) {
    //     return -1
    //   } else if (b.dueDate) {
    //     return 1
    //   } else {
    //     return 0
    //   }
    // }

    // for (const project of projects) {
    //   const nonCompletedTasks = project.tasks.filter(
    //     (task: Task) => !task.completed,
    //   )
    //   const completedTasks = project.tasks.filter(
    //     (task: Task) => task.completed,
    //   )

    //   nonCompletedTasks.sort(sortByDueDate)
    //   completedTasks.sort((a: Task, b: Task) => sortByDueDate(b, a))

    //   project.tasks = [...nonCompletedTasks, ...completedTasks]
    // }

    commit('SET_PROJECTS', projects)
  },
  async fetchProject({ commit }, projectId: number) {
    const project: Project = await this.$axios.$get(
      GET_SINGLE_PROJECT_ENDPOINT(projectId),
    )
    commit('SET_CURRENTLY_OPEN_PROJECT', project)
  },
  async updateProject({ commit }, project: Project) {
    const updatedProject = await this.$axios.$put(
      UPDATE_PROJECT_ENDPOINT(project.id),
      project,
    )
    commit('REPLACE_PROJECT', updatedProject)
  },
  async updateProjectForm(
    { commit },
    form: { changes: any; project: Project },
  ) {
    const updatedProject = await this.$axios.$put(
      UPDATE_PROJECT_ENDPOINT(form.project.id),
      form.changes,
    )
    commit('REPLACE_PROJECT', updatedProject)
    if (updatedProject.id === this.state.currentlyOpenProject?.id) {
      commit('SET_CURRENTLY_OPEN_PROJECT', updatedProject)
    }
  },
  async createProject({ commit }, projectCreationForm: any) {
    const newProject = await this.$axios.$post(
      CREATE_PROJECT_ENDPOINT,
      projectCreationForm,
    )

    commit('ADD_PROJECT', newProject)
    return newProject
  },
  async deleteProject({ commit }, projectId: number) {
    await this.$axios.$delete(GET_SINGLE_PROJECT_ENDPOINT(projectId))
    commit('REMOVE_PROJECT', projectId)
  },
  async duplicateProject(
    { commit },
    form: { project: Project; title: string },
  ) {
    const newProject = await this.$axios.$post(DUPLICATE_PROJECT_ENDPOINT, {
      projectId: form.project.id,
      title: form.title,
    })

    commit('ADD_PROJECT', newProject)
    return newProject
  },
  async deleteTask({ commit }, taskId: number) {
    await this.$axios.$delete(GET_SINGLE_TASK_ENDPOINT(taskId))
    commit('REMOVE_TASK', taskId)
  },
  async deleteRepeatableTask({ commit }, taskId: number) {
    await this.$axios.$delete(GET_SINGLE_REPEATABLE_TASK_ENDPOINT(taskId))
    commit('REMOVE_REPEATABLE_TASK', taskId)
  },
}
