import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { CalendarDays, Check, ChevronDown, Clock3, DollarSign, MoreVertical, Pencil, Play, Plus, Search, Square, Tag as TagIcon, Trash2 } from "lucide-react";
import { toast } from "sonner";

import { getProjects, type Project } from "../api/projects";
import {
  createTimeEntry,
  deleteTimeEntry,
  getTimeEntries,
  stopTimeEntry,
  type TimeEntryGroupWeek,
  type TimeEntry,
  type TimeEntryListParams,
  type TimeEntryPayload,
  updateTimeEntry,
} from "../api/timeEntries";
import { getTags, type Tag } from "../api/tags";
import { Modal } from "../components/Modal";
import { InfiniteScroll } from "../components/InfiniteScroll";
import TimesheetFilterBar, { type TimeEntryFilters } from "../components/timesheet/TimesheetFilterBar";
import JalaliDatePicker from "../components/ui/JalaliDatePicker";
import { Button } from "../components/ui/button";
import { Input } from "../components/ui/input";
import { Select } from "../components/ui/Select";
import { useWorkspace } from "../context/WorkspaceContext";
import { useTranslation } from "../hooks/useTranslation";

type EntryModalMode = "manual" | "edit" | null;

interface EntryFormState {
  description: string;
  projectId: string;
  startDate: string;
  startTime: string;
  endDate: string;
  endTime: string;
  isBillable: boolean;
  tags: string[];
}

interface TimerDraftState {
  description: string;
  projectId: string;
  isBillable: boolean;
  tags: string[];
}

const EMPTY_FORM: EntryFormState = {
  description: "",
  projectId: "",
  startDate: "",
  startTime: "",
  endDate: "",
  endTime: "",
  isBillable: false,
  tags: [],
};

const EMPTY_TIMER_DRAFT: TimerDraftState = {
  description: "",
  projectId: "",
  isBillable: false,
  tags: [],
};

const DEFAULT_ENTRY_FILTERS: TimeEntryFilters = {
  projectId: "",
  clientId: "",
  tagIds: [],
  startedAfter: "",
  startedBefore: "",
};

const pad = (value: number) => String(value).padStart(2, "0");

const normalizeDigits = (value: string) =>
  value
    .replace(/[۰-۹]/g, (digit) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(digit)))
    .replace(/[٠-٩]/g, (digit) => String("٠١٢٣٤٥٦٧٨٩".indexOf(digit)));

const parseApiDateTime = (value?: string | null) => {
  if (!value) return null;

  const normalized = normalizeDigits(String(value).trim());
  const candidates = Array.from(new Set([normalized, normalized.replace(" ", "T")]));

  for (const candidate of candidates) {
    const parsed = new Date(candidate);
    if (!Number.isNaN(parsed.getTime())) {
      return parsed;
    }
  }

  const match = normalized.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?$/);
  if (!match) return null;

  const [, year, month, day, hours, minutes, seconds] = match;
  return new Date(
    Number(year),
    Number(month) - 1,
    Number(day),
    Number(hours),
    Number(minutes),
    Number(seconds || 0),
    0,
  );
};

const formatTimeInputValue = (value: string) => {
  const digits = normalizeDigits(value).replace(/\D/g, "").slice(0, 6);
  if (digits.length <= 2) return digits;
  if (digits.length <= 4) return `${digits.slice(0, 2)}:${digits.slice(2)}`;
  return `${digits.slice(0, 2)}:${digits.slice(2, 4)}:${digits.slice(4)}`;
};

const getTimeCursorPosition = (digitCount: number) => {
  if (digitCount <= 2) return digitCount;
  if (digitCount <= 4) return digitCount + 1;
  return Math.min(digitCount + 2, 8);
};

const handleFormattedTimeInputChange = (
  event: React.ChangeEvent<HTMLInputElement>,
  onChange: (value: string) => void,
) => {
  const input = event.target;
  const selectionStart = input.selectionStart ?? input.value.length;
  const digitsBeforeCursor = normalizeDigits(input.value.slice(0, selectionStart)).replace(/\D/g, "").slice(0, 6);
  const formattedValue = formatTimeInputValue(input.value);
  const nextCursor = getTimeCursorPosition(digitsBeforeCursor.length);

  onChange(formattedValue);

  window.requestAnimationFrame(() => {
    if (document.activeElement !== input) return;
    input.setSelectionRange(nextCursor, nextCursor);
  });
};

const isValidTimeValue = (value: string) => {
  if (!/^\d{2}:\d{2}:\d{2}$/.test(value)) return false;
  const [hours, minutes, seconds] = value.split(":").map(Number);
  return hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59 && seconds >= 0 && seconds <= 59;
};

const getLocalDateParts = (value?: string | null) => {
  const parsed = parseApiDateTime(value);
  if (!parsed) {
    return {
      date: "",
      time: "",
    };
  }

  return {
    date: `${parsed.getFullYear()}-${pad(parsed.getMonth() + 1)}-${pad(parsed.getDate())}`,
    time: `${pad(parsed.getHours())}:${pad(parsed.getMinutes())}:${pad(parsed.getSeconds())}`,
  };
};

const combineDateAndTime = (dateValue: string, timeValue: string) => {
  if (!dateValue || !isValidTimeValue(timeValue)) return null;

  const [year, month, day] = dateValue.split("-").map(Number);
  const [hours, minutes, seconds] = timeValue.split(":").map(Number);

  return new Date(year, month - 1, day, hours, minutes, seconds, 0).toISOString();
};

const formatDateTime = (value: string, locale: "en" | "fa") => {
  const parsed = parseApiDateTime(value);
  if (!parsed) return value;

  return new Intl.DateTimeFormat(locale === "fa" ? "fa-IR" : "en-US", {
    dateStyle: "medium",
    timeStyle: "medium",
  }).format(parsed);
};

const formatDuration = (entry: TimeEntry, now = Date.now()) => {
  const start = parseApiDateTime(entry.start_time)?.getTime();
  const end = entry.end_time ? parseApiDateTime(entry.end_time)?.getTime() : now;

  if (!start || !end) return "00:00:00";

  const totalSeconds = Math.max(0, Math.floor((end - start) / 1000));
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = totalSeconds % 60;

  return [hours, minutes, seconds].map((part) => String(part).padStart(2, "0")).join(":");
};

const getEntryDurationMs = (entry: TimeEntry, now = Date.now()) => {
  const start = parseApiDateTime(entry.start_time)?.getTime();
  const end = entry.end_time ? parseApiDateTime(entry.end_time)?.getTime() : now;
  if (!start || !end) return 0;
  return Math.max(0, end - start);
};

const formatDurationMs = (durationMs: number) => {
  const totalSeconds = Math.max(0, Math.floor(durationMs / 1000));
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = totalSeconds % 60;
  return [hours, minutes, seconds].map((part) => String(part).padStart(2, "0")).join(":");
};

const formatTimeOnly = (value?: string | null, locale: "en" | "fa" = "en") => {
  const parsed = parseApiDateTime(value);
  if (!parsed) return "--:--:--";

  return new Intl.DateTimeFormat(locale === "fa" ? "fa-IR" : "en-US", {
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: false,
  }).format(parsed);
};

const getWeekStart = (date: Date) => {
  const value = new Date(date);
  value.setHours(0, 0, 0, 0);
  value.setDate(value.getDate() - value.getDay());
  return value;
};

const formatWeekRange = (date: Date, locale: "en" | "fa") => {
  const start = getWeekStart(date);
  const end = new Date(start);
  end.setDate(start.getDate() + 6);

  const formatter = new Intl.DateTimeFormat(locale === "fa" ? "fa-IR" : "en-US", {
    month: "short",
    day: "numeric",
  });

  return `${formatter.format(start)} - ${formatter.format(end)}`;
};

const formatDayLabel = (date: Date, locale: "en" | "fa") =>
  new Intl.DateTimeFormat(locale === "fa" ? "fa-IR" : "en-US", {
    weekday: "short",
    month: "short",
    day: "numeric",
  }).format(date);

const mergeGroupedHistory = (currentGroups: TimeEntryGroupWeek[], nextGroups: TimeEntryGroupWeek[]) => {
  const merged = currentGroups.map((week) => ({
    ...week,
    days: week.days.map((day) => ({ ...day, entries: [...day.entries] })),
  }));

  nextGroups.forEach((incomingWeek) => {
    const existingWeek = merged.find((week) => week.key === incomingWeek.key);
    if (!existingWeek) {
      merged.push({
        ...incomingWeek,
        days: incomingWeek.days.map((day) => ({ ...day, entries: [...day.entries] })),
      });
      return;
    }

    existingWeek.total_ms = incomingWeek.total_ms;

    incomingWeek.days.forEach((incomingDay) => {
      const existingDay = existingWeek.days.find((day) => day.key === incomingDay.key);
      if (!existingDay) {
        existingWeek.days.push({ ...incomingDay, entries: [...incomingDay.entries] });
        return;
      }

      existingDay.total_ms = incomingDay.total_ms;
      const existingIds = new Set(existingDay.entries.map((entry) => entry.id));
      incomingDay.entries.forEach((entry) => {
        if (!existingIds.has(entry.id)) {
          existingDay.entries.push(entry);
        }
      });
    });
  });

  return merged;
};

const updateGroupedHistoryEntry = (
  groups: TimeEntryGroupWeek[],
  updatedEntry: TimeEntry,
) => {
  const filteredGroups = groups
    .map((week) => ({
      ...week,
      days: week.days
        .map((day) => ({
          ...day,
          entries: day.entries.filter((entry) => entry.id !== updatedEntry.id),
        }))
        .filter((day) => day.entries.length > 0),
    }))
    .filter((week) => week.days.length > 0);

  if (!updatedEntry.end_time) {
    return filteredGroups;
  }

  const start = parseApiDateTime(updatedEntry.start_time);
  if (!start) {
    return filteredGroups;
  }

  const weekStart = getWeekStart(start);
  const weekKey = `${weekStart.getFullYear()}-${pad(weekStart.getMonth() + 1)}-${pad(weekStart.getDate())}`;
  const dayKey = `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}`;
  const weekEnd = new Date(weekStart);
  weekEnd.setDate(weekStart.getDate() + 6);
  const merged = filteredGroups.map((week) => ({
    ...week,
    days: week.days.map((day) => ({ ...day, entries: [...day.entries] })),
  }));

  let targetWeek = merged.find((week) => week.key === weekKey);
  if (!targetWeek) {
    targetWeek = {
      key: weekKey,
      week_start: `${weekStart.getFullYear()}-${pad(weekStart.getMonth() + 1)}-${pad(weekStart.getDate())}`,
      week_end: `${weekEnd.getFullYear()}-${pad(weekEnd.getMonth() + 1)}-${pad(weekEnd.getDate())}`,
      total_ms: 0,
      days: [],
    };
    merged.push(targetWeek);
  }

  let targetDay = targetWeek.days.find((day) => day.key === dayKey);
  if (!targetDay) {
    targetDay = {
      key: dayKey,
      date: dayKey,
      total_ms: 0,
      entries: [],
    };
    targetWeek.days.push(targetDay);
  }

  targetDay.entries.unshift(updatedEntry);
  targetDay.entries.sort((a, b) => {
    const aTime = parseApiDateTime(a.start_time)?.getTime() || 0;
    const bTime = parseApiDateTime(b.start_time)?.getTime() || 0;
    return bTime - aTime;
  });
  targetDay.total_ms = targetDay.entries.reduce((sum, entry) => sum + getEntryDurationMs(entry), 0);
  targetWeek.total_ms = targetWeek.days.reduce((sum, day) => sum + day.total_ms, 0);

  merged.sort((a, b) => (a.week_start < b.week_start ? 1 : -1));
  merged.forEach((week) => {
    week.days.sort((a, b) => (a.date < b.date ? 1 : -1));
  });

  return merged;
};

const buildEntryFormState = (entry?: TimeEntry | null): EntryFormState => {
  if (!entry) {
    const now = getLocalDateParts(new Date().toISOString());
    return {
      ...EMPTY_FORM,
      startDate: now.date,
      startTime: now.time,
    };
  }

  const start = getLocalDateParts(entry.start_time);
  const end = getLocalDateParts(entry.end_time);

  return {
    description: entry.description || "",
    projectId: entry.project || "",
    startDate: start.date,
    startTime: start.time,
    endDate: end.date,
    endTime: end.time,
    isBillable: entry.is_billable,
    tags: entry.tags || [],
  };
};

const buildTimerDraftState = (entry?: TimeEntry | null): TimerDraftState => ({
  description: entry?.description || "",
  projectId: entry?.project || "",
  isBillable: entry?.is_billable || false,
  tags: entry?.tags || [],
});

const serializeTimerDraft = (state: TimerDraftState) =>
  JSON.stringify({
    description: state.description.trim(),
    projectId: state.projectId || "",
    isBillable: state.isBillable,
    tags: [...state.tags].sort(),
  });

const serializeEntryDraft = (state: EntryFormState) =>
  JSON.stringify({
    description: state.description.trim(),
    projectId: state.projectId || "",
    startDate: state.startDate || "",
    startTime: state.startTime || "",
    endDate: state.endDate || "",
    endTime: state.endTime || "",
    isBillable: state.isBillable,
    tags: [...state.tags].sort(),
  });

const toggleTagId = (currentTags: string[], tagId: string) =>
  currentTags.includes(tagId) ? currentTags.filter((currentId) => currentId !== tagId) : [...currentTags, tagId];

const buildPayloadFromState = (
  state: EntryFormState,
  options: { includeWorkspace: boolean; workspaceId?: string },
): { payload?: TimeEntryPayload; error?: string } => {
  const startDateTime = combineDateAndTime(state.startDate, state.startTime);
  if (!startDateTime) {
    return { error: "Start date and time are required." };
  }

  let endDateTime: string | null = null;
  const hasEndValue = Boolean(state.endDate || state.endTime);
  if (hasEndValue) {
    if (!state.endDate || !state.endTime) {
      return { error: "End date and time must both be filled." };
    }

    endDateTime = combineDateAndTime(state.endDate, state.endTime);
    if (!endDateTime) {
      return { error: "End time is invalid." };
    }
  }

  const payload: TimeEntryPayload = {
    description: state.description.trim(),
    project_id: state.projectId || null,
    start_time: startDateTime,
    end_time: endDateTime,
    tags: state.tags,
    is_billable: state.isBillable,
  };

  if (options.includeWorkspace && options.workspaceId) {
    payload.workspace_id = options.workspaceId;
  }

  return { payload };
};

function TimeField({
  label,
  value,
  onChange,
  placeholder,
  compact = false,
}: {
  label: string;
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
  compact?: boolean;
}) {
  return (
    <div className={compact ? "min-w-[104px]" : ""}>
      <label className={`block text-sm font-medium text-slate-700 dark:text-slate-300 ${compact ? "mb-0.5 text-[11px]" : "mb-1"}`}>{label}</label>
      <Input
        type="text"
        inputMode="numeric"
        dir="ltr"
        value={value}
        maxLength={8}
        placeholder={placeholder || "HH:MM:SS"}
        className={compact ? "h-9 px-2 text-xs" : ""}
        onChange={(event) => handleFormattedTimeInputChange(event, onChange)}
      />
    </div>
  );
}

function BillableIconButton({
  checked,
  onChange,
  label,
  disabled = false,
  compact = false,
}: {
  checked: boolean;
  onChange: (checked: boolean) => void;
  label: string;
  disabled?: boolean;
  compact?: boolean;
}) {
  return (
    <button
      type="button"
      aria-pressed={checked}
      aria-label={label}
      title={label}
      disabled={disabled}
      onClick={() => onChange(!checked)}
      className={`inline-flex items-center justify-center transition-colors ${
        checked
          ? "text-sky-600 dark:text-sky-400"
          : "text-slate-400 dark:text-slate-500"
      } ${compact ? "h-12 w-10 border-s border-slate-200 bg-transparent hover:bg-slate-50 dark:border-slate-800 dark:hover:bg-slate-900/70" : "gap-2 rounded-2xl border border-slate-200 bg-white px-4 py-3 text-sm dark:border-slate-700 dark:bg-slate-900"} disabled:cursor-not-allowed disabled:opacity-60`}
    >
      <DollarSign className="h-4 w-4" />
      {!compact && <span className="font-medium">{label}</span>}
    </button>
  );
}

function TagMultiSelect({
  tags,
  selectedTags,
  onToggleTag,
  emptyHint,
  title,
  compact = false,
  portalOwnerId,
}: {
  tags: Tag[];
  selectedTags: string[];
  onToggleTag: (tagId: string) => void;
  emptyHint: string;
  title: string;
  compact?: boolean;
  portalOwnerId?: string;
}) {
  const [isOpen, setIsOpen] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const wrapperRef = useRef<HTMLDivElement>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);
  const [dropdownStyle, setDropdownStyle] = useState<React.CSSProperties>({});

  useEffect(() => {
    if (!isOpen) return;

    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as Node;
      const clickedInsideTrigger = wrapperRef.current?.contains(target);
      const clickedInsideDropdown = dropdownRef.current?.contains(target);

      if (!clickedInsideTrigger && !clickedInsideDropdown) {
        setIsOpen(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => document.removeEventListener("mousedown", handleClickOutside);
  }, [isOpen]);

  useEffect(() => {
    if (!isOpen || !buttonRef.current) return;

    const rect = buttonRef.current.getBoundingClientRect();
    const dropdownWidth = compact ? 256 : Math.max(rect.width, 256);
    const spaceBelow = window.innerHeight - rect.bottom;
    const openUpward = spaceBelow < 280 && rect.top > spaceBelow;

    setDropdownStyle({
      position: "fixed",
      top: openUpward ? `${rect.top - 8}px` : `${rect.bottom + 8}px`,
      left: `${Math.max(12, rect.right - dropdownWidth)}px`,
      width: `${dropdownWidth}px`,
      transform: openUpward ? "translateY(-100%)" : "none",
      zIndex: 100000,
    });
  }, [compact, isOpen]);

  useEffect(() => {
    const closeOnViewportChange = () => setIsOpen(false);

    if (isOpen) {
      window.addEventListener("resize", closeOnViewportChange);
      window.addEventListener("scroll", closeOnViewportChange, true);
    }

    return () => {
      window.removeEventListener("resize", closeOnViewportChange);
      window.removeEventListener("scroll", closeOnViewportChange, true);
    };
  }, [isOpen]);

  useEffect(() => {
    if (!isOpen) {
      setSearchQuery("");
    }
  }, [isOpen]);

  const selectedLabels = tags.filter((tag) => selectedTags.includes(tag.id)).map((tag) => tag.name);
  const joinedSelectedLabels = selectedLabels.join(" | ");
  const normalizedSearch = searchQuery.trim().toLowerCase();
  const filteredTags = normalizedSearch
    ? tags.filter((tag) => tag.name.toLowerCase().includes(normalizedSearch))
    : tags;
  const buttonLabel = compact
    ? selectedTags.length > 0
      ? joinedSelectedLabels
      : ""
    : selectedLabels.length > 0
      ? selectedLabels.join(", ")
      : title;

  return (
    <div ref={wrapperRef} className={compact ? "relative w-fit" : "relative"}>
      {!compact && <p className="mb-2 block text-sm font-medium text-slate-700 dark:text-slate-300">{title}</p>}
      <button
        ref={buttonRef}
        type="button"
        onClick={() => setIsOpen((current) => !current)}
        title={selectedLabels.length > 0 ? selectedLabels.join(", ") : title}
        className={`inline-flex items-center gap-1 text-slate-700 dark:text-slate-200 ${
          compact
            ? "h-12 w-fit border-0 bg-transparent px-2 text-xs"
            : "w-full rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm dark:border-slate-700 dark:bg-slate-900"
        }`}
      >
        {compact ? (
          <span className="inline-flex min-w-0 items-center gap-1.5">
            {buttonLabel ? (
              <span className="truncate rounded-sm bg-sky-50 px-2 py-1 text-[11px] font-medium text-sky-700 dark:bg-sky-500/15 dark:text-sky-300">
                {buttonLabel}
              </span>
            ) : (
              <TagIcon className="h-4 w-4 text-slate-400 dark:text-slate-500" />
            )}
          </span>
        ) : (
          <span className="truncate">{buttonLabel}</span>
        )}
        {!compact && <ChevronDown className={`h-4 w-4 shrink-0 transition-transform ${isOpen ? "rotate-180" : ""}`} />}
      </button>

      {isOpen && (
        createPortal(
        <div
          ref={dropdownRef}
          style={dropdownStyle}
          data-entry-editor-owner={portalOwnerId}
          className="rounded-2xl border border-slate-200 bg-white p-2 shadow-xl dark:border-slate-700 dark:bg-slate-800"
        >
          {tags.length === 0 ? (
            <p className="px-2 py-2 text-sm text-slate-500 dark:text-slate-400">{emptyHint}</p>
          ) : (
            <>
              <div className="border-b border-slate-200 p-2 dark:border-slate-700">
                <div className="relative">
                  <Search className="pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-400" />
                  <input
                    type="text"
                    value={searchQuery}
                    onChange={(event) => setSearchQuery(event.target.value)}
                    placeholder="Search tags..."
                    className="h-8 w-full rounded-md border border-slate-200 bg-slate-50 pl-8 pr-2 text-xs text-slate-900 outline-none transition focus:border-sky-400 focus:bg-white focus:ring-2 focus:ring-sky-500/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:focus:border-sky-500 dark:focus:bg-slate-800"
                  />
                </div>
              </div>
              <div className="max-h-72 space-y-1 overflow-y-auto p-2">
                {filteredTags.map((tag) => {
                const selected = selectedTags.includes(tag.id);
                return (
                  <button
                    key={tag.id}
                    type="button"
                    onMouseDown={(event) => event.preventDefault()}
                    onClick={() => onToggleTag(tag.id)}
                    className={`flex w-full items-center justify-between rounded-xl px-2 py-2 text-sm transition-colors ${
                      selected ? "bg-slate-100 text-slate-900 dark:bg-slate-700 dark:text-white" : "text-slate-700 dark:text-slate-200"
                    }`}
                  >
                    <span className="inline-flex min-w-0 items-center gap-2 truncate">
                      <span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: tag.color || "#94A3B8" }} />
                      <span className="truncate">{tag.name}</span>
                    </span>
                    {selected && <Check className="h-4 w-4 shrink-0" />}
                  </button>
                );
                })}
                {filteredTags.length === 0 && (
                  <div className="px-2 py-3 text-xs text-slate-500 dark:text-slate-400">
                    No tags found.
                  </div>
                )}
              </div>
            </>
          )}
        </div>,
        document.body
        )
      )}
    </div>
  );
}

function ProjectInlineSelect({
  projects,
  value,
  onChange,
  placeholder,
  portalOwnerId,
  className = "",
  dropdownClassName = "",
  disabled = false,
}: {
  projects: Project[];
  value: string;
  onChange: (projectId: string) => void;
  placeholder: string;
  portalOwnerId?: string;
  className?: string;
  dropdownClassName?: string;
  disabled?: boolean;
}) {
  const [isOpen, setIsOpen] = useState(false);
  const wrapperRef = useRef<HTMLDivElement>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);
  const [dropdownStyle, setDropdownStyle] = useState<React.CSSProperties>({});

  useEffect(() => {
    if (!isOpen) return;

    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as Node;
      const clickedInsideTrigger = wrapperRef.current?.contains(target);
      const clickedInsideDropdown = dropdownRef.current?.contains(target);

      if (!clickedInsideTrigger && !clickedInsideDropdown) {
        setIsOpen(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => document.removeEventListener("mousedown", handleClickOutside);
  }, [isOpen]);

  useEffect(() => {
    if (!isOpen || !buttonRef.current) return;

    const rect = buttonRef.current.getBoundingClientRect();
    const dropdownWidth = 220;
    const spaceBelow = window.innerHeight - rect.bottom;
    const openUpward = spaceBelow < 280 && rect.top > spaceBelow;

    setDropdownStyle({
      position: "fixed",
      top: openUpward ? `${rect.top - 8}px` : `${rect.bottom + 8}px`,
      left: `${Math.max(12, rect.left)}px`,
      width: `${dropdownWidth}px`,
      transform: openUpward ? "translateY(-100%)" : "none",
      zIndex: 100000,
    });
  }, [isOpen]);

  useEffect(() => {
    const closeOnViewportChange = () => setIsOpen(false);

    if (isOpen) {
      window.addEventListener("resize", closeOnViewportChange);
      window.addEventListener("scroll", closeOnViewportChange, true);
    }

    return () => {
      window.removeEventListener("resize", closeOnViewportChange);
      window.removeEventListener("scroll", closeOnViewportChange, true);
    };
  }, [isOpen]);

  const selectedProject = projects.find((project) => project.id === value);
  const label = selectedProject?.name || placeholder;

  return (
    <div ref={wrapperRef} className={`relative shrink-0 ${className}`}>
      <button
        ref={buttonRef}
        type="button"
        onClick={() => !disabled && setIsOpen((current) => !current)}
        disabled={disabled}
        className={`inline-flex max-w-full items-center rounded-sm bg-transparent py-0 text-sm transition-colors ${
          selectedProject
            ? "text-sky-600 hover:text-sky-700 dark:text-sky-400 dark:hover:text-sky-300"
            : "text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
        } disabled:cursor-not-allowed disabled:opacity-60`}
        title={label}
      >
        <span className="truncate">{label}</span>
      </button>

      {isOpen && !disabled &&
        createPortal(
          <div
            ref={dropdownRef}
            style={dropdownStyle}
            data-entry-editor-owner={portalOwnerId}
            className={`rounded-2xl border border-slate-200 bg-white p-2 shadow-xl dark:border-slate-700 dark:bg-slate-800 ${dropdownClassName}`}
          >
            <div className="max-h-64 space-y-1 overflow-y-auto">
              <button
                type="button"
                onMouseDown={(event) => event.preventDefault()}
                onClick={() => {
                  onChange("");
                  setIsOpen(false);
                }}
                className={`flex w-full items-center rounded-xl px-3 py-2 text-sm transition-colors ${
                  value === ""
                    ? "bg-sky-50 text-sky-700 dark:bg-sky-500/15 dark:text-sky-300"
                    : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-700/70"
                }`}
              >
                {placeholder}
              </button>

              {projects.map((project) => {
                const selected = project.id === value;
                return (
                  <button
                    key={project.id}
                    type="button"
                    onMouseDown={(event) => event.preventDefault()}
                    onClick={() => {
                      onChange(project.id);
                      setIsOpen(false);
                    }}
                    className={`flex w-full items-center rounded-xl px-3 py-2 text-sm transition-colors ${
                      selected
                        ? "bg-sky-50 text-sky-700 dark:bg-sky-500/15 dark:text-sky-300"
                        : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-700/70"
                    }`}
                  >
                    <span className="truncate">{project.name}</span>
                  </button>
                );
              })}
            </div>
          </div>,
          document.body,
        )}
    </div>
  );
}

function CompactDateTimeField({
  label,
  dateValue,
  timeValue,
  onDateChange,
  onTimeChange,
}: {
  label: string;
  dateValue: string;
  timeValue: string;
  onDateChange: (value: string) => void;
  onTimeChange: (value: string) => void;
}) {
  return (
    <div className="min-w-[208px]">
      <label className="mb-0.5 block text-[11px] font-medium text-slate-700 dark:text-slate-300">{label}</label>
      <div className="flex h-9 items-center overflow-hidden rounded-xl border border-slate-200 bg-white dark:border-slate-700 dark:bg-slate-900">
        <div className="min-w-0 flex-1">
          <JalaliDatePicker
            value={dateValue}
            onChange={onDateChange}
            inputClassName="h-9 rounded-none border-0 bg-transparent px-2 text-xs shadow-none focus:ring-0 focus:outline-none"
          />
        </div>
        <div className="h-5 w-px bg-slate-200 dark:bg-slate-700" />
        <Input
          type="text"
          inputMode="numeric"
          dir="ltr"
          value={timeValue}
          maxLength={8}
          placeholder="HH:MM:SS"
          className="h-9 min-w-[88px] border-0 bg-transparent px-2 text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
          onChange={(event) => onTimeChange(formatTimeInputValue(event.target.value))}
        />
      </div>
    </div>
  );
}

function InlineTimeRangeField({
  startTime,
  endTime,
  onStartTimeChange,
  onEndTimeChange,
}: {
  startTime: string;
  endTime: string;
  onStartTimeChange: (value: string) => void;
  onEndTimeChange: (value: string) => void;
}) {
  return (
    <div className="flex h-12 items-center justify-center border-s border-slate-200 px-2 text-xs text-slate-600 dark:border-slate-800 dark:text-slate-200">
      <Input
        type="text"
        inputMode="numeric"
        dir="ltr"
        value={startTime}
        maxLength={8}
        placeholder="HH:MM:SS"
        className="h-9 min-w-[68px] border-0 bg-transparent dark:bg-transparent px-0 text-center text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
        onChange={(event) => handleFormattedTimeInputChange(event, onStartTimeChange)}
      />
      <span className="px-1 text-slate-400">-</span>
      <Input
        type="text"
        inputMode="numeric"
        dir="ltr"
        value={endTime}
        maxLength={8}
        placeholder="HH:MM:SS"
        className="h-9 min-w-[68px] border-0 bg-transparent dark:bg-transparent px-0 text-center text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
        onChange={(event) => handleFormattedTimeInputChange(event, onEndTimeChange)}
      />
    </div>
  );
}

function DateRangePopover({
  startDate,
  endDate,
  onStartDateChange,
  onEndDateChange,
  portalOwnerId,
}: {
  startDate: string;
  endDate: string;
  onStartDateChange: (value: string) => void;
  onEndDateChange: (value: string) => void;
  portalOwnerId?: string;
}) {
  const [isOpen, setIsOpen] = useState(false);
  const wrapperRef = useRef<HTMLDivElement>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);
  const [dropdownStyle, setDropdownStyle] = useState<React.CSSProperties>({});

  useEffect(() => {
    if (!isOpen) return;

    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as Node;
      const clickedInsideTrigger = wrapperRef.current?.contains(target);
      const clickedInsideDropdown = dropdownRef.current?.contains(target);

      if (!clickedInsideTrigger && !clickedInsideDropdown) {
        setIsOpen(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => document.removeEventListener("mousedown", handleClickOutside);
  }, [isOpen]);

  useEffect(() => {
    if (!isOpen || !buttonRef.current) return;

    const rect = buttonRef.current.getBoundingClientRect();
    const dropdownWidth = 280;
    const spaceBelow = window.innerHeight - rect.bottom;
    const openUpward = spaceBelow < 240 && rect.top > spaceBelow;

    setDropdownStyle({
      position: "fixed",
      top: openUpward ? `${rect.top - 8}px` : `${rect.bottom + 8}px`,
      left: `${Math.max(12, rect.right - dropdownWidth)}px`,
      width: `${dropdownWidth}px`,
      transform: openUpward ? "translateY(-100%)" : "none",
      zIndex: 100000,
    });
  }, [isOpen]);

  return (
    <div ref={wrapperRef} className="relative">
      <button
        ref={buttonRef}
        type="button"
        onClick={() => setIsOpen((current) => !current)}
        className="inline-flex h-12 w-10 items-center justify-center border-s border-slate-200 bg-transparent text-slate-400 transition-colors hover:bg-slate-50 hover:text-slate-700 dark:border-slate-800 dark:text-slate-300 dark:hover:bg-slate-900 dark:hover:text-white"
        title="Edit dates"
      >
        <CalendarDays className="h-4 w-4" />
      </button>

      {isOpen &&
        createPortal(
          <div
            ref={dropdownRef}
            style={dropdownStyle}
            data-entry-editor-owner={portalOwnerId}
            className="rounded-2xl border border-slate-200 bg-white p-3 shadow-xl dark:border-slate-700 dark:bg-slate-950"
          >
            <div className="grid gap-3">
              <JalaliDatePicker label="Start date" value={startDate} onChange={onStartDateChange} />
              <JalaliDatePicker label="End date" value={endDate} onChange={onEndDateChange} />
            </div>
          </div>,
          document.body,
        )}
    </div>
  );
}

function DeleteEntryButton({
  onDelete,
}: {
  onDelete: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onDelete}
      className="inline-flex h-12 w-10 items-center justify-center rounded-none border-0 bg-transparent text-slate-400 transition-colors hover:bg-red-50 hover:text-red-600 dark:text-slate-500 dark:hover:bg-red-500/10 dark:hover:text-red-400"
      title="Delete"
    >
      <Trash2 className="h-4 w-4" />
    </button>
  );
}

function EntryEditorFields({
  state,
  onChange,
  onToggleTag,
  onProjectChange,
  projects,
  tags,
  t,
  isRtl,
  compact = false,
  portalOwnerId,
}: {
  state: EntryFormState;
  onChange: (patch: Partial<EntryFormState>) => void;
  onToggleTag: (tagId: string) => void;
  onProjectChange?: (projectId: string) => void;
  projects: Project[];
  tags: Tag[];
  t: any;
  isRtl: boolean;
  compact?: boolean;
  portalOwnerId?: string;
}) {
  if (compact) {
    const selectedProject = projects.find((project) => project.id === state.projectId);
    return (
      <div className="grid min-w-0 flex-1 grid-cols-[minmax(420px,1fr)_40px_188px_40px] items-center">
        <div className="flex min-w-0 items-center">
          <Input
            value={state.description}
            onChange={(event) => onChange({ description: event.target.value })}
            placeholder={t.timesheet?.descriptionPlaceholder || "What are you working on?"}
            className="h-12 w-[220px] shrink-0 rounded-none border-0 bg-transparent px-0 pe-3 text-sm shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 truncate dark:bg-transparent dark:text-slate-100"
          />

          <span className="me-2 shrink-0 text-sm font-semibold leading-none text-sky-600 dark:text-sky-400">•</span>

          <ProjectInlineSelect
            projects={projects}
            value={state.projectId}
            onChange={(projectId) => (onProjectChange ? onProjectChange(projectId) : onChange({ projectId }))}
            placeholder={t.timesheet?.projectLabel || "Project"}
            portalOwnerId={portalOwnerId}
            className="max-w-[180px]"
          />

          {selectedProject && (
            <span className="ms-2 shrink-0 truncate text-sm text-slate-400 dark:text-slate-500">
              - {selectedProject.client?.name || ""}
            </span>
          )}

          <div className="min-w-[24px] flex-1" />

          <div className="shrink-0">
            <TagMultiSelect
              tags={tags}
              selectedTags={state.tags}
              onToggleTag={onToggleTag}
              emptyHint={t.timesheet?.noTagsHint || "Create tags first from the Tags page."}
              title={t.tags?.title || "Tags"}
              compact
              portalOwnerId={portalOwnerId}
            />
          </div>
        </div>

        <div className="w-10">
          <BillableIconButton
            checked={state.isBillable}
            onChange={(checked) => onChange({ isBillable: checked })}
            label={t.timesheet?.billable || "Billable"}
            compact
          />
        </div>

        <div>
          <InlineTimeRangeField
            startTime={state.startTime}
            endTime={state.endTime}
            onStartTimeChange={(value) => onChange({ startTime: value })}
            onEndTimeChange={(value) => onChange({ endTime: value })}
          />
        </div>

        <div>
          <DateRangePopover
            startDate={state.startDate}
            endDate={state.endDate}
            onStartDateChange={(value) => onChange({ startDate: value })}
            onEndDateChange={(value) => onChange({ endDate: value })}
            portalOwnerId={portalOwnerId}
          />
        </div>
      </div>
    );
  }

  return (
    <div className="space-y-5">
      <div>
        <label className={`block font-medium text-slate-700 dark:text-slate-300 ${compact ? "mb-0.5 text-[11px]" : "mb-1 text-sm"}`}>
          {t.timesheet?.descriptionLabel || "Description"}
        </label>
        <Input
          value={state.description}
          onChange={(event) => onChange({ description: event.target.value })}
          placeholder={t.timesheet?.descriptionPlaceholder || "What are you working on?"}
          className={compact ? "h-9 px-2 text-xs" : ""}
        />
      </div>

      <div>
        <label className={`block font-medium text-slate-700 dark:text-slate-300 ${compact ? "mb-0.5 text-[11px]" : "mb-1 text-sm"}`}>
          {t.timesheet?.projectLabel || "Project"}
        </label>
        <Select
          value={state.projectId}
          onChange={(value) => onChange({ projectId: String(value) })}
          options={[
            { value: "", label: t.timesheet?.noProject || "No project" },
            ...projects.map((project) => ({ value: project.id, label: project.name })),
          ]}
          className="w-full"
          buttonClassName={compact ? "w-full h-9 px-2 text-xs" : "w-full"}
        />
      </div>

      <div className="grid gap-4 md:grid-cols-2">
        <div className={compact ? "" : "space-y-4 rounded-2xl border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/60"}>
          <JalaliDatePicker
            label={t.timesheet?.startLabel || "Start"}
            value={state.startDate}
            onChange={(date) => onChange({ startDate: date })}
            inputClassName={compact ? "h-9 px-2 text-xs" : undefined}
          />
          <TimeField
            label={t.timesheet?.timeLabel || "Time"}
            value={state.startTime}
            onChange={(value) => onChange({ startTime: value })}
            compact={compact}
          />
        </div>

        <div className={compact ? "" : "space-y-4 rounded-2xl border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/60"}>
          <JalaliDatePicker
            label={t.timesheet?.endLabel || "End"}
            value={state.endDate}
            onChange={(date) => onChange({ endDate: date })}
            inputClassName={compact ? "h-9 px-2 text-xs" : undefined}
          />
          <TimeField
            label={t.timesheet?.timeLabel || "Time"}
            value={state.endTime}
            onChange={(value) => onChange({ endTime: value })}
            compact={compact}
          />
        </div>
      </div>

      <div>
        <BillableIconButton
          checked={state.isBillable}
          onChange={(checked) => onChange({ isBillable: checked })}
          label={t.timesheet?.billable || "Billable"}
        />
      </div>

      <TagMultiSelect
        tags={tags}
        selectedTags={state.tags}
        onToggleTag={onToggleTag}
        emptyHint={t.timesheet?.noTagsHint || "Create tags first from the Tags page."}
        title={t.tags?.title || "Tags"}
        compact={compact}
      />
    </div>
  );
}

function RecordedEntryCard({
  entry,
  t,
  projects,
  tags,
  onDelete,
  onRestart,
  onEntryUpdated,
}: {
  entry: TimeEntry;
  t: any;
  projects: Project[];
  tags: Tag[];
  onDelete: (entry: TimeEntry) => void;
  onRestart: (entry: TimeEntry) => void;
  onEntryUpdated: (entry: TimeEntry) => void;
}) {
  const [draft, setDraft] = useState<EntryFormState>(() => buildEntryFormState(entry));
  const [validationMessage, setValidationMessage] = useState("");
  const syncedSignatureRef = useRef(serializeEntryDraft(buildEntryFormState(entry)));
  const rowRef = useRef<HTMLDivElement>(null);
  const isSavingRef = useRef(false);
  const pendingSignatureRef = useRef<string | null>(null);
  const editorOwnerId = `time-entry-editor-${entry.id}`;
  const timesheetCopy = (t.timesheet as { saveError?: string; saveSuccess?: string }) || {};
  const saveErrorText = timesheetCopy.saveError || "Failed to save time entry";
  const saveSuccessText = timesheetCopy.saveSuccess || "Time entry saved";

  useEffect(() => {
    const nextDraft = buildEntryFormState(entry);
    const nextSignature = serializeEntryDraft(nextDraft);
    syncedSignatureRef.current = nextSignature;
    pendingSignatureRef.current = nextSignature;
    setDraft(nextDraft);
    setValidationMessage("");
  }, [entry]);

  const isInsideEditorContext = useCallback((target: EventTarget | null) => {
    if (!(target instanceof Node)) return false;
    if (rowRef.current?.contains(target)) return true;
    return target instanceof Element && Boolean(target.closest(`[data-entry-editor-owner="${editorOwnerId}"]`));
  }, [editorOwnerId]);

  const commitDraft = useCallback(async () => {
    const currentSignature = serializeEntryDraft(draft);
    if (currentSignature === syncedSignatureRef.current) {
      setValidationMessage("");
      return false;
    }

    if (isSavingRef.current || pendingSignatureRef.current === currentSignature) {
      return false;
    }

    const { payload, error } = buildPayloadFromState(draft, { includeWorkspace: false });
    if (!payload) {
      setValidationMessage(error || "");
      return false;
    }

    setValidationMessage("");
    isSavingRef.current = true;
    pendingSignatureRef.current = currentSignature;
    try {
      const updatedEntry = await updateTimeEntry(entry.id, payload);
      const updatedDraft = buildEntryFormState(updatedEntry);
      const updatedSignature = serializeEntryDraft(updatedDraft);
      syncedSignatureRef.current = updatedSignature;
      pendingSignatureRef.current = updatedSignature;
      setDraft(updatedDraft);
      onEntryUpdated(updatedEntry);
      toast.success(saveSuccessText);
      return true;
    } catch (error) {
      console.error(error);
      pendingSignatureRef.current = null;
      toast.error(saveErrorText);
      return false;
    } finally {
      isSavingRef.current = false;
    }
  }, [draft, entry.id, onEntryUpdated, saveErrorText, saveSuccessText]);

  const commitPatchedDraft = useCallback(async (patch: Partial<EntryFormState>) => {
    const nextDraft = { ...draft, ...patch };
    const nextSignature = serializeEntryDraft(nextDraft);
    setDraft(nextDraft);

    if (nextSignature === syncedSignatureRef.current) {
      setValidationMessage("");
      return false;
    }

    if (isSavingRef.current || pendingSignatureRef.current === nextSignature) {
      return false;
    }

    const { payload, error } = buildPayloadFromState(nextDraft, { includeWorkspace: false });
    if (!payload) {
      setValidationMessage(error || "");
      return false;
    }

    setValidationMessage("");
    isSavingRef.current = true;
    pendingSignatureRef.current = nextSignature;

    try {
      const updatedEntry = await updateTimeEntry(entry.id, payload);
      const updatedDraft = buildEntryFormState(updatedEntry);
      const updatedSignature = serializeEntryDraft(updatedDraft);
      syncedSignatureRef.current = updatedSignature;
      pendingSignatureRef.current = updatedSignature;
      setDraft(updatedDraft);
      onEntryUpdated(updatedEntry);
      toast.success(saveSuccessText);
      return true;
    } catch (error) {
      console.error(error);
      pendingSignatureRef.current = null;
      toast.error(saveErrorText);
      return false;
    } finally {
      isSavingRef.current = false;
    }
  }, [draft, entry.id, onEntryUpdated, saveErrorText, saveSuccessText]);

  useEffect(() => {
    const handlePointerDown = (event: MouseEvent) => {
      if (isInsideEditorContext(event.target)) return;
      void commitDraft();
    };

    document.addEventListener("mousedown", handlePointerDown);
    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
    };
  }, [commitDraft, isInsideEditorContext]);

  const handleBlurCapture = () => {
    window.setTimeout(() => {
      if (isInsideEditorContext(document.activeElement)) return;
      void commitDraft();
    }, 0);
  };

  return (
    <div ref={rowRef} onBlurCapture={handleBlurCapture} className="border-b border-slate-200 bg-white px-4 py-4 dark:border-slate-800 dark:bg-slate-950">
      <div className="flex min-w-[1040px] items-center">
        <EntryEditorFields
          state={draft}
          onChange={(patch) => setDraft((current) => ({ ...current, ...patch }))}
          onToggleTag={(tagId) => setDraft((current) => ({ ...current, tags: toggleTagId(current.tags, tagId) }))}
          onProjectChange={(projectId) => void commitPatchedDraft({ projectId })}
          projects={projects}
          tags={tags}
          t={t}
          isRtl={false}
          compact
          portalOwnerId={editorOwnerId}
        />

        <div className="flex h-12 shrink-0 items-center border-s border-slate-200 px-5 text-sm font-semibold text-slate-700 dark:border-slate-800 dark:text-slate-200">
          {formatDuration(entry)}
        </div>

        <button
          type="button"
          onClick={() => onRestart(entry)}
          className="inline-flex h-12 w-10 shrink-0 items-center justify-center border-s border-slate-200 bg-transparent text-slate-400 transition-colors hover:bg-slate-50 hover:text-slate-700 dark:border-slate-800 dark:text-slate-500 dark:hover:bg-slate-900 dark:hover:text-white"
          title="Start from this entry"
        >
          <Play className="h-4 w-4" />
        </button>

        <div className="border-s border-slate-200 dark:border-slate-800">
          <DeleteEntryButton onDelete={() => onDelete(entry)} />
        </div>
      </div>

      {validationMessage && (
        <div className="px-1 pb-3 pt-2">
          <p className="text-xs font-medium text-amber-600 dark:text-amber-400">{validationMessage}</p>
        </div>
      )}
    </div>
  );
}

function MobileRecordedEntryCard({
  entry,
  t,
  projects,
  tags,
  onEdit,
  onDelete,
  onRequestRestart,
}: {
  entry: TimeEntry;
  t: any;
  projects: Project[];
  tags: Tag[];
  onEdit: (entry: TimeEntry) => void;
  onDelete: (entry: TimeEntry) => void;
  onRequestRestart: (entry: TimeEntry) => void;
}) {
  const project = projects.find((item) => item.id === entry.project);
  const entryTags = tags.filter((tag) => entry.tags.includes(tag.id));
  const wrapperRef = useRef<HTMLDivElement>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);
  const touchStartXRef = useRef<number | null>(null);
  const [menuOpen, setMenuOpen] = useState(false);
  const [swipeOffset, setSwipeOffset] = useState(0);
  const [menuStyle, setMenuStyle] = useState<React.CSSProperties>({});

  useEffect(() => {
    if (!menuOpen) return;

    const handlePointerDown = (event: MouseEvent) => {
      if (
        wrapperRef.current?.contains(event.target as Node) ||
        dropdownRef.current?.contains(event.target as Node)
      ) {
        return;
      }
      setMenuOpen(false);
    };

    document.addEventListener("mousedown", handlePointerDown);
    return () => document.removeEventListener("mousedown", handlePointerDown);
  }, [menuOpen]);

  useEffect(() => {
    if (!menuOpen || !buttonRef.current) return;

    const rect = buttonRef.current.getBoundingClientRect();
    const dropdownWidth = 168;
    const spaceBelow = window.innerHeight - rect.bottom;
    const openUpward = spaceBelow < 180 && rect.top > spaceBelow;

    setMenuStyle({
      position: "fixed",
      top: openUpward ? `${rect.top - 8}px` : `${rect.bottom + 8}px`,
      left: `${Math.max(12, rect.right - dropdownWidth)}px`,
      width: `${dropdownWidth}px`,
      transform: openUpward ? "translateY(-100%)" : "none",
      zIndex: 100000,
    });
  }, [menuOpen]);

  useEffect(() => {
    const closeMenu = () => setMenuOpen(false);

    if (menuOpen) {
      window.addEventListener("resize", closeMenu);
      window.addEventListener("scroll", closeMenu, true);
    }

    return () => {
      window.removeEventListener("resize", closeMenu);
      window.removeEventListener("scroll", closeMenu, true);
    };
  }, [menuOpen]);

  const closeSwipe = () => {
    touchStartXRef.current = null;
    setSwipeOffset(0);
  };

  const handleTouchStart = (event: React.TouchEvent<HTMLDivElement>) => {
    if (menuOpen) {
      setMenuOpen(false);
    }
    touchStartXRef.current = event.touches[0]?.clientX ?? null;
  };

  const handleTouchMove = (event: React.TouchEvent<HTMLDivElement>) => {
    if (touchStartXRef.current === null) return;
    const delta = (event.touches[0]?.clientX ?? 0) - touchStartXRef.current;
    setSwipeOffset(Math.max(-88, Math.min(88, delta)));
  };

  const handleTouchEnd = () => {
    if (swipeOffset <= -72) {
      closeSwipe();
      onDelete(entry);
      return;
    }

    if (swipeOffset >= 72) {
      closeSwipe();
      onRequestRestart(entry);
      return;
    }

    closeSwipe();
  };

  return (
    <div ref={wrapperRef} className="relative overflow-hidden border-b border-slate-200 bg-slate-100/70 dark:border-slate-800 dark:bg-slate-900/70 md:hidden">
      <div className="pointer-events-none absolute inset-y-0 left-0 flex w-24 items-center justify-start bg-emerald-500/12 ps-4 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300">
        <Play className="h-4 w-4" />
      </div>
      <div className="pointer-events-none absolute inset-y-0 right-0 flex w-24 items-center justify-end bg-red-500/12 pe-4 text-red-700 dark:bg-red-500/10 dark:text-red-300">
        <Trash2 className="h-4 w-4" />
      </div>

      <div
        className="relative bg-white px-4 py-5 transition-transform duration-150 ease-out dark:bg-slate-950"
        style={{ transform: `translateX(${swipeOffset}px)` }}
        onTouchStart={handleTouchStart}
        onTouchMove={handleTouchMove}
        onTouchEnd={handleTouchEnd}
        onTouchCancel={closeSwipe}
      >
        <div className="absolute end-3 top-3">
          <button
            ref={buttonRef}
            type="button"
            onClick={() => setMenuOpen((current) => !current)}
            className="inline-flex h-9 w-9 items-center justify-center rounded-full text-slate-400 transition-colors hover:bg-slate-100 hover:text-slate-700 dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-slate-100"
            title={t.actions?.more || "More"}
          >
            <MoreVertical className="h-4 w-4" />
          </button>
        </div>

        <div className="pe-10">
          <div className="flex items-start justify-between gap-3">
            <div className="min-w-0 flex-1">
              <div className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
                <p className="max-w-[14rem] truncate text-sm font-medium text-slate-800 dark:text-slate-100">
                  {entry.description || t.timesheet?.emptyDescription || "No description"}
                </p>
                {project && (
                  <span className="inline-flex min-w-0 items-center gap-2 text-xs">
                    <span className="shrink-0 text-sky-600 dark:text-sky-400">{"\u2022"}</span>
                    <span className="max-w-[10rem] truncate font-medium text-sky-600 dark:text-sky-400">{project.name}</span>
                  </span>
                )}
                {project?.client?.name && (
                  <span className="max-w-[8rem] truncate text-xs text-slate-500 dark:text-slate-400">
                    - {project.client.name}
                  </span>
                )}
              </div>

              <div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-slate-500 dark:text-slate-400">
                <span>{formatTimeOnly(entry.start_time)} - {formatTimeOnly(entry.end_time)}</span>
                {/* <span>{formatDateTime(entry.start_time, "en")}</span> */}
              </div>
            </div>

            <div className="shrink-0 text-right">
              <p className="text-base font-semibold text-slate-900 dark:text-white">{formatDuration(entry)}</p>
            </div>
          </div>

          {(entryTags.length > 0 || entry.is_billable) && (
            <div className="mt-3 flex flex-wrap items-center gap-2">
              {entryTags.length > 0 && (
                <span className="inline-flex min-w-0 items-center rounded-md bg-sky-50 px-2 py-1 text-[11px] font-medium text-sky-700 dark:bg-sky-500/15 dark:text-sky-300">
                  {entryTags.map((tag) => tag.name).join(" | ")}
                </span>
              )}
              {entry.is_billable && (
                <span className="inline-flex h-6 w-6 items-center justify-center rounded-full bg-sky-50 text-sky-600 dark:bg-sky-500/15 dark:text-sky-300">
                  <DollarSign className="h-3.5 w-3.5" />
                </span>
              )}
            </div>
          )}
        </div>
      </div>

      {menuOpen &&
        createPortal(
          <div
            ref={dropdownRef}
            style={menuStyle}
            className="rounded-xl border border-slate-200 bg-white p-1 shadow-xl dark:border-slate-700 dark:bg-slate-900"
          >
            <button
              type="button"
              onClick={() => {
                setMenuOpen(false);
                onEdit(entry);
              }}
              className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-slate-700 transition-colors hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800"
            >
              <Pencil className="h-4 w-4" />
              {t.actions?.edit || "Edit"}
            </button>
            <button
              type="button"
              onClick={() => {
                setMenuOpen(false);
                onRequestRestart(entry);
              }}
              className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-slate-700 transition-colors hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800"
            >
              <Play className="h-4 w-4" />
              {t.timesheet?.startTimer || "Start"}
            </button>
            <button
              type="button"
              onClick={() => {
                setMenuOpen(false);
                onDelete(entry);
              }}
              className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 transition-colors hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-500/10"
            >
              <Trash2 className="h-4 w-4" />
              {t.actions?.delete || "Delete"}
            </button>
          </div>,
          document.body,
        )}
    </div>
  );
}

export default function Timesheet() {
  const { t, lang } = useTranslation();
  const { activeWorkspace } = useWorkspace();
  const isRtl = lang === "fa";
  const extendedTimesheet = (t.timesheet as {
    deleteTitle?: string;
    deleteConfirmMessage?: string;
    saveSuccess?: string;
    saveError?: string;
    clearFilters?: string;
    customFromLabel?: string;
    customToLabel?: string;
    allClientsLabel?: string;
    allProjectsLabel?: string;
    allTagsLabel?: string;
    showFiltersLabel?: string;
    hideFiltersLabel?: string;
    applyFiltersLabel?: string;
    clientFilterPrefix?: string;
    projectFilterPrefix?: string;
    tagFilterPrefix?: string;
    fromFilterPrefix?: string;
    toFilterPrefix?: string;
    restartConfirmMessage?: string;
  }) || {};

  const [projects, setProjects] = useState<Project[]>([]);
  const [tags, setTags] = useState<Tag[]>([]);
  const [groupedHistory, setGroupedHistory] = useState<TimeEntryGroupWeek[]>([]);
  const [activeRunningEntry, setActiveRunningEntry] = useState<TimeEntry | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [filters, setFilters] = useState<TimeEntryFilters>(DEFAULT_ENTRY_FILTERS);
  const [hasMoreHistory, setHasMoreHistory] = useState(false);
  const [nextOffset, setNextOffset] = useState<number | null>(0);
  const [limit] = useState(20);
  const [ticker, setTicker] = useState(Date.now());

  const [modalMode, setModalMode] = useState<EntryModalMode>(null);
  const [formState, setFormState] = useState<EntryFormState>(EMPTY_FORM);
  const [editingEntry, setEditingEntry] = useState<TimeEntry | null>(null);
  const [isSaving, setIsSaving] = useState(false);

  const [timerDraft, setTimerDraft] = useState<TimerDraftState>(EMPTY_TIMER_DRAFT);
  const [isStartingTimer, setIsStartingTimer] = useState(false);
  const desktopTimerRef = useRef<HTMLDivElement>(null);
  const mobileTimerRef = useRef<HTMLDivElement>(null);
  const timerDraftSignatureRef = useRef(serializeTimerDraft(EMPTY_TIMER_DRAFT));
  const pendingTimerSignatureRef = useRef<string | null>(serializeTimerDraft(EMPTY_TIMER_DRAFT));
  const isTimerSavingRef = useRef(false);
  const timerEditorOwnerId = "running-timer-editor";

  const [deleteModal, setDeleteModal] = useState<{ isOpen: boolean; entry: TimeEntry | null }>({
    isOpen: false,
    entry: null,
  });
  const [isDeleting, setIsDeleting] = useState(false);
  const [restartModal, setRestartModal] = useState<{ isOpen: boolean; entry: TimeEntry | null }>({
    isOpen: false,
    entry: null,
  });
  const [isRestarting, setIsRestarting] = useState(false);
  const [discardTimerModal, setDiscardTimerModal] = useState<{ isOpen: boolean; entry: TimeEntry | null }>({
    isOpen: false,
    entry: null,
  });
  const [isDiscardingTimer, setIsDiscardingTimer] = useState(false);

  const runningEntry = activeRunningEntry;

  useEffect(() => {
    if (!runningEntry) return;

    const intervalId = window.setInterval(() => setTicker(Date.now()), 1000);
    return () => window.clearInterval(intervalId);
  }, [runningEntry]);

  useEffect(() => {
    if (!activeWorkspace?.id) return;

    const loadOptions = async () => {
      try {
        const [projectsData, tagsData] = await Promise.all([
          getProjects(activeWorkspace.id, { limit: 100, offset: 0, ordering: "name" }),
          getTags(activeWorkspace.id, { limit: 100, offset: 0, ordering: "name" }),
        ]);

        setProjects(projectsData.results || []);
        setTags(tagsData.results || []);
      } catch (error) {
        console.error(error);
        toast.error(t.timesheet?.optionsError || "Failed to load projects and tags");
      }
    };

    void loadOptions();
  }, [activeWorkspace?.id, t.timesheet?.optionsError]);

  useEffect(() => {
    setSearchQuery("");
    setFilters(DEFAULT_ENTRY_FILTERS);
    setGroupedHistory([]);
    setNextOffset(0);
    setHasMoreHistory(false);
  }, [activeWorkspace?.id]);

  useEffect(() => {
    setGroupedHistory([]);
    setNextOffset(0);
    setHasMoreHistory(false);
  }, [filters, searchQuery]);

  useEffect(() => {
    if (!filters.clientId || !filters.projectId) return;

    const projectStillMatchesClient = projects.some(
      (project) => project.id === filters.projectId && project.client?.id === filters.clientId,
    );

    if (!projectStillMatchesClient) {
      setFilters((current) => ({ ...current, projectId: "" }));
    }
  }, [filters.clientId, filters.projectId, projects]);

  const loadHistory = useCallback(async ({ offset = 0, append = false }: { offset?: number; append?: boolean } = {}) => {
    if (!activeWorkspace?.id) return;

    try {
      if (append) {
        setIsLoadingMore(true);
      } else {
        setIsLoading(true);
      }
      const params: TimeEntryListParams = {
        limit,
        offset,
        search: searchQuery,
        status: "ended",
        project: filters.projectId || undefined,
        client: filters.clientId || undefined,
        tags: filters.tagIds,
        started_after: filters.startedAfter || undefined,
        started_before: filters.startedBefore || undefined,
      };
      const data = await getTimeEntries(activeWorkspace.id, params);
      setGroupedHistory((current) => (append ? mergeGroupedHistory(current, data.groups || []) : (data.groups || [])));
      setHasMoreHistory(Boolean(data.has_more));
      setNextOffset(data.next_offset ?? null);
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.fetchError || "Failed to load time entries");
    } finally {
      if (append) {
        setIsLoadingMore(false);
      } else {
        setIsLoading(false);
      }
    }
  }, [activeWorkspace?.id, filters, limit, searchQuery, t.timesheet?.fetchError]);

  const loadRunningEntry = useCallback(async () => {
    if (!activeWorkspace?.id) {
      setActiveRunningEntry(null);
      return;
    }

    try {
      const data = await getTimeEntries(activeWorkspace.id, {
        limit: 1,
        offset: 0,
        status: "running",
      });
      const entry = data.groups?.[0]?.days?.[0]?.entries?.[0] || null;
      setActiveRunningEntry(entry);
    } catch (error) {
      console.error(error);
    }
  }, [activeWorkspace?.id]);

  useEffect(() => {
    if (!activeWorkspace?.id) return;

    const timeoutId = window.setTimeout(() => {
      void loadHistory();
    }, 250);

    return () => window.clearTimeout(timeoutId);
  }, [activeWorkspace?.id, limit, loadHistory, filters, searchQuery]);

  useEffect(() => {
    void loadRunningEntry();
  }, [loadRunningEntry]);

  useEffect(() => {
    if (!runningEntry) {
      timerDraftSignatureRef.current = serializeTimerDraft(EMPTY_TIMER_DRAFT);
      pendingTimerSignatureRef.current = timerDraftSignatureRef.current;
      setTimerDraft(EMPTY_TIMER_DRAFT);
      return;
    }

    const nextDraft = buildTimerDraftState(runningEntry);
    timerDraftSignatureRef.current = serializeTimerDraft(nextDraft);
    pendingTimerSignatureRef.current = timerDraftSignatureRef.current;
    setTimerDraft(nextDraft);
  }, [runningEntry]);

  const isInsideTimerEditorContext = useCallback((target: EventTarget | null) => {
    if (!(target instanceof Node)) return false;
    if (desktopTimerRef.current?.contains(target) || mobileTimerRef.current?.contains(target)) return true;
    return target instanceof Element && Boolean(target.closest(`[data-entry-editor-owner="${timerEditorOwnerId}"]`));
  }, [timerEditorOwnerId]);

  const commitTimerDraft = useCallback(async () => {
    const saveErrorText = extendedTimesheet.saveError || "Failed to save time entry";
    const saveSuccessText = extendedTimesheet.saveSuccess || "Time entry saved";

    if (!runningEntry) return false;

    const currentSignature = serializeTimerDraft(timerDraft);
    if (currentSignature === timerDraftSignatureRef.current) {
      return false;
    }

    if (isTimerSavingRef.current || pendingTimerSignatureRef.current === currentSignature) {
      return false;
    }

    isTimerSavingRef.current = true;
    pendingTimerSignatureRef.current = currentSignature;

    try {
      const updatedEntry = await updateTimeEntry(runningEntry.id, {
        description: timerDraft.description.trim(),
        project_id: timerDraft.projectId || null,
        tags: timerDraft.tags,
        is_billable: timerDraft.isBillable,
      });

      const syncedDraft = buildTimerDraftState(updatedEntry);
      const syncedSignature = serializeTimerDraft(syncedDraft);
      timerDraftSignatureRef.current = syncedSignature;
      pendingTimerSignatureRef.current = syncedSignature;
      setTimerDraft(syncedDraft);
      setActiveRunningEntry(updatedEntry);
      toast.success(saveSuccessText);
      return true;
    } catch (error) {
      console.error(error);
      pendingTimerSignatureRef.current = null;
      toast.error(saveErrorText);
      return false;
    } finally {
      isTimerSavingRef.current = false;
    }
  }, [extendedTimesheet.saveError, extendedTimesheet.saveSuccess, runningEntry, timerDraft]);

  useEffect(() => {
    if (!runningEntry) return;

    const handlePointerDown = (event: MouseEvent) => {
      if (isInsideTimerEditorContext(event.target)) return;
      void commitTimerDraft();
    };

    document.addEventListener("mousedown", handlePointerDown);
    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
    };
  }, [commitTimerDraft, isInsideTimerEditorContext, runningEntry]);

  const handleTimerBlurCapture = () => {
    window.setTimeout(() => {
      if (isInsideTimerEditorContext(document.activeElement)) return;
      void commitTimerDraft();
    }, 0);
  };

  const closeCreateModal = () => {
    if (isSaving) return;
    setModalMode(null);
    setEditingEntry(null);
    setFormState(EMPTY_FORM);
  };

  const openCreateModal = () => {
    setModalMode("manual");
    setEditingEntry(null);
    setFormState(buildEntryFormState());
  };

  const openEditModal = (entry: TimeEntry) => {
    setEditingEntry(entry);
    setModalMode("edit");
    setFormState(buildEntryFormState(entry));
  };

  const handleSaveEntryModal = async () => {
    if (modalMode === "manual" && !activeWorkspace?.id) return;

    const { payload, error } = buildPayloadFromState(formState, {
      includeWorkspace: modalMode === "manual",
      workspaceId: activeWorkspace?.id,
    });

    if (!payload) {
      toast.error(error || (t.timesheet?.saveError || "Failed to save time entry"));
      return;
    }

    try {
      setIsSaving(true);
      if (modalMode === "edit" && editingEntry) {
        const updatedEntry = await updateTimeEntry(editingEntry.id, payload);
        setGroupedHistory((current) => updateGroupedHistoryEntry(current, updatedEntry));
        toast.success(t.timesheet?.updateSuccess || "Time entry updated successfully.");
      } else {
        if (!activeWorkspace?.id) return;
        await createTimeEntry(payload);
        toast.success(t.timesheet?.createSuccess || "Time entry created");
        await loadHistory();
        await loadRunningEntry();
      }
      setModalMode(null);
      setEditingEntry(null);
      setFormState(EMPTY_FORM);
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.saveError || "Failed to save time entry");
    } finally {
      setIsSaving(false);
    }
  };

  const handleStartTimer = async () => {
    if (!activeWorkspace?.id || runningEntry) return;

    try {
      setIsStartingTimer(true);
      await createTimeEntry({
        workspace_id: activeWorkspace.id,
        description: timerDraft.description.trim(),
        project_id: timerDraft.projectId || null,
        start_time: new Date().toISOString(),
        tags: timerDraft.tags,
        is_billable: timerDraft.isBillable,
      });

      toast.success(t.timesheet?.startSuccess || "Timer started");
      await loadHistory();
      await loadRunningEntry();
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.saveError || "Failed to save time entry");
    } finally {
      setIsStartingTimer(false);
    }
  };

  const handleStop = async (entry: TimeEntry) => {
    try {
      await stopTimeEntry(entry.id);
      toast.success(t.timesheet?.stopSuccess || "Timer stopped");
      timerDraftSignatureRef.current = serializeTimerDraft(EMPTY_TIMER_DRAFT);
      setTimerDraft(EMPTY_TIMER_DRAFT);
      await loadHistory();
      await loadRunningEntry();
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.stopError || "Failed to stop timer");
    }
  };

  const handleRestartFromEntry = async (entry: TimeEntry) => {
    if (!activeWorkspace?.id || runningEntry) return;

    try {
      await createTimeEntry({
        workspace_id: activeWorkspace.id,
        description: entry.description,
        project_id: entry.project,
        tags: entry.tags,
        is_billable: entry.is_billable,
        start_time: new Date().toISOString(),
      });

      toast.success(t.timesheet?.startSuccess || "Timer started");
      await loadHistory();
      await loadRunningEntry();
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.saveError || "Failed to save time entry");
    }
  };

  const openDeleteModal = (entry: TimeEntry) => {
    setDeleteModal({ isOpen: true, entry });
  };

  const openRestartModal = (entry: TimeEntry) => {
    setRestartModal({ isOpen: true, entry });
  };

  const closeDeleteModal = () => {
    if (isDeleting) return;
    setDeleteModal({ isOpen: false, entry: null });
  };

  const closeRestartModal = () => {
    if (isRestarting) return;
    setRestartModal({ isOpen: false, entry: null });
  };

  const openDiscardTimerModal = () => {
    if (!runningEntry || isDiscardingTimer) return;
    setDiscardTimerModal({ isOpen: true, entry: runningEntry });
  };

  const closeDiscardTimerModal = () => {
    if (isDiscardingTimer) return;
    setDiscardTimerModal({ isOpen: false, entry: null });
  };

  const confirmDelete = async () => {
    if (!deleteModal.entry) return;

    try {
      setIsDeleting(true);
      await deleteTimeEntry(deleteModal.entry.id);
      toast.success(t.timesheet?.deleteSuccess || "Time entry deleted");
      setDeleteModal({ isOpen: false, entry: null });
      setGroupedHistory((current) =>
        current
          .map((week) => ({
            ...week,
            days: week.days
              .map((day) => ({
                ...day,
                entries: day.entries.filter((entry) => entry.id !== deleteModal.entry!.id),
              }))
              .filter((day) => day.entries.length > 0)
              .map((day) => ({ ...day, total_ms: day.entries.reduce((sum, item) => sum + getEntryDurationMs(item), 0) })),
          }))
          .filter((week) => week.days.length > 0)
          .map((week) => ({ ...week, total_ms: week.days.reduce((sum, day) => sum + day.total_ms, 0) })),
      );
      await loadRunningEntry();
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.deleteError || "Failed to delete time entry");
    } finally {
      setIsDeleting(false);
    }
  };

  const confirmRestart = async () => {
    if (!restartModal.entry) return;

    try {
      setIsRestarting(true);
      await handleRestartFromEntry(restartModal.entry);
      setRestartModal({ isOpen: false, entry: null });
    } finally {
      setIsRestarting(false);
    }
  };

  const handleEntryUpdated = useCallback((updatedEntry: TimeEntry) => {
    setGroupedHistory((current) => updateGroupedHistoryEntry(current, updatedEntry));
    if (!updatedEntry.end_time) {
      setActiveRunningEntry(updatedEntry);
    }
  }, []);

  const handleApplyFilters = useCallback((nextSearchQuery: string, nextFilters: TimeEntryFilters) => {
    setSearchQuery(nextSearchQuery);
    setFilters(nextFilters);
  }, []);

  const handleClearFilters = useCallback(() => {
    setSearchQuery("");
    setFilters(DEFAULT_ENTRY_FILTERS);
  }, []);

  const handleLoadMore = useCallback(() => {
    if (!hasMoreHistory || nextOffset === null || isLoadingMore) return;
    void loadHistory({ offset: nextOffset, append: true });
  }, [hasMoreHistory, isLoadingMore, loadHistory, nextOffset]);

  const handleDiscardTimerDraft = useCallback(async () => {
    if (!discardTimerModal.entry || isDiscardingTimer) return;

    try {
      setIsDiscardingTimer(true);

      await deleteTimeEntry(discardTimerModal.entry.id);
      timerDraftSignatureRef.current = serializeTimerDraft(EMPTY_TIMER_DRAFT);
      setTimerDraft(EMPTY_TIMER_DRAFT);
      setActiveRunningEntry(null);
      setDiscardTimerModal({ isOpen: false, entry: null });
      toast.success(t.timesheet?.deleteSuccess || "Time entry deleted");
      await loadHistory();
      await loadRunningEntry();
    } catch (error) {
      console.error(error);
      toast.error(t.timesheet?.deleteError || "Failed to delete time entry");
    } finally {
      setIsDiscardingTimer(false);
    }
  }, [discardTimerModal.entry, isDiscardingTimer, loadHistory, loadRunningEntry, t.timesheet?.deleteError, t.timesheet?.deleteSuccess]);

  if (!activeWorkspace) {
    return <div className="p-6 text-center text-slate-500">{t.timesheet?.selectWorkspace || t.clients.selectWorkspace}</div>;
  }

  return (
    <div className="flex min-h-[calc(100vh-73px)] flex-col bg-slate-100/70 p-4 dark:bg-slate-900">
      <div className="mb-4 flex items-center justify-between">
        <h1 className="text-sm font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">
          {t.timesheet?.title || "Timesheet"}
        </h1>
      </div>

      <div
        ref={desktopTimerRef}
        onBlurCapture={handleTimerBlurCapture}
        className="mb-4 hidden overflow-x-auto rounded-xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-950 md:block"
      >
        <div className="flex min-w-[1040px] items-center h-20 px-3">
          <div className="min-w-[360px] flex-1">
            <Input
              value={timerDraft.description}
              placeholder={t.timesheet?.descriptionPlaceholder || "What are you working on?"}
              onChange={(event) => setTimerDraft((current) => ({ ...current, description: event.target.value }))}
              disabled={isStartingTimer}
              className="h-12 rounded-none border-0 bg-transparent dark:bg-transparent px-5 text-sm shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
            />
          </div>

          <div className="flex shrink-0 items-center">
            <Select
              value={timerDraft.projectId}
              onChange={(value) => setTimerDraft((current) => ({ ...current, projectId: String(value) }))}
              options={[
                { value: "", label: t.timesheet?.projectLabel || "Project" },
                ...projects.map((project) => ({ value: project.id, label: project.name })),
              ]}
              className="min-w-[170px]"
              buttonClassName="h-12 w-full rounded-none border-0 bg-transparent px-3 text-sm text-sky-600 shadow-none outline-none dark:bg-transparent dark:text-sky-400 focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0"
              disabled={isStartingTimer}
              portalOwnerId={timerEditorOwnerId}
            />
          </div>

          <div className="flex shrink-0 items-center">
            <TagMultiSelect
              tags={tags}
              selectedTags={timerDraft.tags}
              onToggleTag={(tagId) =>
                setTimerDraft((current) => ({ ...current, tags: toggleTagId(current.tags, tagId) }))
              }
              emptyHint={t.timesheet?.noTagsHint || "Create tags first from the Tags page."}
              title={t.tags?.title || "Tags"}
              compact
              portalOwnerId={timerEditorOwnerId}
            />
          </div>

          <div className="flex shrink-0 items-center">
            <BillableIconButton
              checked={timerDraft.isBillable}
              onChange={(checked) => setTimerDraft((current) => ({ ...current, isBillable: checked }))}
              label={t.timesheet?.billable || "Billable"}
              disabled={isStartingTimer}
              compact
            />
          </div>

          <div className="flex h-12 shrink-0 items-center px-5 text-lg font-semibold text-slate-900 dark:text-white">
            {runningEntry ? formatDuration(runningEntry, ticker) : "00:00:00"}
          </div>

          <div className="ms-2 flex shrink-0 items-center gap-2">
            {runningEntry ? (
              <>
                <Button
                  variant="destructive"
                  size="icon"
                  onClick={() => void handleStop(runningEntry)}
                  className="h-12 w-12 rounded-md"
                  title={t.timesheet?.stopTimer || "Stop"}
                  aria-label={t.timesheet?.stopTimer || "Stop"}
                >
                  <Square className="h-4 w-4 fill-current" />
                </Button>
                <Button
                  variant="secondary"
                  size="icon"
                  onClick={openDiscardTimerModal}
                  disabled={isDiscardingTimer}
                  className="h-12 w-12 rounded-md"
                  title={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
                  aria-label={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
                >
                  {isDiscardingTimer ? "..." : <Trash2 className="h-4 w-4" />}
                </Button>
              </>
            ) : (
              <Button
                onClick={() => void handleStartTimer()}
                disabled={isStartingTimer}
                size="icon"
                className="h-12 w-12 rounded-md"
                title={t.timesheet?.startTimer || "Start"}
                aria-label={t.timesheet?.startTimer || "Start"}
              >
                {isStartingTimer ? "..." : <Play className="h-4 w-4 fill-current" />}
              </Button>
            )}
          </div>

        </div>
      </div>

      <div
        ref={mobileTimerRef}
        onBlurCapture={handleTimerBlurCapture}
        className="mb-4 rounded-xl border border-slate-200 bg-white p-3 shadow-sm dark:border-slate-800 dark:bg-slate-950 md:hidden"
      >
        <div className="space-y-3">
          <Input
            value={timerDraft.description}
            placeholder={t.timesheet?.descriptionPlaceholder || "What are you working on?"}
            onChange={(event) => setTimerDraft((current) => ({ ...current, description: event.target.value }))}
            disabled={isStartingTimer}
            className="h-10 border-slate-200 bg-slate-50 text-sm dark:border-slate-700 dark:bg-slate-900"
          />

          <div className="grid grid-cols-[minmax(0,1fr)_auto] gap-2">
            <Select
              value={timerDraft.projectId}
              onChange={(value) => setTimerDraft((current) => ({ ...current, projectId: String(value) }))}
              options={[
                { value: "", label: t.timesheet?.projectLabel || "Project" },
                ...projects.map((project) => ({ value: project.id, label: project.name })),
              ]}
              className="w-full"
              buttonClassName="h-10 w-full rounded-md border border-slate-200 bg-slate-50 px-3 text-sm shadow-none outline-none dark:border-slate-700 dark:bg-slate-900 focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0"
              disabled={isStartingTimer}
              portalOwnerId={timerEditorOwnerId}
            />

            <div className="flex h-10 min-w-[110px] items-center justify-center rounded-md border border-slate-200 bg-slate-50 px-3 text-sm font-semibold text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-white">
              {runningEntry ? formatDuration(runningEntry, ticker) : "00:00:00"}
            </div>
          </div>

          <div className="flex items-center justify-between gap-2">
            <div className="flex min-w-0 items-center gap-2">
              <TagMultiSelect
                tags={tags}
                selectedTags={timerDraft.tags}
                onToggleTag={(tagId) =>
                  setTimerDraft((current) => ({ ...current, tags: toggleTagId(current.tags, tagId) }))
                }
                emptyHint={t.timesheet?.noTagsHint || "Create tags first from the Tags page."}
                title={t.tags?.title || "Tags"}
                compact
                portalOwnerId={timerEditorOwnerId}
              />

              <BillableIconButton
                checked={timerDraft.isBillable}
                onChange={(checked) => setTimerDraft((current) => ({ ...current, isBillable: checked }))}
                label={t.timesheet?.billable || "Billable"}
                disabled={isStartingTimer}
                compact
              />
            </div>

            <div className="flex shrink-0 items-center gap-2">
              {runningEntry ? (
                <>
                  <Button
                    variant="destructive"
                    size="icon"
                    onClick={() => void handleStop(runningEntry)}
                    className="h-10 w-10 rounded-md"
                    title={t.timesheet?.stopTimer || "Stop"}
                    aria-label={t.timesheet?.stopTimer || "Stop"}
                  >
                    <Square className="h-4 w-4 fill-current" />
                  </Button>
                  <Button
                    variant="secondary"
                    size="icon"
                    onClick={openDiscardTimerModal}
                    disabled={isDiscardingTimer}
                    className="h-10 w-10 rounded-md"
                    title={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
                    aria-label={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
                  >
                    {isDiscardingTimer ? "..." : <Trash2 className="h-4 w-4" />}
                  </Button>
                </>
              ) : (
                <Button
                  onClick={() => void handleStartTimer()}
                  disabled={isStartingTimer}
                  size="icon"
                  className="h-10 w-10 rounded-md"
                  title={t.timesheet?.startTimer || "Start"}
                  aria-label={t.timesheet?.startTimer || "Start"}
                >
                  {isStartingTimer ? "..." : <Play className="h-4 w-4 fill-current" />}
                </Button>
              )}
            </div>
          </div>
        </div>
      </div>

      <div className="mb-4">
        <TimesheetFilterBar
          searchQuery={searchQuery}
          filters={filters}
          onApply={handleApplyFilters}
          onClearFilters={handleClearFilters}
          projects={projects}
          tags={tags}
          searchPlaceholder={t.timesheet?.searchPlaceholder || "Search time entries..."}
          labels={{
            project: t.timesheet?.projectLabel || "Project",
            client: t.projects?.clientLabel || "Client",
            tags: t.tags?.title || "Tags",
            clear: extendedTimesheet.clearFilters || "Clear filters",
            customFrom: extendedTimesheet.customFromLabel || "From date",
            customTo: extendedTimesheet.customToLabel || "To date",
            allClients: extendedTimesheet.allClientsLabel || "All clients",
            allProjects: extendedTimesheet.allProjectsLabel || "All projects",
            allTags: extendedTimesheet.allTagsLabel || "All tags",
            showFilters: extendedTimesheet.showFiltersLabel || "Show filters",
            hideFilters: extendedTimesheet.hideFiltersLabel || "Hide filters",
            apply: extendedTimesheet.applyFiltersLabel || "Apply",
            clientPrefix: extendedTimesheet.clientFilterPrefix || "Client",
            projectPrefix: extendedTimesheet.projectFilterPrefix || "Project",
            tagPrefix: extendedTimesheet.tagFilterPrefix || "Tag",
            fromPrefix: extendedTimesheet.fromFilterPrefix || "From",
            toPrefix: extendedTimesheet.toFilterPrefix || "To",
          }}
        />
      </div>

      {isLoading ? (
        <div className="flex justify-center p-12 text-slate-500">{t.loading || "Loading..."}</div>
      ) : (
        <InfiniteScroll
          className="flex flex-1 flex-col"
          onLoadMore={handleLoadMore}
          hasMore={hasMoreHistory}
          isLoading={isLoadingMore}
        >
          <div className="mb-6 space-y-4">
            {groupedHistory.map((week) => (
              <div key={week.key} className="space-y-2">
                <div className="flex items-center justify-between px-1">
                  <p className="text-sm font-medium text-slate-700 dark:text-slate-200">
                    {formatWeekRange(new Date(`${week.week_start}T00:00:00`), lang)}
                  </p>
                  <p className="text-sm text-slate-500 dark:text-slate-400">
                    Week total: <span className="font-semibold text-slate-800 dark:text-slate-100">{formatDurationMs(week.total_ms)}</span>
                  </p>
                </div>

                {week.days.map((day) => (
                  <div key={day.key} className="border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-950">
                    <div className="flex items-center justify-between border-b border-slate-200 bg-slate-100/80 px-4 py-2 dark:border-slate-800 dark:bg-slate-900">
                      <p className="text-xs font-medium text-slate-500 dark:text-slate-400">
                        {formatDayLabel(new Date(`${day.date}T00:00:00`), lang)}
                      </p>
                      <p className="text-xs text-slate-500 dark:text-slate-400">
                        Total: <span className="font-semibold text-slate-700 dark:text-slate-200">{formatDurationMs(day.total_ms)}</span>
                      </p>
                    </div>

                    <div>
                      {day.entries.map((entry) => (
                        <div key={entry.id}>
                          <div className="hidden md:block">
                            <RecordedEntryCard
                              entry={entry}
                              t={t}
                              projects={projects}
                              tags={tags}
                              onDelete={openDeleteModal}
                              onRestart={handleRestartFromEntry}
                              onEntryUpdated={handleEntryUpdated}
                            />
                          </div>
                          <div className="md:hidden">
                            <MobileRecordedEntryCard
                              entry={entry}
                              t={t}
                              projects={projects}
                              tags={tags}
                              onEdit={openEditModal}
                              onDelete={openDeleteModal}
                              onRequestRestart={openRestartModal}
                            />
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                ))}
              </div>
            ))}

            {groupedHistory.length === 0 && (
              <div className="flex flex-col items-center justify-center border-2 border-dashed border-slate-200 py-16 text-slate-500 dark:border-slate-800 dark:text-slate-400">
                <Clock3 className="mb-3 h-10 w-10" />
                <p>{t.timesheet?.emptyState || "No time entries found"}</p>
              </div>
            )}
          </div>
        </InfiniteScroll>
      )}

      <Modal
        isOpen={modalMode === "manual" || modalMode === "edit"}
        onClose={closeCreateModal}
        title={modalMode === "edit" ? (t.timesheet?.editTitle || "Edit Time Entry") : (t.timesheet?.createTitle || "Add Time Entry")}
        maxWidth="max-w-2xl"
        footer={
          <>
            <Button variant="secondary" onClick={closeCreateModal}>
              {t.actions?.cancel || "Cancel"}
            </Button>
            <Button onClick={() => void handleSaveEntryModal()} disabled={isSaving}>
              {isSaving ? "..." : (modalMode === "edit" ? (t.save || "Save") : (t.create || "Create"))}
            </Button>
          </>
        }
      >
        <EntryEditorFields
          state={formState}
          onChange={(patch) => setFormState((current) => ({ ...current, ...patch }))}
          onToggleTag={(tagId) => setFormState((current) => ({ ...current, tags: toggleTagId(current.tags, tagId) }))}
          projects={projects}
          tags={tags}
          t={t}
          isRtl={isRtl}
        />
      </Modal>

      {deleteModal.entry && (
        <Modal
          isOpen={deleteModal.isOpen}
          onClose={closeDeleteModal}
          title={extendedTimesheet.deleteTitle || "Delete Time Entry"}
          maxWidth="max-w-md"
          footer={
            <>
              <Button variant="secondary" onClick={closeDeleteModal}>
                {t.actions?.cancel || "Cancel"}
              </Button>
              <Button variant="destructive" onClick={confirmDelete} disabled={isDeleting}>
                {isDeleting ? "..." : (t.actions?.delete || "Delete")}
              </Button>
            </>
          }
        >
          <div className="space-y-3">
            <p className="text-sm leading-relaxed text-slate-600 dark:text-slate-400">
              {extendedTimesheet.deleteConfirmMessage || "Are you sure you want to delete this time entry?"}
            </p>
            <div className="rounded-2xl border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/60">
              <p className="font-medium text-slate-900 dark:text-white">
                {deleteModal.entry.description || t.timesheet?.emptyDescription || "No description"}
              </p>
              <p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
                {formatDateTime(deleteModal.entry.start_time, lang)}
                {deleteModal.entry.end_time ? ` - ${formatDateTime(deleteModal.entry.end_time, lang)}` : ""}
              </p>
            </div>
          </div>
        </Modal>
      )}

      {restartModal.entry && (
        <Modal
          isOpen={restartModal.isOpen}
          onClose={closeRestartModal}
          title={t.timesheet?.startTimer || "Start"}
          maxWidth="max-w-md"
          footer={
            <>
              <Button variant="secondary" onClick={closeRestartModal}>
                {t.actions?.cancel || "Cancel"}
              </Button>
              <Button onClick={() => void confirmRestart()} disabled={isRestarting}>
                {isRestarting ? "..." : (t.timesheet?.startTimer || "Start")}
              </Button>
            </>
          }
        >
          <div className="space-y-3">
            <p className="text-sm leading-relaxed text-slate-600 dark:text-slate-400">
              {(extendedTimesheet.restartConfirmMessage || "Start a new running timer from this entry?")}
            </p>
            <div className="rounded-2xl border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/60">
              <p className="font-medium text-slate-900 dark:text-white">
                {restartModal.entry.description || t.timesheet?.emptyDescription || "No description"}
              </p>
              <p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
                {formatDateTime(restartModal.entry.start_time, lang)}
                {restartModal.entry.end_time ? ` - ${formatDateTime(restartModal.entry.end_time, lang)}` : ""}
              </p>
            </div>
          </div>
        </Modal>
      )}

      {discardTimerModal.entry && (
        <Modal
          isOpen={discardTimerModal.isOpen}
          onClose={closeDiscardTimerModal}
          title={(t.actions as { discard?: string } | undefined)?.discard || "Discard"}
          maxWidth="max-w-md"
          footer={
            <>
              <Button variant="secondary" onClick={closeDiscardTimerModal}>
                {t.actions?.cancel || "Cancel"}
              </Button>
              <Button variant="destructive" onClick={() => void handleDiscardTimerDraft()} disabled={isDiscardingTimer}>
                {isDiscardingTimer ? "..." : ((t.actions as { discard?: string } | undefined)?.discard || "Discard")}
              </Button>
            </>
          }
        >
          <div className="space-y-3">
            <p className="text-sm leading-relaxed text-slate-600 dark:text-slate-400">
              {extendedTimesheet.deleteConfirmMessage || "Are you sure you want to delete this time entry?"}
            </p>
            <div className="rounded-2xl border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-900/60">
              <p className="font-medium text-slate-900 dark:text-white">
                {discardTimerModal.entry.description || t.timesheet?.emptyDescription || "No description"}
              </p>
              <p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
                {formatDateTime(discardTimerModal.entry.start_time, lang)}
              </p>
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}
