"use client";

import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  Box,
  Button,
  Checkbox,
  IconButton,
  Paper,
  TablePagination,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Typography,
} from "@mui/material";
import EditNote from "@mui/icons-material/EditNote";
import DeleteIcon from "@mui/icons-material/Delete";
import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";
import SearchBox, { Criterion } from "../SearchBox";
import { UserResult } from "@/app/api/user";
import { deleteUser } from "@/app/api/user/actions";
import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
import { updateUser } from "@/app/api/user/client";

interface UserAuthItem {
  id: number;
  authority?: string;
  name?: string;
  description?: string | null;
  v?: number;
}

interface UserListDetail extends UserResult {
  authIds?: number[];
  auths?: UserAuthItem[];
}

interface Props {
  users: UserListDetail[];
}

function hasUserAuthority(user: UserListDetail, authorityId: number): boolean {
  return (user.auths ?? []).some(
    auth => auth.id === authorityId && (typeof auth.v === "number" ? auth.v === 1 : true),
  );
}

function cloneUserList(list: UserListDetail[]): UserListDetail[] {
  return list.map(user => ({
    ...user,
    authIds: [...(user.authIds ?? [])],
    auths: (user.auths ?? []).map(auth => ({ ...auth })),
  }));
}

function computeAuthorityChanges(
  baseline: UserListDetail,
  current: UserListDetail,
  authorityIds: number[],
): { addAuthIds: number[]; removeAuthIds: number[] } {
  const addAuthIds: number[] = [];
  const removeAuthIds: number[] = [];
  for (const authorityId of authorityIds) {
    const wasChecked = hasUserAuthority(baseline, authorityId);
    const isChecked = hasUserAuthority(current, authorityId);
    if (wasChecked === isChecked) continue;
    if (isChecked) addAuthIds.push(authorityId);
    else removeAuthIds.push(authorityId);
  }
  return { addAuthIds, removeAuthIds };
}

type SearchQuery = Partial<Omit<UserResult, "id">>;
type SearchParamNames = keyof SearchQuery;
type SearchBoxQuery = Record<string, string>;

const headerCellSx = {
  border: "1px solid #9e9e9e",
  fontWeight: 700,
  backgroundColor: "#f3f4f6",
  whiteSpace: "normal",
  wordBreak: "normal",
  overflowWrap: "normal",
  lineHeight: 1.2,
};

const bodyCellSx = {
  border: "1px solid #c6c6c6",
  whiteSpace: "nowrap",
};

const changedRowBg = "#fff8e1";
const changedCellOutline = "#f9a825";

const checkboxSx = {
  p: 0.5,
  "&:hover": { backgroundColor: "transparent" },
  "&.Mui-focusVisible": { backgroundColor: "transparent" },
};

function buildChangeHighlights(
  allUsers: UserListDetail[],
  savedUsers: UserListDetail[],
  authorityIds: number[],
) {
  const changedUserIds = new Set<number>();
  const changedAuthorityKeys = new Set<string>();

  if (authorityIds.length === 0) {
    return { changedUserIds, changedAuthorityKeys };
  }

  for (const user of allUsers) {
    const baseline = savedUsers.find(item => item.id === user.id);
    if (!baseline) continue;

    for (const authorityId of authorityIds) {
      if (hasUserAuthority(baseline, authorityId) === hasUserAuthority(user, authorityId)) {
        continue;
      }
      changedUserIds.add(user.id);
      changedAuthorityKeys.add(`${user.id}-${authorityId}`);
    }
  }

  return { changedUserIds, changedAuthorityKeys };
}

/** Memoized so toggling one checkbox does not re-render every cell on the page. */
const AuthorityCheckboxCell = memo(function AuthorityCheckboxCell({
  checked,
  changed,
  rowChanged,
  disabled,
  userId,
  authorityId,
  onToggle,
}: {
  checked: boolean;
  changed: boolean;
  rowChanged: boolean;
  disabled: boolean;
  userId: number;
  authorityId: number;
  onToggle: (userId: number, authorityId: number, checked: boolean) => void;
}) {
  return (
    <TableCell
      sx={{
        ...bodyCellSx,
        ...(rowChanged && { backgroundColor: changedRowBg }),
        ...(changed && { boxShadow: `inset 0 0 0 2px ${changedCellOutline}` }),
      }}
      align="center"
    >
      <Checkbox
        size="small"
        checked={checked}
        disabled={disabled}
        disableRipple
        disableFocusRipple
        onChange={(e, next) => {
          onToggle(userId, authorityId, next);
          (e.target as HTMLInputElement).blur();
        }}
        sx={checkboxSx}
      />
    </TableCell>
  );
});

const UserExcelSheetView: React.FC<Props> = ({ users }) => {
  const { t } = useTranslation(["user", "common"]);
  const router = useRouter();
  const [savedUsers, setSavedUsers] = useState(() => cloneUserList(users));
  const [allUsers, setAllUsers] = useState(() => cloneUserList(users));
  const [searchQuery, setSearchQuery] = useState<SearchBoxQuery>({});
  const [isSaving, setIsSaving] = useState(false);
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(20);
  const saveInFlightRef = useRef(false);

  useEffect(() => {
    const next = cloneUserList(users);
    setSavedUsers(next);
    setAllUsers(next);
  }, [users]);

  const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
    () => [
      { label: "用戶/姓名", paramName: "username", type: "text" },
      { label: t("staffNo"), paramName: "staffNo", type: "text" },
    ],
    [t],
  );

  const authorityColumns = useMemo(() => {
    const authorityMap = new Map<number, string>();
    users.forEach(user => {
      (user.auths ?? []).forEach(auth => {
        const label = auth.name?.trim() || auth.description?.trim() || auth.authority?.trim();
        if (!label) return;
        if (!authorityMap.has(auth.id)) authorityMap.set(auth.id, label);
      });
    });
    return Array.from(authorityMap.entries())
      .map(([id, label]) => ({ id, label }))
      .sort((a, b) => a.id - b.id);
  }, [users]);

  const filteredUsers = useMemo(() => {
    let results = allUsers;
    if (searchQuery.username?.trim()) {
      const keyword = searchQuery.username.trim().toLowerCase();
      results = results.filter(
        user =>
          user.username?.toLowerCase().includes(keyword) ||
          user.name?.toLowerCase().includes(keyword),
      );
    }
    if (searchQuery.staffNo?.trim()) {
      const keyword = searchQuery.staffNo.trim();
      results = results.filter(user => user.staffNo?.toString().includes(keyword));
    }
    return results;
  }, [allUsers, searchQuery]);

  const pagedUsers = useMemo(() => {
    const start = page * rowsPerPage;
    return filteredUsers.slice(start, start + rowsPerPage);
  }, [filteredUsers, page, rowsPerPage]);

  const authorityIds = useMemo(
    () => authorityColumns.map(authority => authority.id),
    [authorityColumns],
  );

  const { changedUserIds, changedAuthorityKeys } = useMemo(
    () => buildChangeHighlights(allUsers, savedUsers, authorityIds),
    [allUsers, savedUsers, authorityIds],
  );

  const hasPendingChanges = changedUserIds.size > 0;

  const handleEdit = useCallback(
    (user: UserResult) => {
      router.push(`/settings/user/edit?id=${user.id}`);
    },
    [router],
  );

  const handleDelete = useCallback(
    (user: UserResult) => {
      deleteDialog(async () => {
        await deleteUser(user.id);
        setAllUsers(prev => prev.filter(item => item.id !== user.id));
        router.refresh();
        successDialog(t("Delete Success"), t);
      }, t);
    },
    [router, t],
  );

  const handleAuthorityToggle = useCallback(
    (userId: number, authorityId: number, checked: boolean) => {
      if (isSaving) return;
      setAllUsers(prev =>
        prev.map(item =>
          item.id !== userId
            ? item
            : {
                ...item,
                auths: (item.auths ?? []).map(auth =>
                  auth.id === authorityId ? { ...auth, v: checked ? 1 : 0 } : auth,
                ),
                authIds: checked
                  ? Array.from(new Set([...(item.authIds ?? []), authorityId]))
                  : (item.authIds ?? []).filter(id => id !== authorityId),
              },
        ),
      );
    },
    [isSaving],
  );

  const handleSave = useCallback(async () => {
    if (!hasPendingChanges || saveInFlightRef.current) return;
    saveInFlightRef.current = true;
    setIsSaving(true);
    try {
      const usersToUpdate = allUsers.filter(user => {
        const baseline = savedUsers.find(item => item.id === user.id);
        if (!baseline) return false;
        const { addAuthIds, removeAuthIds } = computeAuthorityChanges(
          baseline,
          user,
          authorityIds,
        );
        return addAuthIds.length > 0 || removeAuthIds.length > 0;
      });

      for (const user of usersToUpdate) {
        const baseline = savedUsers.find(item => item.id === user.id)!;
        const { addAuthIds, removeAuthIds } = computeAuthorityChanges(
          baseline,
          user,
          authorityIds,
        );
        await updateUser(user.id, {
          username: user.username,
          name: user.name,
          staffNo: user.staffNo?.toString(),
          locked: false,
          addAuthIds,
          removeAuthIds,
        });
      }

      const snapshot = cloneUserList(allUsers);
      setSavedUsers(snapshot);
      setAllUsers(snapshot);
      router.refresh();
      await successDialog(t("Update Success", { ns: "common" }), t);
    } catch (error) {
      console.error("Failed to save user authorities", error);
      setAllUsers(cloneUserList(savedUsers));
      alert(t("Save failed. Please try again.", { defaultValue: "儲存失敗，請再試一次。" }));
    } finally {
      setIsSaving(false);
      saveInFlightRef.current = false;
    }
  }, [allUsers, authorityIds, hasPendingChanges, router, savedUsers, t]);

  return (
    <>
      <SearchBox
        criteria={searchCriteria}
        onSearch={async query => {
          setSearchQuery(query as SearchBoxQuery);
          setPage(0);
        }}
      />

      {hasPendingChanges && (
        <Box sx={{ mt: 2, display: "flex", justifyContent: "flex-end" }}>
          <Button
            variant="contained"
            onClick={handleSave}
            disabled={isSaving}
          >
            {isSaving ? t("Saving...", { defaultValue: "儲存中..." }) : t("Save", { ns: "common" })}
          </Button>
        </Box>
      )}

      <Paper variant="outlined" sx={{ mt: 2, overflow: "hidden" }}>
        <TableContainer sx={{ maxHeight: "calc(100vh - 280px)" }}>
          <Table stickyHeader size="small">
            <TableHead>
              <TableRow>
                <TableCell sx={{ ...headerCellSx, minWidth: 260 }}>
                  編輯 & 員工資訊
                </TableCell>
                {authorityColumns.map(authority => (
                  <TableCell
                    key={authority.id}
                    sx={{ ...headerCellSx, minWidth: 90, maxWidth: 110 }}
                    align="center"
                  >
                    {authority.label.replace(/\s+/g, "\n")}
                  </TableCell>
                ))}
                <TableCell sx={{ ...headerCellSx, minWidth: 78 }} align="center">
                  {t("Delete")}
                </TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {pagedUsers.length > 0 ? (
                pagedUsers.map((user, index) => {
                  const rowChanged = changedUserIds.has(user.id);
                  return (
                  <TableRow
                    key={user.id}
                    hover={!rowChanged}
                    sx={rowChanged ? { backgroundColor: changedRowBg } : undefined}
                  >
                    <TableCell
                      sx={{
                        ...bodyCellSx,
                        minWidth: 260,
                        whiteSpace: "normal",
                        ...(rowChanged && { backgroundColor: changedRowBg }),
                      }}
                    >
                      <Box display="flex" flexDirection="column" gap={0.5}>
                        <Box display="flex" alignItems="center" gap={1}>
                          <IconButton size="small" color="primary" onClick={() => handleEdit(user)}>
                            <EditNote fontSize="small" />
                          </IconButton>
                          <Typography variant="body2" component="span">
                            #{page * rowsPerPage + index + 1}
                          </Typography>
                          <Typography variant="body2" component="span">
                            {user.staffNo}
                          </Typography>
                        </Box>
                        <Box display="flex" alignItems="center" gap={1}>
                          <Typography variant="body2" component="span" fontWeight={600}>
                            {user.username}
                          </Typography>
                          <Typography variant="body2" component="span" color="text.secondary">
                            {user.name}
                          </Typography>
                        </Box>
                      </Box>
                    </TableCell>
                    {authorityColumns.map(authority => (
                      <AuthorityCheckboxCell
                        key={authority.id}
                        userId={user.id}
                        authorityId={authority.id}
                        checked={hasUserAuthority(user, authority.id)}
                        changed={changedAuthorityKeys.has(`${user.id}-${authority.id}`)}
                        rowChanged={rowChanged}
                        disabled={isSaving}
                        onToggle={handleAuthorityToggle}
                      />
                    ))}
                    <TableCell
                      sx={{
                        ...bodyCellSx,
                        ...(rowChanged && { backgroundColor: changedRowBg }),
                      }}
                      align="center"
                    >
                      <IconButton size="small" color="error" onClick={() => handleDelete(user)}>
                        <DeleteIcon fontSize="small" />
                      </IconButton>
                    </TableCell>
                  </TableRow>
                  );
                })
              ) : (
                <TableRow>
                  <TableCell colSpan={2 + authorityColumns.length} sx={bodyCellSx}>
                    <Box py={2}>
                      <Typography variant="body2" color="text.secondary">
                        No data
                      </Typography>
                    </Box>
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
        </TableContainer>
        <TablePagination
          component="div"
          count={filteredUsers.length}
          page={page}
          onPageChange={(_e, nextPage) => setPage(nextPage)}
          rowsPerPage={rowsPerPage}
          onRowsPerPageChange={e => {
            setRowsPerPage(parseInt(e.target.value, 10));
            setPage(0);
          }}
          rowsPerPageOptions={[10, 20, 50]}
          labelRowsPerPage="每頁"
        />
      </Paper>
    </>
  );
};

export default UserExcelSheetView;

