import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { Input } from "@/shared/common/ui/input";
import { Button } from "@/shared/common/ui/button";
import {
  Form,
  FormField,
  FormItem,
  FormLabel,
  FormControl,
  FormMessage,
} from "@/shared/common/ui/form";
import { toast } from "sonner";
import {
  invalidatePositionTypeQueries,
  usePositionTypes,
} from "@/user-management/hooks/usePositionTypes";
import { positionTypePermissionService } from "@/user-management/services/api/positionTypePermissionService";
import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useAuth } from "@/shared/context/AuthContext";
import { useUnit } from "@/user-management/hooks/useUnit";
import { useEffect, useMemo, useRef, useState } from "react";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/shared/common/ui/select";
import { SingleSelect } from "@/shared/common/ui/single-select";
import { UnitDto } from "@/user-management/dto/unit/unitDto";
import { PositionTypeDto } from "@/user-management/dto/positions/positionType";
import { useLocalizedName } from "@/shared/common/localizedName";
import { useOrganizations } from "@/super-admin/hooks/useOrganizations";
import { OrganizationDto } from "@/shared/dto/organization/organizationDto";
import { useErrorHandler } from "@/shared/hooks/useErrorHandler";
import { PermissionSearch } from "./PermissionSearch";
import { useApplications } from "@/user-management/hooks/useApplications";
import { useQuery, useQueryClient } from "@tanstack/react-query";

export interface CreatePositionFormProps {
  mode?: "create" | "edit";
  positionTypeId?: string;
  initialValues?: {
    nameAm: string;
    nameEn: string;
    unitId: string;
    key?: string;
  };
  onSuccess?: () => void;
  onCancel?: () => void;
}

export const CreatePositionForm = ({
  mode = "create",
  positionTypeId,
  initialValues,
  onSuccess,
  onCancel,
}: CreatePositionFormProps = {}) => {
  const navigate = useNavigate();
  const { t } = useTranslation();
  const { handleError } = useErrorHandler(t);
  const {
    createPositionType,
    updatePositionType,
    positionTypes,
    isLoading: isLoadingPositionTypes,
    isError: isErrorPositionTypes,
  } = usePositionTypes();
  const { user } = useAuth();
  const { getList, getById } = useUnit();
  const localizedName = useLocalizedName();
  const userOrganizationId =
    user?.employee && user.employee.length > 0
      ? user.employee[0].organizationId
      : undefined;
  const [selectedApplicationId, setSelectedApplicationId] =
    useState<string>("");
  const [copyFromPositionId, setCopyFromPositionId] = useState<string>("");
  const [isCopying, setIsCopying] = useState(false);
  const [isLoadingEditData, setIsLoadingEditData] = useState(mode === "edit");
  const hasLoadedEditData = useRef(false);
  // Permissions the position type had when the form opened. Needed because the
  // API cannot represent "no permissions" (see onSubmit).
  const loadedPermissionCount = useRef(0);
  const { applications, isLoading: isLoadingApplications } = useApplications();
  const queryClient = useQueryClient();

  const formSchema = useMemo(
    () =>
      z.object({
        nameEn: z.string().trim().min(2, t("organization.englishNameRequired")),
        nameAm: z.string().trim().min(2, t("organization.amharicNameRequired")),
        organizationId: z.string().min(1, t("organization.organizationRequired")),
        unitId: z.string().min(1, t("contentManagement.unitRequired")),
        permissions: z.array(z.string()),
      }),
    [t],
  );

  type FormValues = z.infer<typeof formSchema>;

  const form = useForm<FormValues>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      nameAm: initialValues?.nameAm ?? "",
      nameEn: initialValues?.nameEn ?? "",
      organizationId: userOrganizationId ?? "",
      unitId: initialValues?.unitId ?? "",
      permissions: [],
    },
  });

  const selectedOrganizationId = form.watch("organizationId");

  const { organizationsResponse, isLoading: isLoadingOrgs } = useOrganizations(
    "Org",
    { take: 3000 },
  );

  const { data: unitsResponse, isLoading: isLoadingUnits } = getList(
    selectedOrganizationId,
    { take: 3000, skip: 0 },
  );

  const organizationOptions = useMemo(
    () =>
      (organizationsResponse?.items ?? []).map((org: OrganizationDto) => ({
        value: org.id,
        label: localizedName(org.name) || org.id,
      })),
    [organizationsResponse, localizedName],
  );

  const unitOptions = useMemo(
    () =>
      (unitsResponse?.data?.items ?? []).map((unit: UnitDto) => ({
        value: unit.id,
        label: localizedName(unit.name) || unit.id,
      })),
    [unitsResponse, localizedName],
  );

  const {
    data: editUnitResponse,
    isSuccess: isUnitSuccess,
    isError: isUnitError,
  } = getById(initialValues?.unitId ?? "");

  const {
    data: permissionsResponse,
    isSuccess: isPermissionsSuccess,
    isError: isPermissionsError,
  } = useQuery({
    queryKey: ["position-type-permissions", positionTypeId],
    queryFn: () =>
      positionTypePermissionService.getPermissionsByPositionTypeId(
        positionTypeId!,
      ),
    enabled: mode === "edit" && !!positionTypeId,
  });

  // A position type belongs to a unit, and a unit to an organization — IAM has
  // no organizationId on the type itself and no organization-scoped route, so
  // the picked org narrows the list through its units. isSystem types are the
  // shared "commons" and stay available to every organization.
  const orgUnitIds = useMemo(
    () =>
      new Set(
        (unitsResponse?.data?.items ?? []).map((unit: UnitDto) => unit.id),
      ),
    [unitsResponse],
  );

  const copyFromOptions = useMemo(() => {
    if (!selectedOrganizationId) return [];
    return positionTypes.filter(
      (type: PositionTypeDto) =>
        type.id !== positionTypeId &&
        (type.isSystem || (!!type.unitId && orgUnitIds.has(type.unitId))),
    );
  }, [positionTypes, orgUnitIds, selectedOrganizationId, positionTypeId]);

  // Reset the selected unit when the organization changes so a unit from a
  // different org can't be submitted by mistake. The copy source is cleared
  // too — it is scoped to the old organization.
  useEffect(() => {
    if (mode === "edit") return;
    form.setValue("unitId", "");
    setCopyFromPositionId("");
  }, [selectedOrganizationId, mode, form]);

  useEffect(() => {
    if (mode !== "edit" || !initialValues || !positionTypeId) return;
    if (hasLoadedEditData.current) return;

    const isUnitDone = !initialValues.unitId || isUnitSuccess || isUnitError;
    const isPermissionsDone = isPermissionsSuccess || isPermissionsError;

    if (isUnitDone && isPermissionsDone) {
      hasLoadedEditData.current = true;

      const unit = editUnitResponse?.data;
      const ids = permissionsResponse?.data?.items?.map((p) => p.id) ?? [];
      loadedPermissionCount.current = ids.length;

      form.reset({
        nameAm: initialValues.nameAm,
        nameEn: initialValues.nameEn,
        organizationId: unit?.organizationId ?? userOrganizationId ?? "",
        unitId: unit?.id ?? initialValues.unitId ?? "",
        permissions: ids,
      });

      setIsLoadingEditData(false);
    }
  }, [
    mode,
    initialValues,
    positionTypeId,
    isUnitSuccess,
    isUnitError,
    isPermissionsSuccess,
    isPermissionsError,
    editUnitResponse,
    permissionsResponse,
    userOrganizationId,
    form,
  ]);

  const handlePermissionChange = (permissionId: string, checked: boolean) => {
    const currentPermissions = form.getValues("permissions");
    form.setValue(
      "permissions",
      checked
        ? [...currentPermissions, permissionId]
        : currentPermissions.filter((id) => id !== permissionId),
    );
  };

  const handleCopyFrom = async (sourcePositionTypeId: string) => {
    setCopyFromPositionId(sourcePositionTypeId);
    setIsCopying(true);
    try {
      const response =
        await positionTypePermissionService.getPermissionsByPositionTypeId(
          sourcePositionTypeId,
        );
      const ids = response.data.items?.map((p) => p.id) ?? [];
      form.setValue("permissions", ids);
    } catch (error) {
      handleError(error);
      toast.error(t("contentManagement.copyPermissionsFailed"));
    } finally {
      setIsCopying(false);
    }
  };

  const onSubmit = async (values: FormValues) => {
    const payload = {
      name: { am: values.nameAm, en: values.nameEn },
      key: values.nameEn.toLowerCase().replace(/\s+/g, "-"),
      unitId: values.unitId,
    };

    // Save the position type first. If this fails nothing else runs, and the
    // mutation's own onError surfaces the reason (403 for built-in types,
    // conflict on the globally-unique key, ...).
    let targetId = positionTypeId;
    try {
      if (mode === "edit" && positionTypeId) {
        await updatePositionType.mutateAsync({
          id: positionTypeId,
          data: payload,
        });
      } else {
        const response = await createPositionType.mutateAsync(payload);
        targetId = response.data.id;
      }
    } catch {
      return; // already reported by the mutation's onError
    }

    // assign-seconds-for-first replaces the whole set, but an empty secondIds
    // fails server-side — so "unassign everything" is not expressible. Keep the
    // save and tell the user their permissions were left alone.
    const mustClearAll =
      values.permissions.length === 0 && loadedPermissionCount.current > 0;

    if (targetId && values.permissions.length > 0) {
      try {
        await positionTypePermissionService.assignPermissionsToPositionType({
          firstId: targetId,
          secondIds: values.permissions,
        });
      } catch (error) {
        handleError(error);
        invalidatePositionTypeQueries(queryClient);
        toast.error(t("contentManagement.permissionsAssignFailed"));
        return;
      }
    }

    invalidatePositionTypeQueries(queryClient);
    queryClient.invalidateQueries({ queryKey: ["position-type-permissions"] });

    if (mustClearAll) {
      toast.warning(t("contentManagement.cannotClearAllPermissions"));
    } else {
      toast.success(t("contentManagement.permissionSuccess"));
    }

    if (onSuccess) {
      onSuccess();
    } else {
      navigate("/user-management/position-management");
    }
  };

  if (isLoadingEditData) {
    return (
      <div className="py-8 text-center text-muted-foreground">
        {t("common.loading")}
      </div>
    );
  }

  const selectedPermissionCount = form.watch("permissions").length;
  // form.formState.isSubmitting stays true for the whole async handler, so it
  // also covers the permission-assignment call that follows the save.
  const isBusy = form.formState.isSubmitting || isCopying;

  const copyFromPlaceholder = !selectedOrganizationId
    ? t("contentManagement.selectOrganizationToCopy")
    : isCopying || isLoadingPositionTypes || isLoadingUnits
      ? t("common.loading")
      : isErrorPositionTypes
        ? t("contentManagement.failedToLoadPositionTypes")
        : t("contentManagement.selectPositionToCopy");

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
        <FormField
          control={form.control}
          name="nameEn"
          render={({ field }) => (
            <FormItem>
              <FormLabel>{t("contentManagement.englishName")}</FormLabel>
              <FormControl>
                <Input {...field} placeholder="e.g. HR Coordinator" />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField
          control={form.control}
          name="nameAm"
          render={({ field }) => (
            <FormItem>
              <FormLabel>{t("contentManagement.amharicName")}</FormLabel>
              <FormControl>
                <Input {...field} placeholder="e.g. ሰብል አያት" />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        {/* Organization (searchable, all orgs) */}
        <FormField
          control={form.control}
          name="organizationId"
          render={({ field }) => (
            <FormItem>
              <FormLabel>{t("organization.organization")}</FormLabel>
              <SingleSelect
                options={organizationOptions}
                value={field.value}
                onValueChange={field.onChange}
                placeholder={
                  isLoadingOrgs
                    ? t("common.loading")
                    : t("organization.selectOrganization")
                }
              />
              <FormMessage />
            </FormItem>
          )}
        />

        {/* Unit Selector — searchable, scoped to picked org */}
        <FormField
          control={form.control}
          name="unitId"
          render={({ field }) => (
            <FormItem>
              <FormLabel>{t("organization.selectUnit")}</FormLabel>
              <SingleSelect
                options={unitOptions}
                value={field.value}
                onValueChange={field.onChange}
                placeholder={
                  !selectedOrganizationId
                    ? t("organization.selectOrganizationFirst")
                    : isLoadingUnits
                      ? t("common.loading")
                      : t("organization.selectUnit")
                }
              />
              <FormMessage />
            </FormItem>
          )}
        />

        <div className="mb-4 w-1/2">
          <label className="block text-sm font-medium text-gray-700">
            {t("contentManagement.selectApplication")}
          </label>
          <Select
            value={selectedApplicationId}
            onValueChange={setSelectedApplicationId}
            disabled={isLoadingApplications}>
            <SelectTrigger className="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
              <SelectValue
                placeholder={
                  isLoadingApplications
                    ? t("common.loading")
                    : t("contentManagement.selectApplication")
                }
              />
            </SelectTrigger>
            <SelectContent className="max-h-60 overflow-y-auto">
              {applications?.map((app) => (
                <SelectItem key={app.id} value={app.id}>
                  {localizedName(app.name)}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>

        <div className="space-y-2">
          <label className="block text-sm font-medium text-gray-700">
            {t("contentManagement.copyPermissionsFrom")}
          </label>
          <Select
            value={copyFromPositionId}
            onValueChange={handleCopyFrom}
            disabled={
              !selectedOrganizationId ||
              isLoadingPositionTypes ||
              isLoadingUnits ||
              isCopying
            }>
            <SelectTrigger className="w-full">
              <SelectValue placeholder={copyFromPlaceholder} />
            </SelectTrigger>
            <SelectContent className="max-h-60 overflow-y-auto">
              {copyFromOptions.map((p: PositionTypeDto) => (
                <SelectItem key={p.id} value={p.id}>
                  {localizedName(p.name)}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <p className="text-xs text-muted-foreground">
            {t("contentManagement.copyPermissionsHint")}
          </p>
        </div>

        <FormField
          control={form.control}
          name="permissions"
          render={({ field }) => (
            <FormItem>
              <FormLabel>
                {t("contentManagement.permission")}
                {selectedPermissionCount > 0 && (
                  <span className="ml-2 font-normal text-muted-foreground">
                    (
                    {t("contentManagement.permissionsSelected", {
                      count: selectedPermissionCount,
                    })}
                    )
                  </span>
                )}
              </FormLabel>
              <PermissionSearch
                selectedPermissions={field.value}
                onPermissionChange={handlePermissionChange}
                applicationId={selectedApplicationId}
              />
              <FormMessage />
            </FormItem>
          )}
        />

        <div className="flex justify-end gap-4">
          <Button
            type="button"
            variant="outline"
            disabled={isBusy}
            onClick={() => {
              if (onCancel) {
                onCancel();
              } else {
                window.history.back();
              }
            }}>
            {t("common.Cancel")}
          </Button>
          <Button type="submit" disabled={isBusy}>
            {form.formState.isSubmitting
              ? t("common.saving")
              : mode === "edit"
                ? t("delegation.update")
                : t("delegation.save")}
          </Button>
        </div>
      </form>
    </Form>
  );
};
