import { useState } from "react";
import { Button, useNavigate } from "@tailor-platform/app-shell";
import { useQuery, useMutation } from "urql";
import { graphql, type FragmentOf, readFragment } from "@/graphql";
import { useToast } from "@/hooks/use-toast";
import { Loading } from "@/components/composed/loading";
import { ErrorFallback } from "@/components/composed/error-fallback";
import { groupPermissions, isAutoAssigned, type PermissionGroup } from "@/lib/permission-groups";

export const EditRoleFormFragment = graphql(`
  fragment EditRoleForm on Role {
    id
    name
    rolePermissions {
      edges {
        node {
          id
          permission {
            id
            key
            description
          }
        }
      }
    }
  }
`);

const AllPermissionsQuery = graphql(`
  query AllPermissions {
    permissions {
      edges {
        node {
          id
          key
          description
        }
      }
    }
  }
`);

const AssignPermissionMutation = graphql(`
  mutation AssignPermissionToRole($roleId: String!, $permissionId: String!) {
    assignPermissionToRole(roleId: $roleId, permissionId: $permissionId) {
      roleId
      permissionId
    }
  }
`);

const RevokePermissionMutation = graphql(`
  mutation RevokePermissionFromRole($roleId: String!, $permissionId: String!) {
    revokePermissionFromRole(roleId: $roleId, permissionId: $permissionId) {
      roleId
      permissionId
    }
  }
`);

interface EditRoleFormProps {
  role: FragmentOf<typeof EditRoleFormFragment>;
}

export const EditRoleForm = ({ role: roleFragment }: EditRoleFormProps) => {
  const role = readFragment(EditRoleFormFragment, roleFragment);
  const navigate = useNavigate();
  const toast = useToast();
  const [submitting, setSubmitting] = useState(false);

  const assignedPermissionIds = new Set(
    role.rolePermissions.edges
      .filter((edge) => edge.node.permission != null)
      .map((edge) => edge.node.permission!.id),
  );

  const [selectedPermissionIds, setSelectedPermissionIds] = useState<Set<string>>(
    () => new Set(assignedPermissionIds),
  );

  const [{ data: permissionsData, error: permissionsError, fetching: permissionsFetching }] =
    useQuery({ query: AllPermissionsQuery });

  const [, assignPermission] = useMutation(AssignPermissionMutation);
  const [, revokePermission] = useMutation(RevokePermissionMutation);

  if (permissionsFetching) return <Loading />;

  if (permissionsError || !permissionsData) {
    return (
      <ErrorFallback
        title="Failed to load permissions"
        message="An error occurred while fetching the permissions list."
      />
    );
  }

  const allPermissions = permissionsData.permissions.edges.map((edge) => edge.node);
  const permissionGroups = groupPermissions(allPermissions);

  const isGroupSelected = (group: PermissionGroup) =>
    group.permissions.every((p) => selectedPermissionIds.has(p.id));

  const toggleGroup = (group: PermissionGroup) => {
    setSelectedPermissionIds((prev) => {
      const next = new Set(prev);
      const allSelected = group.permissions.every((p) => next.has(p.id));
      for (const p of group.permissions) {
        if (allSelected) next.delete(p.id);
        else next.add(p.id);
      }
      return next;
    });
  };

  const onSubmit = async () => {
    setSubmitting(true);

    const autoAssignedIds = new Set(allPermissions.filter(isAutoAssigned).map((p) => p.id));
    const toAssign = [...selectedPermissionIds].filter((id) => !assignedPermissionIds.has(id));
    // Never revoke auto-assigned permissions
    const toRevoke = [...assignedPermissionIds].filter(
      (id) => !selectedPermissionIds.has(id) && !autoAssignedIds.has(id),
    );

    const operations = [
      ...toAssign.map((permissionId) =>
        assignPermission(
          { roleId: role.id, permissionId },
          { additionalTypenames: ["RolePermission", "Role"] },
        ),
      ),
      ...toRevoke.map((permissionId) =>
        revokePermission(
          { roleId: role.id, permissionId },
          { additionalTypenames: ["RolePermission", "Role"] },
        ),
      ),
    ];

    const results = await Promise.all(operations);
    const errorResult = results.find((r) => r.error);

    setSubmitting(false);

    if (errorResult?.error) {
      toast.error(errorResult.error);
      return;
    }

    toast.success("Role permissions updated successfully");
    void navigate("..");
  };

  const onCancel = () => {
    void navigate("..");
  };

  return (
    <div className="max-w-md space-y-4">
      <h2 className="text-lg font-semibold">Permissions for {role.name}</h2>
      {permissionGroups.length === 0 ? (
        <p className="text-sm text-muted-foreground">No permissions available.</p>
      ) : (
        <div className="space-y-3">
          {permissionGroups.map((group) => (
            <label key={group.label} className="flex items-start gap-2 cursor-pointer">
              <input
                type="checkbox"
                checked={isGroupSelected(group)}
                onChange={() => toggleGroup(group)}
                className="mt-1"
              />
              <div>
                <div className="text-sm font-medium">{group.label}</div>
                <div className="text-xs text-muted-foreground">{group.description}</div>
              </div>
            </label>
          ))}
        </div>
      )}
      <div className="flex gap-2">
        <Button onClick={() => void onSubmit()} disabled={submitting}>
          {submitting ? "Saving..." : "Save"}
        </Button>
        <Button type="button" variant="outline" onClick={onCancel}>
          Cancel
        </Button>
      </div>
    </div>
  );
};
