import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import {
  Modal,
  ConfirmDialog,
  Field,
  TextField,
  Select,
  DateField,
  CheckboxField,
  Button,
  Card,
} from "../ui";
import useReducedMotion from "../hooks/useReducedMotion";
import {
  ResolvedCell,
  PlanCategory,
  planCategoryLabel,
  cellProjectLabel,
} from "../lib/queries/plan";
import { formatDate } from "../utils/formatters";

interface Project {
  id: number;
  name: string;
  project_number?: string;
}

/** Fields shared by every plan-record save payload the modal emits. */
interface PlanRecordFields {
  project_id: number | null;
  category: string;
  note: string;
}

/** Body for creating a plan entry (a single day or a multi-day range). */
export interface PlanEntryCreateBody extends PlanRecordFields {
  user_id: number;
  date_from: string;
  date_to: string;
}

/** Body for updating an existing plan entry (the entry id is passed separately). */
export interface PlanEntryUpdateBody extends PlanRecordFields {
  date_from: string;
  date_to: string;
}

/** Body for creating a single-day override. */
export interface PlanOverrideCreateBody extends PlanRecordFields {
  user_id: number;
  shift_date: string;
}

/** Body for updating an existing override (the override id is passed separately). */
export type PlanOverrideUpdateBody = PlanRecordFields;

export type PlanCellModalMode =
  | { kind: "closed" }
  | { kind: "create"; userId: number; date: string }
  | { kind: "create-override"; userId: number; date: string }
  | {
      kind: "edit-range";
      entryId: number;
      userId: number;
      date: string;
      range: {
        date_from: string;
        date_to: string;
        project_id: number | null;
        category: string;
        note: string;
      };
    }
  | {
      kind: "edit-override";
      overrideId: number;
      userId: number;
      date: string;
      cell: ResolvedCell;
    }
  | {
      kind: "day-in-range";
      entryId: number;
      userId: number;
      date: string;
      range: {
        date_from: string;
        date_to: string;
        project_id: number | null;
        category: string;
        note: string;
      };
    }
  | { kind: "view"; userId: number; date: string; cell: ResolvedCell }
  | { kind: "day"; userId: number; date: string; cells: ResolvedCell[] };

interface Props {
  open: boolean;
  mode: PlanCellModalMode;
  projects: Project[];
  categories: PlanCategory[];
  onClose: () => void;
  onSaveEntry: (body: PlanEntryCreateBody) => Promise<void>;
  onUpdateEntry: (id: number, body: PlanEntryUpdateBody) => Promise<void>;
  onDeleteEntry: (id: number) => Promise<void>;
  onSaveOverride: (body: PlanOverrideCreateBody) => Promise<void>;
  onUpdateOverride: (id: number, body: PlanOverrideUpdateBody) => Promise<void>;
  onDeleteOverride: (id: number) => Promise<void>;
  onCreateOverrideFromRange: (
    entryId: number,
    userId: number,
    date: string,
  ) => Promise<void>;
  onSwitchToEditRange: (entryId: number, userId: number, date: string) => void;
  /** Open the create form for a brand-new record on (userId, date). The
   * `source` picks the layer the new record lands in: "entry" in normal mode,
   * "override" in exception mode (a day whose shown records are overrides). */
  onAddRecord: (
    userId: number,
    date: string,
    source: "entry" | "override",
  ) => void;
  /** Open the right editor for one of the day's existing records. */
  onEditRecord: (cell: ResolvedCell, userId: number, date: string) => void;
}

const TrashIcon = (
  <svg
    width="14"
    height="14"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth="2"
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <polyline points="3 6 5 6 21 6" />
    <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
    <path d="M10 11v6" />
    <path d="M14 11v6" />
    <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" />
  </svg>
);

export default function PlanCellModal(props: Props) {
  const { open, mode, onClose } = props;
  if (!open || mode.kind === "closed") return null;
  if (mode.kind === "view") return <ViewModal {...props} />;
  if (mode.kind === "day-in-range")
    return <DayInRangeModal {...props} mode={mode} />;
  if (mode.kind === "day") return <DayPanel {...props} mode={mode} />;
  return <EditForm {...props} />;
}

interface EditFormInitial {
  date_from: string;
  date_to: string;
  is_range: boolean;
  project_id: number | null;
  category: string;
  note: string;
}

/**
 * Initial form values for every mode kind. Unhandled kinds get a harmless
 * default (EditForm renders null for them — but only AFTER its hooks have run).
 * Keeping this a plain function (not an inline IIFE with early returns) lets
 * EditForm call its useState hooks unconditionally, satisfying the Rules of
 * Hooks.
 */
function computeEditFormInitial(
  mode: PlanCellModalMode,
  activeCategories: { key: string }[],
): EditFormInitial {
  if (mode.kind === "create" || mode.kind === "create-override") {
    return {
      date_from: mode.date,
      date_to: mode.date,
      is_range: false,
      project_id: null,
      // Default to "work" when it's active, otherwise the first active
      // category, so a new entry never starts on a retired key.
      category: activeCategories.some((c) => c.key === "work")
        ? "work"
        : (activeCategories[0]?.key ?? "work"),
      note: "",
    };
  }
  if (mode.kind === "edit-range") {
    return {
      date_from: mode.range.date_from,
      date_to: mode.range.date_to,
      is_range: mode.range.date_from !== mode.range.date_to,
      project_id: mode.range.project_id,
      category: mode.range.category,
      note: mode.range.note,
    };
  }
  if (mode.kind === "edit-override") {
    return {
      date_from: mode.date,
      date_to: mode.date,
      is_range: false,
      project_id: mode.cell.project_id,
      category: mode.cell.category,
      note: mode.cell.note,
    };
  }
  // Unhandled kinds: harmless defaults (EditForm returns null after its hooks).
  return {
    date_from: "",
    date_to: "",
    is_range: false,
    project_id: null,
    category: "work",
    note: "",
  };
}

function EditForm(props: Props) {
  const {
    mode,
    onClose,
    onSaveEntry,
    onUpdateEntry,
    onDeleteEntry,
    onSaveOverride,
    onUpdateOverride,
    onDeleteOverride,
    projects,
  } = props;
  // An override is a single-day record — both editing one ("edit-override")
  // and creating one ("create-override") lock the date and hide the range UI.
  const isOverride =
    mode.kind === "edit-override" || mode.kind === "create-override";
  const activeCategories = props.categories.filter((c) => c.is_active);
  // Computed for ALL mode kinds (the helper has a fallback) so the hooks below
  // are unconditional.
  const initial = computeEditFormInitial(mode, activeCategories);

  const [submitting, setSubmitting] = useState(false);
  const [confirmDelete, setConfirmDelete] = useState(false);
  const [dateFrom, setDateFrom] = useState(initial.date_from);
  const [dateTo, setDateTo] = useState(initial.date_to);
  const [isRange, setIsRange] = useState(initial.is_range);
  const [projectId, setProjectId] = useState<number | null>(initial.project_id);
  const [category, setCategory] = useState(initial.category);
  const [note, setNote] = useState(initial.note);

  // Narrow mode to the kinds EditForm actually handles. The call site only ever
  // passes the four editable kinds; we guard AFTER all hooks so the hook order
  // is identical on every render (Rules of Hooks).
  if (
    mode.kind !== "create" &&
    mode.kind !== "create-override" &&
    mode.kind !== "edit-range" &&
    mode.kind !== "edit-override"
  ) {
    return null;
  }

  const title =
    mode.kind === "create"
      ? "Nový záznam plánu"
      : mode.kind === "create-override"
        ? "Nové přepsání dne"
        : mode.kind === "edit-range"
          ? "Upravit rozsah"
          : "Upravit přepsání dne";

  const handleSubmit = async () => {
    setSubmitting(true);
    try {
      if (mode.kind === "create") {
        const userId = mode.userId;
        await onSaveEntry({
          user_id: userId,
          date_from: dateFrom,
          date_to: isRange ? dateTo : dateFrom,
          project_id: projectId,
          category,
          note,
        });
      } else if (mode.kind === "create-override") {
        // An override is a single day — lock it to mode.date regardless of
        // any local dateFrom/isRange state (the date field is disabled for
        // overrides, but be explicit).
        await onSaveOverride({
          user_id: mode.userId,
          shift_date: mode.date,
          project_id: projectId,
          category,
          note,
        });
      } else if (mode.kind === "edit-range") {
        await onUpdateEntry(mode.entryId, {
          date_from: dateFrom,
          date_to: isRange ? dateTo : dateFrom,
          project_id: projectId,
          category,
          note,
        });
      } else {
        await onUpdateOverride(mode.overrideId, {
          project_id: projectId,
          category,
          note,
        });
      }
      onClose();
    } finally {
      setSubmitting(false);
    }
  };

  const handleDelete = async () => {
    setSubmitting(true);
    try {
      if (mode.kind === "edit-range") {
        await onDeleteEntry(mode.entryId);
      } else if (mode.kind === "edit-override") {
        await onDeleteOverride(mode.overrideId);
      }
      onClose();
    } finally {
      setSubmitting(false);
    }
  };

  // If the row's current category was retired/hidden, still show it (marked)
  // so the select reflects reality instead of silently snapping to the first
  // active option. The server only re-checks the category if it changes, so
  // keeping it saves fine.
  const showRetiredCategory =
    category && !activeCategories.some((c) => c.key === category);

  // Both create flows ("create" entry, "create-override") show "Vytvořit" and
  // have no Smazat button (there is no existing row to delete yet).
  const isCreate = mode.kind === "create" || mode.kind === "create-override";

  return (
    <>
      <Modal
        isOpen
        title={title}
        onClose={onClose}
        onSubmit={handleSubmit}
        submitText={isCreate ? "Vytvořit" : "Uložit"}
        loading={submitting}
      >
        {!isCreate && (
          <Box sx={{ mb: 2 }}>
            <Button
              variant="outlined"
              color="error"
              startIcon={TrashIcon}
              onClick={() => setConfirmDelete(true)}
              disabled={submitting}
            >
              Smazat
            </Button>
          </Box>
        )}
        <Field label="Datum od">
          <DateField
            value={dateFrom}
            onChange={setDateFrom}
            disabled={isOverride}
          />
        </Field>
        {!isOverride && (
          <Field label="Rozsah dnů">
            <CheckboxField
              label="Více dní"
              checked={isRange}
              onChange={setIsRange}
            />
          </Field>
        )}
        {!isOverride && isRange && (
          <Field label="Datum do">
            <DateField value={dateTo} onChange={setDateTo} />
          </Field>
        )}
        <Field label="Projekt">
          <Select
            value={projectId === null ? "" : String(projectId)}
            onChange={(val) => setProjectId(val ? Number(val) : null)}
            options={[
              { value: "", label: "— bez projektu —" },
              ...projects.map((p) => ({
                value: String(p.id),
                label: p.name,
              })),
            ]}
          />
        </Field>
        <Field label="Kategorie">
          <Select
            value={category}
            onChange={(val) => setCategory(val)}
            options={[
              ...(showRetiredCategory
                ? [
                    {
                      value: category,
                      label: `${planCategoryLabel(
                        category,
                        Object.fromEntries(
                          props.categories.map((x) => [x.key, x]),
                        ),
                      )} (skrytá)`,
                    },
                  ]
                : []),
              ...activeCategories.map((c) => ({
                value: c.key,
                label: c.label,
              })),
            ]}
          />
        </Field>
        <Field label="Text poznámky (volitelný)">
          <TextField
            multiline
            minRows={3}
            value={note}
            onChange={(e) => setNote(e.target.value)}
            inputProps={{ maxLength: 500 }}
          />
        </Field>
      </Modal>

      <ConfirmDialog
        isOpen={confirmDelete}
        title="Smazat záznam plánu?"
        message="Tato akce je nevratná (záznam bude soft-delete)."
        confirmText="Smazat"
        confirmVariant="danger"
        loading={submitting}
        onClose={() => setConfirmDelete(false)}
        onConfirm={handleDelete}
      />
    </>
  );
}

function DayInRangeModal(
  props: Props & { mode: Extract<PlanCellModalMode, { kind: "day-in-range" }> },
) {
  const { mode, onClose, onCreateOverrideFromRange, onSwitchToEditRange } =
    props;
  const reducedMotion = useReducedMotion();

  // Two clear paths: edit the whole range (default, safe) or carve out
  // a one-day override (deviates from the plan). Close = no action.
  const choices = [
    {
      key: "edit-range",
      title: "Upravit celý rozsah",
      desc: `Změní projekt, kategorii nebo poznámku pro všechny dny v rozsahu ${mode.range.date_from} – ${mode.range.date_to}.`,
      tone: "primary" as const,
      onClick: () => onSwitchToEditRange(mode.entryId, mode.userId, mode.date),
      cta: "Upravit rozsah",
    },
    {
      key: "override-day",
      title: "Upravit pouze tento den",
      desc: `Vytvoří přepsání pro ${mode.range.date_from === mode.date ? "tento" : mode.date}; zbytek rozsahu zůstane beze změny.`,
      tone: "secondary" as const,
      onClick: async () => {
        await onCreateOverrideFromRange(mode.entryId, mode.userId, mode.date);
        onClose();
      },
      cta: "Vytvořit přepsání",
    },
  ];

  return (
    <Modal
      isOpen
      title="Den je součástí rozsahu"
      onClose={onClose}
      onSubmit={onClose}
      submitText="Zavřít — ponechat rozsah beze změny"
      maxWidth="md"
    >
      <Typography variant="body2" sx={{ mb: 2 }}>
        Den <strong>{mode.date}</strong> je součástí rozsahu{" "}
        <strong>
          {mode.range.date_from} – {mode.range.date_to}
        </strong>
        . Vyberte, co chcete udělat:
      </Typography>
      <Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
        <AnimatePresence>
          {choices.map((c, i) => (
            <motion.div
              key={c.key}
              initial={reducedMotion ? false : { opacity: 0, y: 8 }}
              animate={{ opacity: 1, y: 0 }}
              exit={reducedMotion ? undefined : { opacity: 0, y: -4 }}
              transition={{
                duration: reducedMotion ? 0 : 0.22,
                delay: reducedMotion ? 0 : 0.04 + i * 0.05,
                ease: [0.16, 1, 0.3, 1],
              }}
            >
              <Card
                variant="outlined"
                sx={{
                  borderColor:
                    c.tone === "primary" ? "primary.main" : "divider",
                }}
              >
                <Box
                  sx={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "space-between",
                    gap: 2,
                    flexWrap: "wrap",
                  }}
                >
                  <Box sx={{ flex: 1, minWidth: 200 }}>
                    <Typography sx={{ fontWeight: 600 }}>{c.title}</Typography>
                    <Typography variant="body2" color="text.secondary">
                      {c.desc}
                    </Typography>
                  </Box>
                  <Button
                    variant={c.tone === "primary" ? "contained" : "outlined"}
                    color={c.tone === "primary" ? "primary" : "inherit"}
                    onClick={c.onClick}
                  >
                    {c.cta}
                  </Button>
                </Box>
              </Card>
            </motion.div>
          ))}
        </AnimatePresence>
      </Box>
    </Modal>
  );
}

function ViewModal(props: Props) {
  const { mode, onClose, projects, categories } = props;
  if (mode.kind !== "view") return null;
  const c = mode.cell;
  // Prefer the server-embedded project label; fall back to the projects-list
  // lookup only for cells that predate the embed (stale cache).
  const fallback = c.project_id
    ? (projects.find((p) => p.id === c.project_id) ?? null)
    : null;
  const projectLabel =
    cellProjectLabel(c) ??
    (fallback
      ? fallback.project_number
        ? `${fallback.project_number} — ${fallback.name}`
        : fallback.name
      : null);
  return (
    <Modal
      isOpen
      title="Detail plánu"
      onClose={onClose}
      onSubmit={onClose}
      submitText="Zavřít"
      hideCancel
    >
      <Typography sx={{ mb: 1 }}>
        <strong>Datum:</strong> {formatDate(c.shift_date)}
      </Typography>
      <Typography sx={{ mb: 1 }}>
        <strong>Kategorie:</strong>{" "}
        {planCategoryLabel(
          c.category,
          Object.fromEntries(categories.map((x) => [x.key, x])),
        )}
      </Typography>
      <Typography sx={{ mb: 1 }}>
        <strong>Projekt:</strong> {projectLabel || "—"}
      </Typography>
      <Typography sx={{ mb: 1 }}>
        <strong>Text:</strong> {c.note || "—"}
      </Typography>
      {c.rangeFrom && c.rangeTo && (
        <Typography sx={{ mb: 1 }}>
          <strong>Patří do rozsahu:</strong> {formatDate(c.rangeFrom)} –{" "}
          {formatDate(c.rangeTo)}
        </Typography>
      )}
    </Modal>
  );
}

const EditIcon = (
  <svg
    width="14"
    height="14"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth="2"
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
    <path d="M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
  </svg>
);

function DayPanel(
  props: Props & { mode: Extract<PlanCellModalMode, { kind: "day" }> },
) {
  const { mode, onClose, onAddRecord, onEditRecord, categories } = props;
  const catMap = Object.fromEntries(categories.map((x) => [x.key, x]));
  const atCap = mode.cells.length >= 3;
  const free = 3 - mode.cells.length;
  // A day is in "exception mode" when its shown records are overrides. Cells
  // are single-layer (all overrides OR all entries), so cells[0] is enough.
  // "+ Přidat záznam" must add to the layer that's showing — an override here,
  // otherwise resolveCell would hide a freshly-created entry behind the
  // existing override after refetch.
  const isOverrideMode = mode.cells[0]?.source === "override";
  return (
    <Modal
      isOpen
      title={`Plán — ${formatDate(mode.date)}`}
      onClose={onClose}
      onSubmit={onClose}
      submitText="Zavřít"
      hideCancel
    >
      <Box sx={{ display: "flex", flexDirection: "column", gap: 1.25, mb: 2 }}>
        {mode.cells.map((c, i) => {
          const color =
            catMap[c.category]?.color || "var(--mui-palette-divider)";
          const projectLabel = cellProjectLabel(c);
          return (
            <Card
              key={
                c.entryId != null
                  ? c.entryId
                  : c.overrideId != null
                    ? `o${c.overrideId}`
                    : i
              }
              variant="outlined"
            >
              <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
                <Box
                  sx={{
                    width: 4,
                    alignSelf: "stretch",
                    minHeight: 28,
                    borderRadius: 2,
                    bgcolor: color,
                  }}
                />
                <Box sx={{ flex: 1, minWidth: 0 }}>
                  <Typography sx={{ fontWeight: 600, fontSize: "0.85rem" }}>
                    {planCategoryLabel(c.category, catMap)}
                    {projectLabel ? ` · ${projectLabel}` : ""}
                  </Typography>
                  {c.note && (
                    <Typography variant="body2" color="text.secondary" noWrap>
                      {c.note}
                    </Typography>
                  )}
                </Box>
                <Button
                  variant="outlined"
                  color="inherit"
                  startIcon={EditIcon}
                  onClick={() => onEditRecord(c, mode.userId, mode.date)}
                >
                  Upravit
                </Button>
              </Box>
            </Card>
          );
        })}
      </Box>
      <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
        <Button
          variant="contained"
          color="primary"
          disabled={atCap}
          onClick={() =>
            onAddRecord(
              mode.userId,
              mode.date,
              isOverrideMode ? "override" : "entry",
            )
          }
        >
          + Přidat záznam
        </Button>
        <Typography variant="caption" color="text.secondary">
          {atCap
            ? "Maximum 3 záznamy na den"
            : `${free} ${free === 1 ? "volné místo" : "volná místa"}`}
        </Typography>
      </Box>
    </Modal>
  );
}
