<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: [], customers: [], projects: [] });
const pendingRemovedIds = ref([]);

const typeOptions = [
  { label: "Internal", value: "internal" },
  { label: "Customer", value: "customer" },
  { 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 hasInternalCategories = computed(
  () => Array.isArray(registry.value.internalCategories) && registry.value.internalCategories.length > 0,
);
const hasCustomers = computed(
  () => Array.isArray(registry.value.customers) && registry.value.customers.length > 0,
);
const hasProjects = computed(
  () => Array.isArray(registry.value.projects) && registry.value.projects.length > 0,
);
const hasAnyHourTarget = computed(
  () => hasInternalCategories.value || hasCustomers.value || hasProjects.value,
);
const registryHelpVisible = computed(() => !loading.value && !hasAnyHourTarget.value);
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 base = {
      id: ln.id ?? undefined,
      registration_type: ln.registration_type,
      internal_category_id: null,
      hour_project_id: null,
      customer_id: null,
      hour_task_id: null,
    };
    switch (ln.registration_type) {
      case "internal":
        base.internal_category_id = ln.internal_category_id;
        break;
      case "customer":
        base.customer_id = ln.customer_id;
        break;
      case "project":
        base.hour_project_id = ln.hour_project_id;
        break;
      case "task":
        base.hour_task_id = ln.hour_task_id;
        break;
      default:
        break;
    }
    WEEKDAY_KEYS.forEach((d) => {
      base[`duration_${d}`] = Number(ln.durations[d]) || 0;
    });
    return base;
  });
}

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() {
  if (!hasInternalCategories.value) return;
  const id = registry.value.internalCategories?.[0]?.id;
  if (!id) {
    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 addCustomerRow() {
  if (!hasCustomers.value) return;
  const id = registry.value.customers?.[0]?.id;
  if (!id) {
    return;
  }
  lines.value.push({
    registration_type: "customer",
    internal_category_id: null,
    hour_project_id: null,
    customer_id: id,
    hour_task_id: null,
    durations: blankDurations(),
  });
}

function addProjectRow() {
  if (!hasProjects.value) return;
  const id = registry.value.projects?.[0]?.id;
  if (!id) {
    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>
    <Message v-if="registryHelpVisible" severity="info" class="mb-4" :closable="false">
      Hour registration has no targets yet. Configure at least one of: active internal categories, active CRM customers, or
      hour projects — e.g. run
      <code class="text-xs px-1">php artisan db:seed</code>
      (includes hour registration seeders), or ask an administrator to set up reference data.
    </Message>
    <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 || !hasInternalCategories"
          @click="addInternalRow"
        />
        <Button
          type="button"
          outlined
          size="small"
          icon="pi pi-plus"
          label="Add customer"
          :disabled="weekLocked || !hasCustomers"
          @click="addCustomerRow"
        />
        <Button
          type="button"
          outlined
          size="small"
          icon="pi pi-plus"
          label="Add project"
          :disabled="weekLocked || !hasProjects"
          @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 === 'customer'"
                v-model="line.customer_id"
                class="w-full"
                option-label="label"
                option-value="value"
                :disabled="weekLocked"
                :options="
                  registry.customers.map((c) => ({
                    label: c.name + ' (#' + c.id + ')',
                    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>
