"use client";

import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";

import Alert from "@mui/material/Alert";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
import CircularProgress from "@mui/material/CircularProgress";
import Divider from "@mui/material/Divider";
import Paper from "@mui/material/Paper";
import Snackbar from "@mui/material/Snackbar";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";

import {
  AppButton,
  AppCancelButton,
  AppSecondaryButton,
} from "@/components/ui/button";
import { AppCheckbox } from "@/components/ui/checkbox";
import { AppFormField, AppFormTextField } from "@/components/ui/form";
import {
  createRole,
  getPermissionMatrix,
  getRoleDetail,
  updateRole,
} from "@/features/role/service/role-service";
import type {
  PermissionActionKey,
  PermissionMatrixResource,
} from "@/features/role/role-data";

type Mode = "create" | "edit";

type RoleFormScreenProps = {
  mode: Mode;
  roleId?: number;
};

const FULL_ACCESS_PERMISSION_NAME = "manage all";

// Matrix columns, in the mockup's order. `key` is the CASL action stored in the
// permission name ("<action> <subject>").
const ACTION_COLUMNS = [
  { key: "read", label: "View" },
  { key: "delete", label: "Delete" },
  { key: "create", label: "Create" },
  { key: "update", label: "Update" },
  { key: "export", label: "Export" },
] as const;

type ResourceRow = {
  subject: string;
  label: string;
  order: number;
  actions: Partial<Record<PermissionActionKey, number>>; // action key -> permission id
};

function buildMatrix(resources: PermissionMatrixResource[]) {
  const matrixIds = new Set<number>();

  const rows = resources.map((resource) => {
    const actions: ResourceRow["actions"] = {};

    for (const column of ACTION_COLUMNS) {
      const permission = resource.permissions[column.key];
      if (permission) {
        actions[column.key] = permission.id;
        matrixIds.add(permission.id);
      }
    }

    return {
      subject: resource.resource,
      label: resource.label,
      order: resource.order,
      actions,
    };
  });

  return {
    rows: rows.sort(
      (a, b) => a.order - b.order || a.label.localeCompare(b.label),
    ),
    matrixIds,
  };
}

function isFullAccessPermission(permissionName: string) {
  return permissionName.trim().toLowerCase() === FULL_ACCESS_PERMISSION_NAME;
}

export function RoleFormScreen({ mode, roleId }: RoleFormScreenProps) {
  const router = useRouter();

  const [name, setName] = useState("");
  const [rows, setRows] = useState<ResourceRow[]>([]);
  const [matrixIds, setMatrixIds] = useState<Set<number>>(new Set());
  const [selected, setSelected] = useState<Set<number>>(new Set());
  // Permissions the matrix can't represent (e.g. "manage all") — kept on save.
  const preservedPermissionIdsRef = useRef<number[]>([]);
  const fullAccessPermissionIdsRef = useRef<number[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [formError, setFormError] = useState("");
  const [notice, setNotice] = useState<string | null>(null);

  useEffect(() => {
    let ignore = false;

    async function load() {
      setLoading(true);
      setFormError("");

      try {
        if (ignore) return;

        const permissionMatrix = await getPermissionMatrix();
        const matrix = buildMatrix(permissionMatrix.resources);

        if (!ignore) {
          setRows(matrix.rows);
          setMatrixIds(matrix.matrixIds);
          setSelected(new Set());
          preservedPermissionIdsRef.current = [];
          fullAccessPermissionIdsRef.current = [];
        }

        if (
          !ignore &&
          mode === "edit" &&
          roleId !== undefined &&
          Number.isFinite(roleId)
        ) {
          const detail = await getRoleDetail(roleId);

          if (!ignore) {
            setName(detail.name);

            const hasFullAccess = detail.permissions.some((permission) =>
              isFullAccessPermission(permission.name),
            );
            const nextSelected = hasFullAccess
              ? new Set(matrix.matrixIds)
              : new Set<number>();
            const nextPreserved: number[] = [];
            const nextFullAccessPermissionIds: number[] = [];

            for (const permission of detail.permissions) {
              if (isFullAccessPermission(permission.name)) {
                nextFullAccessPermissionIds.push(permission.id);
              }

              if (matrix.matrixIds.has(permission.id)) {
                if (!hasFullAccess) {
                  nextSelected.add(permission.id);
                }
              } else {
                nextPreserved.push(permission.id);
              }
            }

            setSelected(nextSelected);
            preservedPermissionIdsRef.current = nextPreserved;
            fullAccessPermissionIdsRef.current = nextFullAccessPermissionIds;
          }
        }
      } catch (requestError) {
        if (!ignore) {
          setFormError(
            requestError instanceof Error
              ? requestError.message
              : "Could not load the form.",
          );
        }
      } finally {
        if (!ignore) setLoading(false);
      }
    }

    void load();

    return () => {
      ignore = true;
    };
  }, [mode, roleId]);

  const totalCount = matrixIds.size;
  const activeCount = selected.size;
  const allSelected = totalCount > 0 && activeCount === totalCount;

  function togglePermission(id: number) {
    setSelected((previous) => {
      const next = new Set(previous);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  function rowPermissionIds(row: ResourceRow) {
    return Object.values(row.actions).filter(
      (value): value is number => typeof value === "number",
    );
  }

  function toggleRow(row: ResourceRow) {
    const ids = rowPermissionIds(row);
    const allOn = ids.every((id) => selected.has(id));

    setSelected((previous) => {
      const next = new Set(previous);
      for (const id of ids) {
        if (allOn) next.delete(id);
        else next.add(id);
      }
      return next;
    });
  }

  function toggleSelectAll() {
    setSelected(() => (allSelected ? new Set() : new Set(matrixIds)));
  }

  async function handleSave() {
    const trimmed = name.trim();

    if (!trimmed) {
      setFormError("Role name is required.");
      return;
    }

    setFormError("");
    setSaving(true);

    const fullAccessPermissionIds = fullAccessPermissionIdsRef.current;
    const preservedPermissionIds = preservedPermissionIdsRef.current;
    const fullAccessStillSelected =
      fullAccessPermissionIds.length > 0 && selected.size === matrixIds.size;
    const preservedWithoutFullAccess = preservedPermissionIds.filter(
      (id) => !fullAccessPermissionIds.includes(id),
    );
    const permissionIds = fullAccessStillSelected
      ? [...fullAccessPermissionIds, ...preservedWithoutFullAccess]
      : [...selected, ...preservedWithoutFullAccess];

    try {
      if (mode === "edit" && roleId !== undefined) {
        await updateRole(roleId, { name: trimmed, permissionIds });
      } else {
        await createRole({ name: trimmed, permissionIds });
      }

      router.push("/admin/role");
    } catch (requestError) {
      setSaving(false);
      setNotice(
        requestError instanceof Error
          ? requestError.message
          : "Could not save the role.",
      );
    }
  }

  const isEdit = mode === "edit";

  return (
    <Box
      sx={{
        width: "100%",
        px: { xs: 2, sm: 3 },
        py: 3,
        maxWidth: 1280,
        mx: "auto",
      }}
    >
      <Typography sx={{ fontSize: 28, fontWeight: 800 }}>
        {isEdit ? "Edit Role" : "Create Role"}
      </Typography>
      <Typography sx={{ color: "text.secondary", mb: 2.5 }}>
        Define permissions and assign access for {isEdit ? "this" : "a new"}{" "}
        role.
      </Typography>

      <Divider sx={{ mb: 3 }} />

      <Paper variant="outlined" sx={{ p: { xs: 2, md: 3 }, borderRadius: 3 }}>
        {loading ? (
          <Box sx={{ display: "flex", justifyContent: "center", py: 8 }}>
            <CircularProgress />
          </Box>
        ) : (
          <>
            <Box sx={{ maxWidth: 560, mb: 3 }}>
              <AppFormField label="Role Name" required>
                <AppFormTextField
                  value={name}
                  onChange={(event) => setName(event.target.value)}
                  placeholder="Enter role name"
                  disabled={saving}
                  autoFocus
                />
              </AppFormField>
            </Box>

            <Box
              sx={{
                display: "flex",
                alignItems: "center",
                gap: 1.5,
                flexWrap: "wrap",
                mb: 1.5,
              }}
            >
              <Box sx={{ mr: "auto" }}>
                <Typography sx={{ fontSize: 16, fontWeight: 700 }}>
                  Resources
                </Typography>
                <Typography sx={{ fontSize: 13, color: "text.secondary" }}>
                  Toggle every permission for this role or adjust per row.
                </Typography>
              </Box>

              <Chip label={rows.length} size="small" />
              <Typography sx={{ fontSize: 13, color: "text.secondary" }}>
                {activeCount} of {totalCount} permissions active
              </Typography>
              <AppSecondaryButton
                onClick={toggleSelectAll}
                size="small"
                disabled={saving || totalCount === 0}
                sx={{
                  minWidth: 92,
                  minHeight: 34,
                  height: 34,
                  fontSize: 12,
                }}
              >
                {allSelected ? "Clear all" : "Select all"}
              </AppSecondaryButton>
            </Box>

            <TableContainer
              component={Paper}
              variant="outlined"
              sx={{ borderRadius: 2 }}
            >
              <Table size="small">
                <TableHead>
                  <TableRow>
                    <TableCell sx={{ fontWeight: 700 }}>Resource</TableCell>
                    {ACTION_COLUMNS.map((column) => (
                      <TableCell
                        key={column.key}
                        align="center"
                        sx={{ fontWeight: 700, width: 110 }}
                      >
                        {column.label}
                      </TableCell>
                    ))}
                  </TableRow>
                </TableHead>
                <TableBody>
                  {rows.length === 0 ? (
                    <TableRow>
                      <TableCell colSpan={ACTION_COLUMNS.length + 1}>
                        <Typography sx={{ py: 2, color: "text.secondary" }}>
                          No permissions available to assign.
                        </Typography>
                      </TableCell>
                    </TableRow>
                  ) : (
                    rows.map((row) => {
                      const ids = rowPermissionIds(row);
                      const someOn = ids.some((id) => selected.has(id));
                      const allOn =
                        ids.length > 0 && ids.every((id) => selected.has(id));

                      return (
                        <TableRow key={row.subject} hover>
                          <TableCell>
                            <Box
                              sx={{
                                display: "flex",
                                alignItems: "center",
                                gap: 1,
                              }}
                            >
                              <AppCheckbox
                                checked={allOn}
                                indeterminate={someOn && !allOn}
                                onChange={() => toggleRow(row)}
                                disabled={saving}
                              />
                              <Typography sx={{ fontWeight: 600 }}>
                                {row.label}
                              </Typography>
                            </Box>
                          </TableCell>

                          {ACTION_COLUMNS.map((column) => {
                            const permissionId = row.actions[column.key];

                            return (
                              <TableCell key={column.key} align="center">
                                {permissionId === undefined ? (
                                  <Typography sx={{ color: "text.disabled" }}>
                                    —
                                  </Typography>
                                ) : (
                                  <AppCheckbox
                                    checked={selected.has(permissionId)}
                                    onChange={() =>
                                      togglePermission(permissionId)
                                    }
                                    disabled={saving}
                                  />
                                )}
                              </TableCell>
                            );
                          })}
                        </TableRow>
                      );
                    })
                  )}
                </TableBody>
              </Table>
            </TableContainer>

            {formError ? (
              <Alert severity="error" sx={{ mt: 2, fontWeight: 600 }}>
                {formError}
              </Alert>
            ) : null}

            <Box
              sx={{
                display: "flex",
                justifyContent: "flex-end",
                gap: 1.5,
                mt: 3,
              }}
            >
              <AppCancelButton
                onClick={() => router.push("/admin/role")}
                disabled={saving}
              />
              <AppButton onClick={handleSave} disabled={saving}>
                {saving ? "Saving..." : isEdit ? "Save Changes" : "Create role"}
              </AppButton>
            </Box>
          </>
        )}
      </Paper>

      <Snackbar
        open={Boolean(notice)}
        autoHideDuration={4000}
        onClose={() => setNotice(null)}
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
      >
        {notice ? (
          <Alert
            severity="error"
            variant="filled"
            onClose={() => setNotice(null)}
            sx={{ fontWeight: 700 }}
          >
            {notice}
          </Alert>
        ) : undefined}
      </Snackbar>
    </Box>
  );
}
