<script setup lang="ts">
import type PocketBase from "pocketbase";
import Button from "primevue/button";
import Column from "primevue/column";
import DataTable, { type DataTableRowEditSaveEvent } from "primevue/datatable";
import DatePicker from "primevue/datepicker";
import InputText from "primevue/inputtext";
import Select from "primevue/select";
import Tag from "primevue/tag";
import type { Entry, EntryDatabase, Project } from "src/types";
import { computed, inject, onMounted, ref, toRef, watch } from "vue";

// biome-ignore lint/suspicious/noExplicitAny: the whole idea is to assert any type
function assertType(variable: any, typeNames: string[]) {
  const observedType = typeof variable;

  for (const typeName of typeNames) {
    if (observedType === typeName) {
      return;
    }
  }

  throw new Error(`expected ${typeNames.join("|")}, observed ${observedType}`);
}

const pb = inject("pb") as PocketBase;
const props = defineProps<{
  org: string;
  date: Date;
  projects: Project[];
}>();

const editingRows = ref([]);
const entries = ref<Entry[]>([]);
const newEntrySelection = ref("");
const watchDate = toRef(() => props.date);
const watchOrganisation = toRef(() => props.org);

watch(watchDate, () => {
  getEntries().then();
});

watch(watchOrganisation, () => {
  getEntries().then();
});

onMounted(async () => {
  await getEntries();
});

const summaries = computed(() => {
  const e = entries.value.filter((el) => {
    return el.end !== null && el.end.getTime() > el.start.getTime() && el.project !== "";
  });

  const minutes: { [key: string]: number } = {};

  for (const el of e) {
    if (el.end !== null) {
      const pid = el.project;
      if (!(pid in minutes)) {
        minutes[pid] = 0;
      }
      const end = new Date(el.end).getTime();
      const start = new Date(el.start).getTime();
      const elapsed = Math.round((end - start) / (1000 * 60));
      minutes[pid] = minutes[pid] + elapsed;
    }
  }

  const result: { key: string; start: Date; end: Date }[] = [];
  let cumulative_sum = 0;
  for (const key in minutes) {
    const start = new Date(props.date.getTime());
    start.setHours(8, 0, 0);
    start.setMinutes(start.getMinutes() + cumulative_sum);
    cumulative_sum += minutes[key];
    const end = new Date(props.date.getTime());
    end.setHours(8, 0, 0);
    end.setMinutes(end.getMinutes() + cumulative_sum);
    result.push({ key, start, end });
  }

  return result;
});

const isEditorActive = computed(() => {
  return editingRows.value.length > 0;
});

async function getEntries() {
  entries.value = [];

  const begin = new Date(props.date.getTime());
  begin.setHours(0, 0, 0);

  const end = new Date(props.date.getTime());
  end.setHours(23, 59, 59);

  const filter = pb.filter(
    "{:begin} <= start && start <= {:end} && project.organisation = {:organisation}",
    { begin, end, organisation: props.org },
  );

  const recordsDatabase = await pb.collection<EntryDatabase>("entries").getFullList(500, {
    filter,
    sort: "start",
  });

  const records = recordsDatabase.map((el) => {
    return {
      id: el.id,
      project: el.project,
      start: new Date(el.start),
      end: el.end === "" ? null : new Date(el.end),
      description: el.description,
    };
  });

  entries.value = [...records];
}

async function endNow(index: number) {
  const end = new Date(entries.value[index].start);
  const now = new Date();

  end.setHours(now.getHours());
  end.setSeconds(0, 0);

  const minutes = now.getMinutes();

  if (minutes > 0 && minutes < 30) {
    end.setMinutes(30);
  } else if (minutes > 30 && minutes < 60) {
    end.setMinutes(60);
  }

  entries.value[index].end = end;

  if (entries.value[index].id !== "") {
    await pb.collection<Entry>("entries").update(entries.value[index].id, { end });
  }
}

function getDateFmt(date: Date | null): string {
  assertType(date, ["object", "null"]);

  if (date === null) {
    return "";
  }

  const year = date.getFullYear();
  const month = `0${date.getMonth()}`.slice(-2);
  const date_str = date.getDate();
  const hour = `0${date.getHours()}`.slice(-2);
  const minute = `0${date.getMinutes()}`.slice(-2);

  return `${date_str}/${month}/${year} ${hour}:${minute}`;
}

async function save(event: DataTableRowEditSaveEvent) {
  const { newData, index } = event;

  const bodyParams = {
    project: newData.project,
    start: newData.start,
    end: newData.end ?? "",
    description: newData.description,
    user: newData.user,
  };

  if (newData.id === "") {
    const entry = await pb.collection<Entry>("entries").create(bodyParams);
    newData.id = entry.id;
  } else {
    await pb.collection<Entry>("entries").update(newData.id, bodyParams);
  }

  entries.value[index] = newData;
}

async function deleteEntry(index: number, entry: Entry) {
  if (confirm("Confirm entry deletion")) {
    if (entry.id !== "") {
      await pb.collection<Entry>("entries").delete(entry.id);
    }
    entries.value.splice(index, 1);
  }
}

async function newEntry() {
  const start = new Date(props.date.getTime());
  start.setSeconds(0, 0);

  const entry = {
    id: "",
    project: newEntrySelection.value,
    start,
    end: null,
    description: "",
    user: pb.authStore.model !== null ? pb.authStore.model.id : "",
  };

  entries.value.push(entry);
}

function getProjectById(id: string): Project {
  const item = props.projects.find((p) => p.id === id);
  if (item !== undefined) {
    return item;
  }
  throw Error(`unknown project id ${id}`);
}

function projectTitleFormat(project: Project, titleLimit: number): string {
  if (project.name.length > 3) {
    const name = project.name;
    const trimmedName = name.length > titleLimit ? `${name.slice(0, titleLimit - 3)}...` : name;
    const title = `(${project.identifier}) ${trimmedName}`;
    return title;
  }

  return project.name;
}
</script>

<template>
    <DataTable
      :value="entries"
      edit-mode="row"
      v-model:editingRows="editingRows"
      data-key="id"
      @row-edit-save="save"
    >
        <template #empty>
            No entries
        </template>
        <Column :row-editor="true" :exportable="false"></Column>
        <Column field="project">
            <template #body="slotProps">
                {{ projectTitleFormat(getProjectById(slotProps.data.project), 10) }}
            </template>
            <template #header>
                <Select
                    :options="projects"
                    :option-label="(item: Project) => `${item.name} (${item.identifier})`"
                    v-model="newEntrySelection"
                    optionValue="id"
                    placeholder="Add"
                    @change="newEntry"
                />
            </template>
            <template #editor="{ data, field }">
                <Select
                    :options="projects"
                    :option-label="(item: Project) => `${item.name} (${item.identifier})`"
                    optionValue="id"
                    v-model="data[field]"
                    placeholder="Project"
                />
            </template>
        </Column>
        <Column field="start" header="Start">
            <template #body="slotProps">
                {{ getDateFmt(slotProps.data.start) }}
            </template>
            <template #editor="{ data, field }">
                <DatePicker v-model="data[field]" showTime style="min-width: 100px;" />
            </template>
        </Column>
        <Column field="end" header="End">
            <template #body="slotProps">
                {{ getDateFmt(slotProps.data.end) }}
            </template>
            <template #editor="{ data, field }">
                <DatePicker v-if="data[field]" v-model="data[field]" showTime style="min-width: 100px;" />
            </template>
        </Column>
        <Column field="description" header="Description">
            <template #editor="{ data, field }">
                <InputText v-model="data[field]" />
            </template>
        </Column>
        <Column header="" :exportable="false">
            <template #body="slotProps">
                <Button label="End Now" text @click="endNow(slotProps.index)" v-if="!isEditorActive" />
                <Button label="Delete" text @click="deleteEntry(slotProps.index, slotProps.data)" />
            </template>
        </Column>
        <Column header="" :exportable="false">
            <template #body="slotProps">
                <div style="display: flex; justify-content: center; align-items: center;">
                    <Tag severity="danger" value="Unsaved" v-if="slotProps.data.id === ''"></Tag>
                </div>
            </template>
        </Column>
    </DataTable>
    
    <DataTable v-if="summaries.length > 0" :value="summaries">
        <Column header="Project">
            <template #body="slotProps">
                {{ projectTitleFormat(getProjectById(slotProps.data.key), 60) }}
            </template>
        </Column>
        <Column header="Start">
            <template #body="slotProps">
                {{ getDateFmt(slotProps.data.start) }}
            </template>
        </Column>
        <Column header="End">
            <template #body="slotProps">
                {{ getDateFmt(slotProps.data.end) }}
            </template>
        </Column>
    </DataTable>
</template>

