<script setup>
import { ref, computed, onMounted } from "vue";
import Button from "primevue/button";
import InputNumber from "primevue/inputnumber";
import Dropdown from "primevue/dropdown";
import Message from "primevue/message";
import { useToast } from "primevue/usetoast";
import { apiErrorDetail } from "../../../services/api.js";
import {
  WEEKDAYS as WEEKDAY_KEYS,
  fetchCurrentWeek,
  syncWeekLines,
  submitWeek,
  fetchRegistry,
} from "../composables/useHoursApi.js";
import strings from "../strings.js";

const toast = useToast();
const loading = ref(true);
const saving = ref(false);
const week = ref(null);
const lines = ref([]);
const warnings = ref([]);
const dismissedWarn = ref({});
const registry = ref({ internalCategories: [], projects: [] });
const pendingRemovedIds = ref([]);

const typeOptions = [
  { label: "Internal", value: "internal" },
  { label: "Project", value: "project" },
];
const weekdayLabels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

const weekStartIso = computed(() => week.value?.week_start ?? "");
const weekLocked = computed(() => week.value?.workflow_status === "approved");
const visibleWarnings = computed(() =>
  warnings.value.filter((w) => !dismissedWarn.value[w]),
);

onMounted(() => bootstrap());

async function bootstrap(startIso) {
  loading.value = true;
  warnings.value = [];
  dismissedWarn.value = {};
  try {
    registry.value = await fetchRegistry();
    await loadWeek(startIso);
  } catch (e) {
    toast.add({
      severity: "error",
      summary: strings.weeklyTitle,
      detail: apiErrorDetail(e),
      life: 7000,
    });
  } finally {
    loading.value = false;
  }
}

async function loadWeek(isoMaybe) {
  const res = await fetchCurrentWeek(isoMaybe ?? undefined);
  week.value = res;
  pendingRemovedIds.value = [];
  const raw = Array.isArray(res?.lines) ? res.lines : [];
  lines.value = raw.map((ln) => {
    const durations = {};
    WEEKDAY_KEYS.forEach((d) => {
      const v = ln?.durations?.[d] ?? ln?.[`duration_${d}`] ?? 0;
      durations[d] = typeof v === "number" ? v : Number(v) || 0;
    });
    return {
      ...ln,
      registration_type: ln.registration_type ?? "internal",
      internal_category_id: ln.internal_category_id ?? null,
      hour_project_id: ln.hour_project_id ?? null,
      customer_id: ln.customer_id ?? null,
      hour_task_id: ln.hour_task_id ?? null,
      durations,
    };
  });
}

function normalizedLinesPayload() {
  return lines.value.map((ln) => {
    const out = {
      id: ln.id ?? undefined,
      registration_type: ln.registration_type,
      internal_category_id: ln.internal_category_id,
      hour_project_id: ln.hour_project_id,
      customer_id: ln.customer_id,
      hour_task_id: ln.hour_task_id,
    };
    WEEKDAY_KEYS.forEach((d) => {
      out[`duration_${d}`] = Number(ln.durations[d]) || 0;
    });
    return out;
  });
}

async function persistLines(okToast) {
  if (!week.value?.id) return;
  saving.value = true;
  try {
    await syncWeekLines(
      week.value.id,
      normalizedLinesPayload(),
      [...pendingRemovedIds.value],
    );
    pendingRemovedIds.value = [];
    if (okToast) {
      toast.add({
        severity: "success",
        summary: strings.save,
        detail: "Saved.",
        life: 4000,
      });
    }
    await loadWeek(weekStartIso.value);
  } catch (e) {
    toast.add({
      severity: "error",
      summary: strings.save,
      detail: apiErrorDetail(e),
      life: 7000,
    });
  } finally {
    saving.value = false;
  }
}

async function submit() {
  if (!week.value?.id || weekLocked.value) return;
  saving.value = true;
  try {
    await persistLines(false);
    const result = await submitWeek(week.value.id);
    warnings.value = Array.isArray(result.warnings) ? result.warnings : [];
    dismissedWarn.value = {};
    toast.add({
      severity: "success",
      summary: strings.submit,
      detail: "Week submitted.",
      life: 5200,
    });
    await loadWeek(weekStartIso.value);
  } catch (e) {
    toast.add({
      severity: "error",
      summary: strings.submit,
      detail: apiErrorDetail(e),
      life: 7000,
    });
  } finally {
    saving.value = false;
  }
}

function shiftWeek(dir) {
  if (!weekStartIso.value) return;
  const d = new Date(weekStartIso.value + "T00:00:00Z");
  d.setUTCDate(d.getUTCDate() + dir * 7);
  bootstrap(d.toISOString().slice(0, 10));
}

function blankDurations() {
  return WEEKDAY_KEYS.reduce((acc, k) => ({ ...acc, [k]: 0 }), {});
}

function addInternalRow() {
  const id = registry.value.internalCategories?.[0]?.id;
  if (!id) {
    toast.add({
      severity: "warn",
      summary: strings.weeklyTitle,
      detail: "No internal categories available.",
      life: 4000,
    });
    return;
  }
  lines.value.push({
    registration_type: "internal",
    internal_category_id: id,
    hour_project_id: null,
    customer_id: null,
    hour_task_id: null,
    durations: blankDurations(),
  });
}

function addProjectRow() {
  const id = registry.value.projects?.[0]?.id;
  if (!id) {
    toast.add({
      severity: "warn",
      summary: strings.weeklyTitle,
      detail: "No hour projects configured (admin POST /api/hours/registry/projects).",
      life: 6000,
    });
    return;
  }
  lines.value.push({
    registration_type: "project",
    hour_project_id: id,
    internal_category_id: null,
    customer_id: null,
    hour_task_id: null,
    durations: blankDurations(),
  });
}

function removeRow(idx) {
  const ln = lines.value[idx];
  if (!ln || weekLocked.value) return;
  if (ln.id) pendingRemovedIds.value.push(Number(ln.id));
  lines.value.splice(idx, 1);
}

function dismissWarning(key) {
  dismissedWarn.value = { ...dismissedWarn.value, [key]: true };
}
</script>

<template>
  <div class="p-4">
    <div class="flex flex-wrap items-center gap-3 mb-4">
      <h1 class="text-2xl font-semibold">{{ strings.weeklyTitle }}</h1>
      <span v-if="weekStartIso" class="text-muted-color">{{ weekStartIso }}</span>
      <Button type="button" outlined size="small" icon="pi pi-angle-left" :label="strings.prevWeek" @click="shiftWeek(-1)" />
      <Button type="button" outlined size="small" icon-pos="right" icon="pi pi-angle-right" :label="strings.nextWeek" @click="shiftWeek(1)" />
      <div class="flex-grow min-w-[1rem]" />
      <Button type="button" outlined size="small" :label="strings.save" :disabled="loading || weekLocked" :loading="saving" @click="persistLines(true)" />
      <Button type="button" size="small" :label="strings.submit" :disabled="loading || weekLocked" :loading="saving" @click="submit" />
    </div>
    <div v-if="visibleWarnings.length" class="flex flex-column gap-2 mb-4">
      <Message v-for="w in visibleWarnings" :key="w" severity="warn" closable @close="dismissWarning(w)">{{ w }}</Message>
    </div>
    <div v-if="loading" class="py-8">Loading…</div>
    <div v-else class="surface-card border-round-lg shadow-2 p-3 overflow-auto">
      <div class="flex gap-2 mb-4">
        <Button type="button" outlined size="small" icon="pi pi-plus" label="Add internal" :disabled="weekLocked" @click="addInternalRow" />
        <Button type="button" outlined size="small" icon="pi pi-plus" label="Add project" :disabled="weekLocked" @click="addProjectRow" />
      </div>
      <table class="w-full text-sm">
        <thead>
          <tr>
            <th class="text-left p-2">Target</th>
            <th v-for="(lbl, ix) in weekdayLabels" :key="'h-' + lbl + ix" class="text-center p-2">{{ lbl }}</th>
            <th class="w-10" />
          </tr>
        </thead>
        <tbody>
          <tr v-for="(line, idx) in lines" :key="line.id ?? 'n-' + idx">
            <td class="align-top p-2 md:w-[16rem]">
              <Dropdown
                v-model="line.registration_type"
                class="mb-2 w-full"
                option-label="label"
                option-value="value"
                :options="typeOptions"
                :disabled="weekLocked"
              />
              <Dropdown
                v-if="line.registration_type === 'internal'"
                v-model="line.internal_category_id"
                class="w-full"
                option-label="label"
                option-value="value"
                :disabled="weekLocked"
                :options="
                  registry.internalCategories.map((c) => ({
                    label: c.name,
                    value: c.id,
                  }))
                "
              />
              <Dropdown
                v-if="line.registration_type === 'project'"
                v-model="line.hour_project_id"
                class="w-full"
                option-label="label"
                option-value="value"
                :disabled="weekLocked"
                :options="
                  registry.projects.map((p) => ({
                    label: p.name + ' (#' + p.id + ')',
                    value: p.id,
                  }))
                "
              />
            </td>
            <td v-for="d in WEEKDAY_KEYS" :key="'c-' + d + idx" class="p-1">
              <InputNumber
                v-model="line.durations[d]"
                class="max-w-[5.75rem]"
                suffix=" h"
                locale="en-US"
                mode="decimal"
                :min="0"
                :max-fraction-digits="2"
                :disabled="weekLocked"
              />
            </td>
            <td class="p-2 text-center">
              <Button type="button" outlined size="small" severity="danger" icon="pi pi-times" :disabled="weekLocked" @click="removeRow(idx)" />
            </td>
          </tr>
        </tbody>
      </table>
      <p v-if="lines.length === 0" class="mt-4 text-muted-color">Add at least one row.</p>
    </div>
  </div>
</template>
