import { useMemo } from "react";
import type { CSSProperties } from "react";
import {
  GridData,
  ResolvedCell,
  planCategoryLabel,
  categoryMap,
  PlanCategory,
} from "../lib/queries/plan";
import type { Project } from "../lib/queries/projects";
import PlanRangeChips from "./PlanRangeChips";

interface Props {
  data: GridData | undefined;
  canEdit: boolean;
  projects: Project[];
  categories: PlanCategory[];
  // The (userId, date) of the most recent successful mutation. The
  // matching cell gets a one-shot highlight pulse. `nonce` is part of
  // the value so back-to-back mutations on the same cell re-trigger
  // the animation (React needs a new key to re-mount the class).
  pulseKey?: { userId: number; date: string; nonce: number } | null;
  onCellClick: (
    userId: number,
    date: string,
    cell: ResolvedCell | null,
  ) => void;
}

// Full Czech weekday names, indexed by Date.getUTCDay() (0 = Sunday).
// Used in the date column "stamp" so the day name reads in full rather
// than as a 2-letter abbreviation — easier to scan when the planner
// spans several weeks.
const CZECH_WEEKDAYS = [
  "Neděle",
  "Pondělí",
  "Úterý",
  "Středa",
  "Čtvrtek",
  "Pátek",
  "Sobota",
];

function eachDay(from: string, to: string): string[] {
  const out: string[] = [];
  const a = new Date(from + "T00:00:00.000Z");
  const b = new Date(to + "T00:00:00.000Z");
  for (let d = new Date(a); d <= b; d.setUTCDate(d.getUTCDate() + 1)) {
    out.push(d.toISOString().slice(0, 10));
  }
  return out;
}

function czechWeekday(dateStr: string): string {
  return CZECH_WEEKDAYS[new Date(dateStr + "T00:00:00.000Z").getUTCDay()];
}

function isWeekend(dateStr: string): boolean {
  const day = new Date(dateStr + "T00:00:00.000Z").getUTCDay();
  return day === 0 || day === 6;
}

// Today's local date as YYYY-MM-DD. Used to mark the current row and to
// gate past-day editing. Uses local time (matches the project's date
// conventions in CLAUDE.md and the server's `assertNotPastDate` guard in
// src/services/plan.service.ts).
function todayIso(): string {
  const d = new Date();
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

// True when the given YYYY-MM-DD cell date is strictly before today (local).
// Used to gate the create / edit UI on past days — the server still enforces
// the rule with a 403, but the client should never put the user in the
// "I clicked, modal opened, error toast" state in the first place.
function isPastDate(dateStr: string, today: string): boolean {
  return dateStr < today;
}

// Split a full name into first / last for the column header.
function splitName(full: string): { first: string; last: string } {
  const parts = full.trim().split(/\s+/);
  if (parts.length === 1) return { first: parts[0], last: "" };
  return { first: parts[0], last: parts.slice(1).join(" ") };
}

// Short role label for the column header sub-line.
function shortRole(role: string | null): string {
  if (!role) return "";
  // Keep it brief — column header is tight
  if (/^admin$/i.test(role)) return "Admin";
  if (/^viewer$/i.test(role)) return "Viewer";
  return role.length > 10 ? role.slice(0, 9) + "…" : role;
}

export default function PlanGrid({
  data,
  canEdit,
  projects,
  categories,
  pulseKey,
  onCellClick,
}: Props) {
  const today = useMemo(() => todayIso(), []);
  const catMap = useMemo(() => categoryMap(categories), [categories]);

  if (!data)
    return (
      <div className="admin-loading">
        <div className="admin-spinner" />
      </div>
    );
  const days = eachDay(data.date_from, data.date_to);
  const users = data.users;
  // pulseKey?.nonce is included in the data-pulse attribute so a second
  // mutation on the same cell re-triggers the animation (CSS animations
  // don't restart unless the keyframe applies to a fresh element/class
  // binding).
  const pulseAttr = pulseKey
    ? `${pulseKey.userId}|${pulseKey.date}|${pulseKey.nonce}`
    : undefined;

  return (
    <div className="plan-grid-wrap" data-pulse={pulseAttr}>
      <table className="plan-grid">
        {/* The colgroup is what actually controls column width in a
            table — `min-width` on `<th>`/`<td>` is just a floor that
            `table-layout: auto` happily blows past. The first column
            gets a fixed width via the col element so the date stamp
            doesn't get stretched to share space with person columns. */}
        <colgroup>
          <col className="plan-grid-date-col" />
          {users.map((u) => (
            <col key={u.id} />
          ))}
        </colgroup>
        <thead>
          <tr>
            <th className="plan-grid-date-col">Datum</th>
            {users.map((u) => {
              const { first, last } = splitName(u.full_name);
              return (
                <th key={u.id}>
                  <span className="plan-person-head">
                    <span className="plan-person-dot" aria-hidden />
                    <span className="plan-person-name">
                      <strong title={u.full_name}>
                        {first}
                        {last ? ` ${last}` : ""}
                      </strong>
                      {shortRole(u.role_name) && (
                        <small>{shortRole(u.role_name)}</small>
                      )}
                    </span>
                  </span>
                </th>
              );
            })}
          </tr>
        </thead>
        <tbody>
          {days.map((date) => {
            const dow = czechWeekday(date);
            const dayNum = date.slice(8, 10);
            const isToday = date === today;
            const trCls = [
              isWeekend(date) ? "plan-grid-weekend" : "",
              isToday ? "is-today" : "",
            ]
              .filter(Boolean)
              .join(" ");
            return (
              <tr key={date} className={trCls}>
                <td className="plan-grid-date-col">
                  <span className="plan-date-stamp">
                    <span className="plan-date-daynum">{dayNum}</span>
                    <span className="plan-date-dow">{dow}</span>
                  </span>
                </td>
                {users.map((u) => {
                  const cell = data.cells[u.id]?.[date] ?? null;
                  const past = isPastDate(date, today);
                  // Past-day cells are read-only in the UI: an empty past
                  // cell is non-interactive (no create modal, no "+" hint),
                  // a past cell with data opens in view mode only. The
                  // server still enforces the past-date rule with a 403
                  // (defense in depth), but the click path here never
                  // reaches a create/edit submission for a past date.
                  const isLocked = !canEdit || past;
                  // `isPulsing` is true for the single (user, date) cell
                  // that the most recent successful mutation touched.
                  // CSS restarts the keyframe animation whenever the
                  // `nonce` changes (we embed it in data-pulse on the
                  // wrapper, see above), so back-to-back mutations on the
                  // same cell re-trigger the pulse.
                  const isPulsing =
                    !!pulseKey &&
                    pulseKey.userId === u.id &&
                    pulseKey.date === date;
                  let cls: string;
                  if (cell) {
                    cls = isLocked
                      ? `plan-cell plan-cell--readonly${past ? " plan-cell--past" : ""}`
                      : "plan-cell";
                  } else {
                    cls = isLocked
                      ? `plan-cell plan-cell--empty plan-cell--readonly${past ? " plan-cell--past" : ""}`
                      : "plan-cell plan-cell--empty";
                  }
                  if (isPulsing) cls += " plan-cell--pulse";
                  return (
                    <td key={u.id}>
                      <button
                        type="button"
                        className={cls}
                        style={
                          cell
                            ? ({
                                "--cat-color": catMap[cell.category]?.color,
                              } as CSSProperties)
                            : undefined
                        }
                        onClick={() => onCellClick(u.id, date, cell)}
                        aria-label={
                          cell
                            ? `${u.full_name}, ${date}, ${planCategoryLabel(cell.category, catMap)}`
                            : isLocked
                              ? `${u.full_name}, ${date}, ${past ? "uplynulý den" : "prázdné"} — bez záznamu`
                              : `${u.full_name}, ${date}, prázdné — přidat záznam`
                        }
                      >
                        <PlanRangeChips
                          cell={cell}
                          project={
                            cell?.project_id
                              ? (projects.find(
                                  (p) => p.id === cell.project_id,
                                ) ?? null)
                              : null
                          }
                          readonly={!canEdit}
                          categoryLabel={
                            cell ? planCategoryLabel(cell.category, catMap) : ""
                          }
                        />
                      </button>
                    </td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}
