import { useWaitForConfig } from "@app/core/hooks/useWaitForConfig";
import {
  type PositionValidation,
  PositionValidationSchema,
} from "@app/validation/config/position.ts";
import { create } from "@bufbuild/protobuf";
import {
  DynamicForm,
  type DynamicFormFormInit,
} from "@components/Form/DynamicForm.tsx";
import { Button } from "@components/UI/Button.tsx";
import {
  type FlagName,
  usePositionFlags,
} from "@core/hooks/usePositionFlags.ts";
import { useMyNodeAsProto } from "@core/hooks/useNodesAsProto.ts";
import { useToast } from "@core/hooks/useToast.ts";
import { useDevice } from "@core/stores";
import { Protobuf } from "@meshtastic/sdk";
import { useConfigEditor, useSignal } from "@meshtastic/sdk-react";
import { LocateFixed } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";

interface PositionConfigProps {
  onFormInit: DynamicFormFormInit<PositionValidation>;
}

/**
 * Renders inside the Device GPS card. Pulls the browser's current location
 * via navigator.geolocation and writes lat/lng/altitude into the form.
 * No-op without a geolocation API (e.g. insecure context).
 */
function UseBrowserLocationButton() {
  const { setValue } = useFormContext<PositionValidation>();
  const { toast } = useToast();
  const { t } = useTranslation("config");
  const [busy, setBusy] = useState(false);

  if (typeof navigator === "undefined" || !navigator.geolocation) {
    return null;
  }

  const onClick = (): void => {
    setBusy(true);
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        setBusy(false);
        setValue("latitude", Number(pos.coords.latitude.toFixed(7)), {
          shouldDirty: true,
        });
        setValue("longitude", Number(pos.coords.longitude.toFixed(7)), {
          shouldDirty: true,
        });
        if (
          pos.coords.altitude !== null &&
          !Number.isNaN(pos.coords.altitude)
        ) {
          setValue("altitude", Math.round(pos.coords.altitude), {
            shouldDirty: true,
          });
        }
      },
      (err) => {
        setBusy(false);
        toast({
          title: t(
            "position.useBrowserLocation.failed",
            "Could not read browser location",
          ),
          description: err.message,
        });
      },
      { enableHighAccuracy: true, timeout: 10_000 },
    );
  };

  return (
    <Button type="button" variant="subtle" disabled={busy} onClick={onClick}>
      <LocateFixed className="mr-2 size-4" />
      {busy
        ? t("position.useBrowserLocation.busy", "Reading location…")
        : t("position.useBrowserLocation.label", "Use browser location")}
    </Button>
  );
}

const EMPTY_RADIO_SIGNAL = {
  value: {} as { position?: Protobuf.Config.Config_PositionConfig },
  peek: () => ({}) as { position?: Protobuf.Config.Config_PositionConfig },
  subscribe: () => () => {},
} as const;

export const Position = ({ onFormInit }: PositionConfigProps) => {
  useWaitForConfig({ configCase: "position" });

  const { config, getEffectiveConfig } = useDevice();
  const editor = useConfigEditor();
  const myNode = useMyNodeAsProto();
  const radio = useSignal(editor?.radio ?? EMPTY_RADIO_SIGNAL);

  const effectivePosition =
    radio.position ??
    (getEffectiveConfig("position") as
      | Protobuf.Config.Config_PositionConfig
      | undefined);

  const { flagsValue, activeFlags, toggleFlag, getAllFlags } = usePositionFlags(
    effectivePosition?.positionFlags ?? 0,
  );
  const { t } = useTranslation("config");

  const currentPosition = myNode?.position;
  const displayUnits = getEffectiveConfig("display")?.units;

  const formValues = useMemo(() => {
    return {
      ...config.position,
      ...effectivePosition,
      latitude: currentPosition?.latitudeI
        ? currentPosition.latitudeI / 1e7
        : undefined,
      longitude: currentPosition?.longitudeI
        ? currentPosition.longitudeI / 1e7
        : undefined,
      altitude: currentPosition?.altitude ?? 0,
    } as PositionValidation;
  }, [config.position, effectivePosition, currentPosition]);

  const onSubmit = (data: PositionValidation) => {
    const {
      latitude: _latitude,
      longitude: _longitude,
      altitude: _altitude,
      ...configData
    } = data;
    const payload = { ...configData, positionFlags: flagsValue };

    if (editor) {
      editor.setRadioSection(
        "position",
        payload as unknown as Protobuf.Config.Config_PositionConfig,
      );
    }

    if (
      data.fixedPosition &&
      data.latitude !== undefined &&
      data.longitude !== undefined
    ) {
      const message = create(Protobuf.Admin.AdminMessageSchema, {
        payloadVariant: {
          case: "setFixedPosition",
          value: create(Protobuf.Mesh.PositionSchema, {
            latitudeI: Math.round(data.latitude * 1e7),
            longitudeI: Math.round(data.longitude * 1e7),
            altitude: data.altitude || 0,
            time: Math.floor(Date.now() / 1000),
          }),
        },
      });

      editor?.queueAdminMessage(message);
    }
  };

  const onPositonFlagChange = useCallback(
    (name: string) => {
      return toggleFlag(name as FlagName);
    },
    [toggleFlag],
  );

  return (
    <DynamicForm<PositionValidation>
      onSubmit={(data) => {
        data.positionFlags = flagsValue;
        onSubmit(data);
      }}
      onFormInit={onFormInit}
      validationSchema={PositionValidationSchema}
      defaultValues={config.position}
      values={formValues}
      fieldGroups={[
        {
          label: t("position.positionPacket.label"),
          description: t("position.positionPacket.description"),
          fields: [
            {
              type: "number",
              name: "positionBroadcastSecs",
              label: t("position.broadcastInterval.label"),
              description: t("position.broadcastInterval.description"),
              properties: { suffix: t("unit.second.plural") },
            },
            {
              type: "toggle",
              name: "positionBroadcastSmartEnabled",
              label: t("position.smartPositionEnabled.label"),
              description: t("position.smartPositionEnabled.description"),
            },
            {
              type: "number",
              name: "broadcastSmartMinimumIntervalSecs",
              label: t("position.smartPositionMinInterval.label"),
              description: t("position.smartPositionMinInterval.description"),
              properties: { suffix: t("unit.second.plural") },
              disabledBy: [{ fieldName: "positionBroadcastSmartEnabled" }],
            },
            {
              type: "number",
              name: "broadcastSmartMinimumDistance",
              label: t("position.smartPositionMinDistance.label"),
              description: t("position.smartPositionMinDistance.description"),
              disabledBy: [{ fieldName: "positionBroadcastSmartEnabled" }],
            },
          ],
        },
        {
          label: t("position.deviceGps.label"),
          description: t("position.deviceGps.description"),
          footer: <UseBrowserLocationButton />,
          fields: [
            {
              type: "toggle",
              name: "fixedPosition",
              label: t("position.fixedPosition.label"),
              description: t("position.fixedPosition.description"),
            },
            {
              type: "number",
              name: "latitude",
              label: t("position.fixedPosition.latitude.label"),
              description: `${t("position.fixedPosition.latitude.description")} (Max 7 decimal precision)`,
              properties: {
                step: 0.0000001,
                suffix: "Degrees",
                fieldLength: { max: 10 },
              },
              disabledBy: [{ fieldName: "fixedPosition" }],
            },
            {
              type: "number",
              name: "longitude",
              label: t("position.fixedPosition.longitude.label"),
              description: `${t("position.fixedPosition.longitude.description")} (Max 7 decimal precision)`,
              properties: {
                step: 0.0000001,
                suffix: "Degrees",
                fieldLength: { max: 10 },
              },
              disabledBy: [{ fieldName: "fixedPosition" }],
            },
            {
              type: "number",
              name: "altitude",
              label: t("position.fixedPosition.altitude.label"),
              description: t("position.fixedPosition.altitude.description", {
                unit:
                  displayUnits ===
                  Protobuf.Config.Config_DisplayConfig_DisplayUnits.IMPERIAL
                    ? "Feet"
                    : "Meters",
              }),
              properties: {
                step: 0.0000001,
                suffix:
                  displayUnits ===
                  Protobuf.Config.Config_DisplayConfig_DisplayUnits.IMPERIAL
                    ? "Feet"
                    : "Meters",
              },
              disabledBy: [{ fieldName: "fixedPosition" }],
            },
            {
              type: "select",
              name: "gpsMode",
              label: t("position.gpsMode.label"),
              description: t("position.gpsMode.description"),
              properties: {
                enumValue: Protobuf.Config.Config_PositionConfig_GpsMode,
              },
              disabledBy: [{ fieldName: "fixedPosition", invert: true }],
            },
            {
              type: "number",
              name: "gpsUpdateInterval",
              label: t("position.gpsUpdateInterval.label"),
              description: t("position.gpsUpdateInterval.description"),
              properties: { suffix: t("unit.second.plural") },
              disabledBy: [{ fieldName: "fixedPosition", invert: true }],
            },
          ],
        },
        {
          label: t("position.positionFlags.label"),
          description: t("position.positionFlags.description"),
          fields: [
            {
              type: "multiSelect",
              name: "positionFlags",
              value: activeFlags,
              isChecked: (name: string) =>
                activeFlags?.includes(name as FlagName) ?? false,
              onValueChange: onPositonFlagChange,
              label: t("position.positionFlags.label"),
              placeholder: t("position.flags.placeholder"),
              description: t("position.positionFlags.description"),
              properties: {
                enumValue: getAllFlags(),
              },
            },
          ],
        },
        {
          label: t("position.advancedDeviceGps.label"),
          description: t("position.advancedDeviceGps.description"),
          fields: [
            {
              type: "number",
              name: "rxGpio",
              label: t("position.receivePin.label"),
              description: t("position.receivePin.description"),
            },
            {
              type: "number",
              name: "txGpio",
              label: t("position.transmitPin.label"),
              description: t("position.transmitPin.description"),
            },
            {
              type: "number",
              name: "gpsEnGpio",
              label: t("position.enablePin.label"),
              description: t("position.enablePin.description"),
            },
          ],
        },
      ]}
    />
  );
};
