<script lang="ts">
  import {
    compareYmd,
    todayYmdUTC,
    ymdToDmy,
    isToday,
    isTomorrow,
  } from "$lib/utils/date";
  import { success, error as toastError } from "$lib/stores/toasts";
  import { tick, createEventDispatcher } from "svelte";
  import { fade } from "svelte/transition";
  import { normalizeDigits } from "$lib/utils/phone";
  import { colorForGroup } from "$lib/utils/groupColor";
  import DueOkIcon from "$lib/ui/icons/DueOkIcon.svelte";
  import DueSoonIcon from "$lib/ui/icons/DueSoonIcon.svelte";
  import DueOverdueIcon from "$lib/ui/icons/DueOverdueIcon.svelte";
  import AssigneesIcon from "$lib/ui/icons/AssigneesIcon.svelte";
  import ClaimIcon from "$lib/ui/icons/ClaimIcon.svelte";
  import UnassignIcon from "$lib/ui/icons/UnassignIcon.svelte";
  import EditIcon from "$lib/ui/icons/EditIcon.svelte";
  import CalendarEditIcon from "$lib/ui/icons/CalendarEditIcon.svelte";
  import CheckCircleSuccessIcon from "$lib/ui/icons/CheckCircleSuccessIcon.svelte";
  import TaskDueBadge from "$lib/ui/data/task/TaskDueBadge.svelte";
  import TaskAssignees from "$lib/ui/data/task/TaskAssignees.svelte";

  export let id: number;
  export let description: string;
  export let due_date: string | null = null;
  export let display_code: number | null = null;
  export let assignees: string[] = [];
  export let currentUserId: string | null | undefined = null;
  export let completed: boolean = false;
  export let completed_at: string | null = null;
  export let groupName: string | null = null;
  export let groupId: string | null = null;

  const dispatch = createEventDispatcher<{ changed: { id: number; action: string; patch: any } }>();

  const code = display_code ?? id;
  const codeStr = String(code).padStart(4, "0");
  $: isAssigned =
    !!currentUserId &&
    assignees.some(
      (a) => normalizeDigits(a) === normalizeDigits(currentUserId),
    );
  $: today = todayYmdUTC();
  $: overdue = !!due_date && compareYmd(due_date, today) < 0;
  $: imminent = !!due_date && (isToday(due_date) || isTomorrow(due_date));
  $: dateDmy = due_date ? ymdToDmy(due_date) : "";
  $: groupLabel = groupName != null ? groupName : "Personal";
  $: gc = groupId ? colorForGroup(groupId) : null;

  let editing = false;
  let dateValue: string = due_date ?? "";
  let busy = false;

  // Popover de responsables
  $: assigneesCount = Array.isArray(assignees) ? assignees.length : 0;
  $: canUnassign = !(groupId == null && assigneesCount === 1 && isAssigned);

  // Edición de texto (inline)
  let editingText = false;
  let descEl: HTMLElement | null = null;

  async function doClaim() {
    if (busy) return;
    busy = true;
    try {
      const res = await fetch(`/api/tasks/${id}/claim`, { method: "POST" });
      if (res.ok) {
        // Actualizar estado local (añadirte si no estabas)
        if (currentUserId) {
          const set = new Set<string>(assignees || []);
          set.add(String(currentUserId));
          assignees = Array.from(set);
        }
        success("Tarea reclamada");
        dispatch("changed", { id, action: "claim", patch: { assignees } });
      } else {
        const txt = await res.text();
        toastError(txt || "No se pudo reclamar");
      }
    } catch (e) {
      toastError("Error de red");
    } finally {
      busy = false;
    }
  }

  function toIsoSqlLocal(d: Date = new Date()): string {
    const iso = d.toISOString();
    return iso.substring(0, 23).replace('T', ' ');
  }

  async function doComplete() {
    if (busy || completed) return;
    busy = true;
    try {
      const hadNoAssignees = assigneesCount === 0;
      const res = await fetch(`/api/tasks/${id}/complete`, { method: "POST" });
      if (res.ok) {
        const data = await res.json().catch(() => null);
        const newCompletedAt: string | null = data?.task?.completed_at ? String(data.task.completed_at) : toIsoSqlLocal(new Date());
        // Si no tenía responsables, el backend te auto-asigna: reflejarlo localmente
        if (hadNoAssignees && currentUserId) {
          const set = new Set<string>(assignees || []);
          set.add(String(currentUserId));
          assignees = Array.from(set);
        }
        completed = true;
        completed_at = newCompletedAt;
        success(hadNoAssignees ? "Te has asignado y completado la tarea" : "Tarea completada");
        dispatch("changed", { id, action: "complete", patch: { completed: true, completed_at, assignees } });
      } else {
        const txt = await res.text();
        toastError(txt || "No se pudo completar la tarea");
      }
    } catch {
      toastError("Error de red");
    } finally {
      busy = false;
    }
  }

  async function doUncomplete() {
    if (busy || !completed) return;
    busy = true;
    try {
      const res = await fetch(`/api/tasks/${id}/uncomplete`, { method: "POST" });
      if (res.ok) {
        await res.json().catch(() => null);
        completed = false;
        completed_at = null;
        success("Tarea reabierta");
        dispatch("changed", { id, action: "uncomplete", patch: { completed: false, completed_at: null } });
      } else {
        const txt = await res.text();
        toastError(txt || "No se pudo deshacer completar");
      }
    } catch {
      toastError("Error de red");
    } finally {
      busy = false;
    }
  }

  async function doUnassign() {
    if (busy) return;
    busy = true;
    try {
      const res = await fetch(`/api/tasks/${id}/unassign`, { method: "POST" });
      if (res.ok) {
        if (currentUserId) {
          const after = (assignees || []).filter((a) => normalizeDigits(a) !== normalizeDigits(String(currentUserId)));
          assignees = after;
        }
        success("Asignación eliminada");
        dispatch("changed", { id, action: "unassign", patch: { assignees } });
      } else {
        const txt = await res.text();
        toastError(txt || "No se pudo soltar");
      }
    } catch {
      toastError("Error de red");
    } finally {
      busy = false;
    }
  }

  async function saveDate() {
    if (busy) return;
    busy = true;
    try {
      const body = {
        due_date: dateValue && dateValue.trim() !== "" ? dateValue.trim() : null,
      };
      const res = await fetch(`/api/tasks/${id}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify(body),
      });
      if (res.ok) {
        due_date = body.due_date;
        success("Fecha actualizada");
        dispatch("changed", { id, action: "update_due", patch: { due_date } });
        editing = false;
      } else {
        const txt = await res.text();
        toastError(txt || "No se pudo actualizar la fecha");
      }
    } catch {
      toastError("Error de red");
    } finally {
      busy = false;
    }
  }

  function toggleEdit() {
    editing = !editing;
    if (editing) editingText = false;
    dateValue = due_date ?? "";
  }

  function clearDate() {
    if (busy) return;
    if (!confirm("¿Quitar la fecha de vencimiento?")) return;
    dateValue = "";
    saveDate();
  }

  function toggleEditText() {
    editingText = !editingText;
    if (editingText) {
      editing = false;
      // Asegurar que el elemento refleja el texto actual y enfocarlo
      if (descEl) {
        descEl.textContent = description;
      }
      tick().then(() => {
        if (descEl) {
          descEl.focus();
          placeCaretAtEnd(descEl);
        }
      });
    }
  }

  function placeCaretAtEnd(el: HTMLElement) {
    const range = document.createRange();
    range.selectNodeContents(el);
    range.collapse(false);
    const sel = window.getSelection();
    sel?.removeAllRanges();
    sel?.addRange(range);
  }

  async function saveText() {
    if (busy) return;
    const raw = (descEl?.textContent || "").replace(/\s+/g, " ").trim();
    if (raw.length < 1 || raw.length > 1000) {
      toastError("La descripción debe tener entre 1 y 1000 caracteres.");
      return;
    }
    if (raw === description) {
      editingText = false;
      return;
    }
    busy = true;
    try {
      const res = await fetch(`/api/tasks/${id}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ description: raw }),
      });
      if (res.ok) {
        description = raw;
        success("Descripción actualizada");
        dispatch("changed", { id, action: "update_desc", patch: { description } });
        editingText = false;
      } else {
        const txt = await res.text();
        toastError(txt || "No se pudo actualizar la descripción");
      }
    } catch {
      toastError("Error de red");
    } finally {
      busy = false;
    }
  }

  function cancelText() {
    if (descEl) {
      descEl.textContent = description;
    }
    editingText = false;
  }
</script>

<li class="task" class:completed in:fade={{ duration: 180 }} out:fade={{ duration: 180 }}>
  <div class="code">{codeStr}</div>
  <div
    tabindex="0"
    class="desc"
    class:editing={editingText}
    class:completed
    contenteditable={editingText && !completed}
    role="textbox"
    aria-label="Descripción de la tarea"
    spellcheck="true"
    bind:this={descEl}
    on:keydown={(e) => {
      if (e.key === "Escape") {
        e.preventDefault();
        cancelText();
      } else if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
        e.preventDefault();
        saveText();
      } else if (e.key === "Enter") {
        e.preventDefault();
      }
    }}
  >
    {description}
  </div>

  <div class="meta">
    <span
      class="group-badge"
      title="Grupo"
      style={gc
        ? `--gc-border: ${gc.border}; --gc-bg: ${gc.bg}; --gc-text: ${gc.text};`
        : undefined}>{groupLabel}</span
    >
    {#if due_date}
      <TaskDueBadge {due_date} />
    {/if}
  </div>
  <div class="complete">
    {#if completed}
      <button
        class="btn primary primary-action"
        aria-label="Deshacer completar"
        title="Deshacer completar"
        on:click|preventDefault={doUncomplete}
        disabled={busy}
      >
        ↩️ Deshacer
      </button>
    {:else}
      <button
        class="btn primary primary-action"
        aria-label="Completar"
        title="Completar"
        on:click|preventDefault={doComplete}
        disabled={busy}
        ><CheckCircleSuccessIcon />
        Completar
      </button>
    {/if}
  </div>
  <div class="assignees-container">
    <TaskAssignees {id} {assignees} {currentUserId} />
  </div>
  <div class="actions">
    {#if !completed}
      {#if !isAssigned}
        <button
          class="icon-btn secondary-action"
          aria-label="Reclamar"
          on:click|preventDefault={doClaim}
          disabled={busy}
          ><ClaimIcon />
          Reclamar</button
        >
      {:else}
        <button
          class="icon-btn secondary-action"
          aria-label="Soltar"
          title={canUnassign ? "Soltar" : "No puedes soltar una tarea personal. Márcala como completada para eliminarla"}
          on:click|preventDefault={doUnassign}
          disabled={busy || !canUnassign}
          ><UnassignIcon />
          Soltar</button
        >
      {/if}

      {#if !editingText}
        <button
          class="icon-btn secondary-action"
          aria-label="Editar texto"
          title="Editar texto"
          on:click|preventDefault={toggleEditText}
          disabled={busy}
          ><EditIcon />
          Editar</button
        >
      {:else}
        <button
          class="btn primary secondary-action"
          on:click|preventDefault={saveText}
          disabled={busy}>Guardar</button
        >
        <button
          class="btn ghost secondary-action"
          on:click|preventDefault={cancelText}
          disabled={busy}>Cancelar</button
        >
      {/if}

      {#if !editing}
        <button
          class="icon-btn secondary-action"
          aria-label="Editar fecha"
          title="Editar fecha"
          on:click|preventDefault={toggleEdit}
          disabled={busy}
          ><CalendarEditIcon />
          Fecha</button
        >
      {:else}
        <input class="date" type="date" bind:value={dateValue} />
        <button
          class="btn primary secondary-action"
          on:click|preventDefault={saveDate}
          disabled={busy}>Guardar</button
        >
        <button
          class="btn danger secondary-action"
          on:click|preventDefault={clearDate}
          disabled={busy}>Quitar</button
        >
        <button
          class="btn ghost secondary-action"
          on:click|preventDefault={toggleEdit}
          disabled={busy}>Cancelar</button
        >
      {/if}
    {/if}
  </div>
</li>

<style>
  .task {
    display: grid;
    grid-template-columns: 1fr max-content;
    grid-template-rows: min-content max-content max-content max-content;
    grid-gap: 2px;
    padding: 4px 0 8px 0;
    border-bottom: 2px dashed var(--color-border);
    position: relative;
    margin: 0 0 4px 0;
  }
  .task:last-child {
    border-bottom: 0;
  }
  .code {
    font-weight: 300;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    font-family: monospace;
    letter-spacing: 0.5px;
    grid-row: 1/2;
    grid-column: 1/2;
    align-self: center;
  }
  .completed {
    opacity: 0.5;
  }
  .desc {
    padding: 8px 4px;
    grid-column: 1/3;
    grid-row: 2/3;
  }

  .desc.editing {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    background: var(--color-surface);
    padding: 2px 4px;
    border-radius: 4px;
    white-space: normal;
    text-overflow: clip;
    grid-column: 1/3;
    grid-row: 2/3;
    margin: 16px 0;
  }
  .desc.completed {
    text-decoration: line-through;
  }
  .meta {
    justify-self: end;
    align-items: start;
    grid-row: 1/2;
    grid-column: 2/3;
    display: inline-flex;
    gap: 6px;
    align-items: center;
  }
  .muted {
    color: var(--color-text-muted);
  }
  .group-badge {
    padding: 2px 6px;
    border-radius: 6px;
    border: 1px solid var(--gc-border, var(--color-border));
    background: var(--gc-bg, transparent);
    color: var(--gc-text, inherit);
    font-size: 12px;
  }
  .assignees-container {
    grid-row: 4/5;
    grid-column: 1/2;
  }
  .task.completed {
    opacity: 0.7;
  }
  .complete {
    grid-row: 3/4;
    grid-column: 2/3;
    justify-self: end;
  }
  .actions {
    justify-self: stretch;
    grid-column: 2/3;
    grid-row: 4/5;
    margin: 2px 0 4px 0;
    display: flex;
    flex-wrap: nowrap;
    gap: 6px;
    align-items: center;
    justify-content: flex-end;
  }
  .btn {
    padding: 4px 8px;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    border-radius: 6px;
    font-size: 13px;
    cursor: pointer;
    font-family: monospace;
    box-shadow: 0 0 8px 4px var(--color-border);
    margin-bottom: 4px;
  }
  .btn[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
  }
  .btn.primary {
    border-color: var(--color-primary);
    background: var(--color-primary-muted);
    color: var(--color-text);
  }
  :global(.task .btn.primary svg) {
    margin-right: 8px;
  }
  .btn.ghost {
    background: transparent;
  }
  .btn.danger {
    background: var(--color-danger);
    color: #fff;
    border-color: transparent;
  }
  .icon-btn {
    border: 1px solid var(--color-surface);
    border-radius: 6px;
    background: var(--color-surface);
    font-size: 12px;
    line-height: 1;
    font-family: monospace;
    box-shadow: 0 0 8px 4px var(--color-border);
  }
  :global(.task .icon-btn svg) {
    margin-right: 8px;
  }
  .date {
    padding: 4px 6px;
    font-size: 14px;
  }
  @media (max-width: 768px) {
    .actions {
      justify-self: stretch;
    }
    .actions .secondary-action {
      flex: 0 0 auto;
    }
    /* Botón de completar a ancho completo en mobile */
    .complete {
      grid-column: 1/3;
      justify-self: stretch;
    }
    .complete .btn {
      width: 100%;
    }
  }
  @media (max-width: 480px) {
    .task {
      grid-template-columns: 1fr;
    }
    .meta {
      justify-self: end;
    }
    /* En 1 columna, colocamos acciones en una fila propia bajo el badge */
    .actions {
      grid-row: 4/5;
      grid-column: 1/3;
      justify-self: flex-end;
    }
    .icon-btn {
      padding: 2px 8px;
    }
  }

  /* Badge de responsables */

  .icon-btn-svg {
    fill-rule: evenodd;
    clip-rule: evenodd;
    fill: var(--color-text);
  }
</style>
