import { zodResolver } from "@hookform/resolvers/zod";
import { Button, Field, Form, useNavigate } from "@tailor-platform/app-shell";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { useQuery, useMutation } from "urql";
import { z } from "zod";
import { ErrorFallback } from "@/components/composed/error-fallback";
import { Loading } from "@/components/composed/loading";
import { graphql, type FragmentOf, readFragment } from "@/graphql";
import { useToast } from "@/hooks/use-toast";

export const EditRoleFormFragment = graphql(`
  fragment EditRoleForm on Role {
    id
    name
    description
    permissions
  }
`);

const AvailablePermissionsQuery = graphql(`
  query AvailablePermissionsForEditRole {
    availablePermissions {
      scopes {
        scope
        autoAssigned
      }
    }
  }
`);

const UpdateRoleMutation = graphql(`
  mutation UpdateRole($roleId: ID!, $name: String, $description: String, $permissions: [String!]!) {
    updateRole(roleId: $roleId, name: $name, description: $description, permissions: $permissions) {
      roleId
    }
  }
`);

const formSchema = z.object({
  name: z.string().min(1, "Name is required"),
  description: z.string().optional(),
});

type FormValues = z.infer<typeof formSchema>;

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

export const EditRoleForm = ({ role: roleFragment }: EditRoleFormProps) => {
  const [{ data: permissionsData, error: permError, fetching }] = useQuery({
    query: AvailablePermissionsQuery,
  });

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

  return <EditRoleFormInner role={roleFragment} permissionsData={permissionsData} />;
};

interface EditRoleFormInnerProps {
  role: FragmentOf<typeof EditRoleFormFragment>;
  permissionsData: { availablePermissions: { scopes: { scope: string; autoAssigned: boolean }[] } };
}

const EditRoleFormInner = ({ role: roleFragment, permissionsData }: EditRoleFormInnerProps) => {
  const role = readFragment(EditRoleFormFragment, roleFragment);
  const navigate = useNavigate();
  const toast = useToast();
  const [submitting, setSubmitting] = useState(false);
  const [, updateRole] = useMutation(UpdateRoleMutation);

  const allScopes = permissionsData.availablePermissions.scopes;
  const selectableScopes = allScopes.filter((s) => !s.autoAssigned);

  const rolePermissions = new Set(role.permissions ?? []);
  const [selectedScopes, setSelectedScopes] = useState<Set<string>>(
    () => new Set(selectableScopes.filter((s) => rolePermissions.has(s.scope)).map((s) => s.scope)),
  );

  const form = useForm<FormValues>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      name: role.name,
      description: role.description ?? "",
    },
  });

  const toggleScope = (scope: string) => {
    setSelectedScopes((prev) => {
      const next = new Set(prev);
      if (next.has(scope)) next.delete(scope);
      else next.add(scope);
      return next;
    });
  };

  const onSubmit = async (values: FormValues) => {
    const permissions = [...selectedScopes];

    if (permissions.length === 0) {
      toast.error("At least one permission must be selected");
      return;
    }

    setSubmitting(true);
    const result = await updateRole(
      {
        roleId: role.id,
        name: values.name,
        description: values.description?.trim() ? values.description : null,
        permissions,
      },
      { additionalTypenames: ["Role"] },
    );
    setSubmitting(false);

    if (result.error) {
      toast.error(result.error);
      return;
    }

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

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

  return (
    <Form
      noValidate
      onSubmit={(e) => void form.handleSubmit(onSubmit)(e)}
      className="max-w-md space-y-4"
    >
      <Controller
        control={form.control}
        name="name"
        render={({ field, fieldState }) => (
          <Field.Root name={field.name} invalid={fieldState.invalid}>
            <Field.Label>Name</Field.Label>
            <Field.Control placeholder="Enter role name" {...field} />
            {fieldState.error && <Field.Error match>{fieldState.error.message}</Field.Error>}
          </Field.Root>
        )}
      />
      <Controller
        control={form.control}
        name="description"
        render={({ field, fieldState }) => (
          <Field.Root name={field.name} invalid={fieldState.invalid}>
            <Field.Label>Description</Field.Label>
            <Field.Control placeholder="Enter description" {...field} />
            {fieldState.error && <Field.Error match>{fieldState.error.message}</Field.Error>}
          </Field.Root>
        )}
      />
      <div className="space-y-2">
        <span className="text-sm font-medium leading-none">Permissions</span>
        <div className="rounded-md border p-3">
          {selectableScopes.length === 0 ? (
            <p className="text-sm text-muted-foreground">No permissions available.</p>
          ) : (
            <div className="space-y-3">
              {selectableScopes.map((scope) => (
                <label key={scope.scope} className="flex items-start gap-2 cursor-pointer">
                  <input
                    type="checkbox"
                    checked={selectedScopes.has(scope.scope)}
                    onChange={() => toggleScope(scope.scope)}
                    className="mt-1"
                  />
                  <div>
                    <div className="text-sm font-medium">{scope.scope}</div>
                  </div>
                </label>
              ))}
            </div>
          )}
        </div>
      </div>
      <div className="flex gap-2">
        <Button type="submit" disabled={submitting}>
          {submitting ? "Saving..." : "Save"}
        </Button>
        <Button type="button" variant="outline" onClick={onCancel}>
          Cancel
        </Button>
      </div>
    </Form>
  );
};
