"use client";

import { useMemo, useState } from "react";
import {
  keepPreviousData,
  useQuery,
  useQueryClient,
} from "@tanstack/react-query";
import {
  Alert,
  Avatar,
  Box,
  Button,
  Chip,
  CircularProgress,
  IconButton,
  Stack,
  Typography,
} from "@mui/material";
import { alpha } from "@mui/material/styles";
import type { GridSortModel } from "@mui/x-data-grid";
import { useAppLanguage } from "@/components/providers/app-language-provider";
import {
  DataTable,
  DataTableNumberCell,
  type DataTableColDef,
} from "@/components/ui/data-table";
import { ExportButton } from "@/components/ui/export-button";
import { FilterPanelGrouped } from "@/components/ui/filter-panel";
import { EyeViewIcon } from "@/components/ui/icon";
import { ToastNotification } from "@/components/ui/toast-notification";
import { getDocumentUrl } from "@/lib/document-file";
import { getAvatarText } from "@/features/admin/user-management/components/table/user-table-cells";
import { useCurrentUser } from "@/features/auth/hook/use-current-user";
import { usePermissions } from "@/features/auth/hook/use-permissions";
import { getRoleLabel, isAdminRole } from "@/features/auth/auth-data";
import {
  ActivityActionBadge,
  ActivityLogDetailsDrawer,
  describeActivity,
  formatActivityTime,
} from "./activity-log-details-drawer";
import {
  exportActivityLogs,
  getActivityFilterOptions,
  getActivityLogs,
} from "./activity-log-service";
import {
  actionLabels,
  activityLogText,
  moduleLabels,
  outcomeLabels,
} from "./activity-log-i18n";
import type { ActivityFilters, ActivityLog } from "./activity-log-types";

// Nothing picked in a group means "all", the same rule every filter panel uses.
const emptyFilters: ActivityFilters = {
  module: [],
  action: [],
  actorId: [],
  role: [],
  outcome: [],
  reference: "",
};

// Fixed list for the outcome filter.
const OUTCOME_OPTIONS = ["SUCCESS", "DENIED", "FAILED"];

// The shared table pages with numbered buttons, so the page size is fixed.
const ACTIVITY_PAGE_SIZE = 25;
// Room for the avatar in the user cell; every cell is a single line.
const ACTIVITY_ROW_HEIGHT = 64;

// The Filter panel groups; the record-history filter is set from the
// details panel.
type GroupFilterKey =
  | "module"
  | "action"
  | "actorId"
  | "role"
  | "outcome";

export default function ActivityLogPage() {
  const { language } = useAppLanguage();
  const text = activityLogText[language === "en" ? "en" : "kh"];
  const labelIndex = language === "en" ? 0 : 1;
  const {
    user,
    isLoading: userLoading,
    error: userError,
    refetch: refetchUser,
  } = useCurrentUser();
  const access = usePermissions();
  const queryClient = useQueryClient();
  const isAdmin = isAdminRole(access.role);

  function retryAccess() {
    void refetchUser();
    void queryClient.invalidateQueries({ queryKey: ["auth", "session"] });
  }

  const [filters, setFilters] = useState<ActivityFilters>(emptyFilters);
  const [page, setPage] = useState(0);
  // Sorting runs on the server, so it covers every log, not only this page.
  const [sortModel, setSortModel] = useState<GridSortModel>([]);
  const sort = sortModel[0]
    ? { field: sortModel[0].field, order: sortModel[0].sort ?? "asc" }
    : undefined;
  // The row whose details panel is open.
  const [selectedRow, setSelectedRow] = useState<ActivityLog | null>(null);
  const [exporting, setExporting] = useState(false);
  const [toast, setToast] = useState<{
    message: string;
    variant: "success" | "error";
  } | null>(null);

  const logs = useQuery({
    queryKey: ["activity-logs", user?.id, filters, page, sort],
    placeholderData: keepPreviousData,
    queryFn: ({ signal }) =>
      getActivityLogs(filters, page + 1, ACTIVITY_PAGE_SIZE, sort, signal),
    enabled: Boolean(isAdmin),
    retry: false,
  });
  const options = useQuery({
    queryKey: ["activity-log-options", user?.id],
    queryFn: ({ signal }) => getActivityFilterOptions(signal),
    enabled: Boolean(isAdmin),
    retry: false,
  });

  // How many rows the filters match, which is also the number the first row
  // counts down from.
  const totalRows = logs.data?.meta.total ?? 0;

  // Columns for the shared MUI table, rebuilt only when the language, page or
  // row count changes. Filtering happens in the Filter panel, so the column
  // menu only sorts.
  const columns = useMemo<DataTableColDef<ActivityLog>[]>(
    () => [
      {
        // Show the newest activity with the highest number (for example,
        // 20, 19, 18). The count continues across pages.
        field: "no",
        headerName: text.no,
        // Wide enough for the "No." / "ល.រ" header next to the cell padding.
        width: 64,
        align: "center",
        headerAlign: "center",
        sortable: false,
        filterable: false,
        disableColumnMenu: true,
        renderCell: (params) => (
          <DataTableNumberCell
            value={
              totalRows -
              (page * ACTIVITY_PAGE_SIZE +
                params.api.getRowIndexRelativeToVisibleRows(params.id))
            }
          />
        ),
      },
      {
        field: "actorName",
        headerName: text.user,
        // Room for longer names such as "CDC G-PSF Secretariat".
        minWidth: 240,
        flex: 1.6,
        filterable: false,
        // Profile photo, then the name. Without a photo the
        // avatar shows initials, like the User Management table.
        renderCell: ({ row }) => (
          <Box sx={{ display: "flex", alignItems: "center", gap: 1.25 }}>
            <Avatar
              src={row.actorAvatar ? getDocumentUrl(row.actorAvatar) : undefined}
              alt={row.actorName}
              sx={(theme) => ({
                width: 32,
                height: 32,
                flexShrink: 0,
                fontSize: 12,
                fontWeight: 700,
                color: theme.palette.primary.main,
                bgcolor:
                  theme.palette.mode === "dark"
                    ? alpha(theme.palette.primary.main, 0.14)
                    : "#E8F4FF",
                border: `1px solid ${alpha(theme.palette.divider, 0.8)}`,
              })}
            >
              {getAvatarText(row.actorName)}
            </Avatar>
            <Box sx={{ minWidth: 0 }}>{row.actorName}</Box>
          </Box>
        ),
      },
      {
        field: "actorRoles",
        headerName: text.roles,
        minWidth: 160,
        flex: 1,
        // A list of roles: the server cannot sort by it.
        sortable: false,
        filterable: false,
        valueGetter: (_value, row) =>
          row.actorRoles.map(getRoleLabel).join(", ") || "—",
      },
      {
        field: "module",
        headerName: text.module,
        // Fits longer names such as "Working group issues".
        minWidth: 190,
        flex: 1.2,
        filterable: false,
        valueFormatter: (value: string) =>
          moduleLabels[value]?.[labelIndex] ?? value,
      },
      {
        field: "action",
        headerName: text.action,
        minWidth: 130,
        flex: 0.8,
        filterable: false,
        renderCell: ({ row }) => (
          <ActivityActionBadge
            action={row.action}
            labelIndex={labelIndex}
            outcome={row.outcome}
          />
        ),
      },
      {
        field: "description",
        headerName: text.description,
        minWidth: 220,
        flex: 1.4,
        sortable: false,
        filterable: false,
        // "Updated issue status" on one line; the details panel has the rest.
        valueGetter: (_value, row) => describeActivity(row, labelIndex, text),
      },
      {
        field: "createdAt",
        headerName: text.date,
        // Room for the full Khmer date and time on one line.
        minWidth: 230,
        flex: 1.3,
        filterable: false,
        // Shared date formatter: Khmer months and digits in Khmer.
        valueFormatter: (value: string) => formatActivityTime(value, language),
      },
      {
        // The row's own buttons (open the details panel).
        field: "details",
        headerName: text.rowActions,
        width: 90,
        headerAlign: "center",
        sortable: false,
        filterable: false,
        disableColumnMenu: true,
        align: "center",
        renderCell: ({ row }) => (
          <IconButton
            size="small"
            aria-label={text.viewDetails}
            title={text.viewDetails}
            onClick={() => setSelectedRow(row)}
          >
            <EyeViewIcon fontSize="small" />
          </IconButton>
        ),
      },
    ],
    [labelIndex, language, page, text, totalRows],
  );

  // Any filter change starts again from the first page.
  function changeFilter<Key extends keyof ActivityFilters>(
    key: Key,
    value: ActivityFilters[Key],
  ) {
    setFilters((current) => ({ ...current, [key]: value }));
    setPage(0);
  }

  // "View full history": every activity on one record.
  function showHistory(reference: string) {
    setSelectedRow(null);
    changeFilter("reference", reference);
  }

  async function exportLog() {
    setExporting(true);
    try {
      await exportActivityLogs(filters, sort);
      setToast({ message: text.exported, variant: "success" });
    } catch {
      setToast({ message: text.exportFailed, variant: "error" });
    } finally {
      setExporting(false);
    }
  }

  // Used by the Retry button when loading fails.
  function refresh() {
    void logs.refetch();
    void options.refetch();
  }

  if (userLoading || access.isLoading)
    return <CircularProgress aria-label={text.loading} />;
  if (userError || access.error)
    return (
      <Alert
        severity="error"
        action={<Button onClick={retryAccess}>{text.retry}</Button>}
      >
        {text.error}
      </Alert>
    );
  if (!isAdmin) return <Alert severity="warning">{text.denied}</Alert>;

  return (
    <Stack spacing={3} sx={{ p: { xs: 2, md: 3 } }}>
      <Box>
        <Typography variant="h5" component="h1" sx={{ fontWeight: 600 }}>
          {text.title}
        </Typography>
      </Box>

      <Box
        sx={{
          display: "flex",
          alignItems: "flex-start",
          gap: 2,
          flexWrap: "wrap",
        }}
      >
        <FilterPanelGrouped
          fields={[
            {
              key: "module",
              label: text.module,
              options: (options.data?.modules ?? []).map((value) => ({
                label: moduleLabels[value]?.[labelIndex] ?? value,
                value,
              })),
              value: filters.module,
            },
            {
              key: "action",
              label: text.action,
              options: (options.data?.actions ?? []).map((value) => ({
                label: actionLabels[value]?.[labelIndex] ?? value,
                value,
              })),
              value: filters.action,
            },
            {
              key: "outcome",
              label: text.outcome,
              options: OUTCOME_OPTIONS.map((value) => ({
                label: outcomeLabels[value]?.[labelIndex] ?? value,
                value,
              })),
              value: filters.outcome,
            },
            {
              key: "actorId",
              label: text.user,
              options: (options.data?.actors ?? []).map((actor) => ({
                label: `${actor.actorName} (#${actor.actorId})`,
                value: String(actor.actorId),
              })),
              value: filters.actorId,
              optionsPerRow: 2,
            },
            {
              key: "role",
              label: text.role,
              options: (options.data?.roles ?? []).map((value) => ({
                label: getRoleLabel(value),
                value,
              })),
              value: filters.role,
            },
          ]}
          onChange={(key, values) =>
            changeFilter(key as GroupFilterKey, values)
          }
          onClearAll={() => {
            setFilters((current) => ({
              ...current,
              module: [],
              action: [],
              actorId: [],
              role: [],
              outcome: [],
            }));
            setPage(0);
          }}
        />

        <Box sx={{ ml: { sm: "auto" } }}>
          <ExportButton
            loading={exporting}
            loadingLabel={text.exporting}
            onClick={() => void exportLog()}
          >
            {text.export}
          </ExportButton>
        </Box>
      </Box>

      {filters.reference ? (
        <Box>
          <Chip
            label={`${text.historyOf} ${filters.reference}`}
            onDelete={() => changeFilter("reference", "")}
          />
        </Box>
      ) : null}

      {(logs.isError || options.isError) && (
        <Alert
          severity="error"
          action={
            <Button color="inherit" onClick={refresh}>
              {text.retry}
            </Button>
          }
        >
          {text.error}
        </Alert>
      )}
      {!logs.isError && (
        <DataTable
          rows={logs.data?.data ?? []}
          columns={columns}
          getRowId={(row) => row.id}
          loading={logs.isFetching}
          emptyMessage={text.empty}
          rowHeight={ACTIVITY_ROW_HEIGHT}
          pageSize={ACTIVITY_PAGE_SIZE}
          paginationMode="server"
          rowCount={logs.data?.meta.total ?? 0}
          paginationModel={{ page, pageSize: ACTIVITY_PAGE_SIZE }}
          onPaginationModelChange={(model) => setPage(model.page)}
          enableColumnMenu
          sortingMode="server"
          sortModel={sortModel}
          onSortModelChange={(model) => {
            setSortModel(model);
            setPage(0);
          }}
        />
      )}

      <ActivityLogDetailsDrawer
        row={selectedRow}
        language={language}
        onClose={() => setSelectedRow(null)}
        onViewHistory={showHistory}
      />

      <ToastNotification
        open={Boolean(toast)}
        message={toast?.message ?? ""}
        variant={toast?.variant ?? "success"}
        onClose={() => setToast(null)}
      />
    </Stack>
  );
}
