import React, { useEffect, useMemo, useRef, useState } from "react";
import {
  View,
  Text as RNText,
  StyleSheet,
  ScrollView,
  ActivityIndicator,
  TouchableOpacity,
  TextInput,
  Alert,
  Pressable,
  Modal,
  Keyboard,
} from "react-native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import {
  Central,
  ScannedPeripheral,
  ConnectionStatus,
} from "@systemic-games/react-native-bluetooth-le";
import { RootStackParamList } from "../App";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import MyStatusbar from "./component/MyStatusbar";
import MyHeader from "./component/MyHeader";
import { useTranslation } from "react-i18next";
import { observeFtmsIndoorBikeData } from "./helper/ftmsIndoorBikeDataBus";

type Props = NativeStackScreenProps<RootStackParamList, "Info3">;

type TooltipKey = "weight" | "trim" | "bikeType" | "ergSmooth" | null;
type DropdownKey = "bikeType" | "ergSmooth" | null;
type PendingAction = "weight" | "powerTrim" | null;
type Fff3WaitType =
  | "powerTrimRead"
  | "powerTrimAck"
  | "weightRead"
  | "weightAck"
  | "bikeTypeRead"
  | "bikeTypeAck"
  | "ergSmoothRead"
  | "ergSmoothAck"
  | "usedMileageRead"
  | null;

const powerServiceUuid = "fff1";
const powerWriteUuid = "fff2";
const powerNotifyUuid = "fff3";

const fullUUID = (uuid: string) =>
  uuid.length === 4
    ? `0000${uuid}-0000-1000-8000-00805f9b34fb`
    : uuid;

const Text = ({ children, style, ...props }: any) => {
  return (
    <RNText style={[{ color: "black", fontSize: 16 }, style]} {...props}>
      {children}
    </RNText>
  );
};

export default function InfoScreen3({ route, navigation }: Props) {
  const { peripheral } = route.params;
  const { t } = useTranslation();

  const deviceKey = useMemo(
    () => peripheral.address || peripheral.systemId,
    [peripheral.address, peripheral.systemId]
  );

  const [power, setPower] = useState<number>(0);
  const [cadence, setCadence] = useState<number>(0);
  const [speedKph, setSpeedKph] = useState<number>(0);

  const [isConnected, setIsConnected] = useState(false);
  const [isFtmsSessionReady, setIsFtmsSessionReady] = useState(false);
  const [isFff3Ready, setIsFff3Ready] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  const [serial, setSerial] = useState(t("info.reading"));
  const [firmware, setFirmware] = useState(t("info.reading"));
  const [, setHardware] = useState(t("info.reading"));
  const [, setBattery] = useState(t("info.reading"));

  // 当前已生效值
  const [weight, setWeight] = useState("70");
  const [powerTrim, setPowerTrim] = useState("100.00");

  // 编辑中的草稿值
  const [pendingWeight, setPendingWeight] = useState("70");
  const [pendingPowerTrim, setPendingPowerTrim] = useState("100.00");

  const [bikeType, setBikeType] = useState(t("info3.bikeTypeFollow"));
  const [ergSmooth, setErgSmooth] = useState(t("info3.ergOff"));
  const [usedMileage, setUsedMileage] = useState(t("info.reading"));

  const [confirmModalVisible, setConfirmModalVisible] = useState(false);
  const [progressModalVisible, setProgressModalVisible] = useState(false);
  const [progressText, setProgressText] = useState("");
  const [pendingAction, setPendingAction] = useState<PendingAction>(null);

  const [visibleTooltip, setVisibleTooltip] = useState<TooltipKey>(null);
  const [openDropdown, setOpenDropdown] = useState<DropdownKey>(null);

  const [dropdownOptions, setDropdownOptions] = useState<string[]>([]);
  const [dropdownTitle, setDropdownTitle] = useState("");
  const [dropdownOnSelect, setDropdownOnSelect] = useState<
    ((value: string) => void | Promise<void>) | null
  >(null);

  const [isSettingBikeType, setIsSettingBikeType] = useState(false);
  const [isSettingErgSmooth, setIsSettingErgSmooth] = useState(false);
  const [isInitializingSettings, setIsInitializingSettings] = useState(false);

  const notifySubscribedRef = useRef(false);
  const disconnectingRef = useRef(false);
  const mountedRef = useRef(true);
  const hasConnectedOnceRef = useRef(false);
  const hasNavigatedBackRef = useRef(false);
  const skipDisconnectOnLeaveRef = useRef(false);

  const fff3WaitTypeRef = useRef<Fff3WaitType>(null);
  const fff3ResolverRef = useRef<((value: Uint8Array) => void) | null>(null);
  const fff3RejecterRef = useRef<((reason?: any) => void) | null>(null);
  const fff3TimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const fff3OperationQueueRef = useRef<Promise<void>>(Promise.resolve());

  const clearFff3Waiter = () => {
    fff3WaitTypeRef.current = null;
    fff3ResolverRef.current = null;
    fff3RejecterRef.current = null;
    if (fff3TimeoutRef.current) {
      clearTimeout(fff3TimeoutRef.current);
      fff3TimeoutRef.current = null;
    }
  };

  const decodeBase64 = (value: string) => {
    const atobFn = (globalThis as { atob?: (data: string) => string }).atob;

    if (typeof atobFn === "function") {
      const binary = atobFn(value);
      const bytes = new Uint8Array(binary.length);

      for (let i = 0; i < binary.length; i += 1) {
        bytes[i] = binary.charCodeAt(i);
      }

      return bytes;
    }

    const chars =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    let buffer = 0;
    let bits = 0;
    const output: number[] = [];

    for (const char of value.replace(/=+$/, "")) {
      const index = chars.indexOf(char);
      if (index < 0) continue;

      buffer = (buffer << 6) | index;
      bits += 6;

      if (bits >= 8) {
        bits -= 8;
        output.push((buffer >> bits) & 0xff);
      }
    }

    return new Uint8Array(output);
  };

  const toUint8Array = (raw: unknown): Uint8Array | null => {
    try {
      if (!raw) return null;
      if (raw instanceof Uint8Array) return raw;
      if (raw instanceof ArrayBuffer) return new Uint8Array(raw);
      if (typeof raw === "string") return decodeBase64(raw);
      if (Array.isArray(raw)) return new Uint8Array(raw);

      if (
        typeof raw === "object" &&
        raw !== null &&
        "buffer" in raw &&
        (raw as { buffer?: unknown }).buffer instanceof ArrayBuffer
      ) {
        const view = raw as {
          buffer: ArrayBuffer;
          byteOffset?: number;
          byteLength?: number;
        };
        return new Uint8Array(
          view.buffer,
          view.byteOffset || 0,
          view.byteLength
        );
      }

      return null;
    } catch {
      return null;
    }
  };

  const enqueueFff3Operation = async <T,>(task: () => Promise<T>) => {
    const previous = fff3OperationQueueRef.current;
    let release!: () => void;

    fff3OperationQueueRef.current = new Promise<void>((resolve) => {
      release = resolve;
    });

    await previous;

    try {
      return await task();
    } finally {
      release();
    }
  };

  const floatWeightToDeviceValue = (weightKgText: string) => {
    const weightNum = Number(weightKgText);
    return Math.round(weightNum * 100);
  };

  const deviceValueToWeightText = (deviceValue: number) => {
    return (deviceValue / 100).toFixed(2).replace(/\.?0+$/, "");
  };

  const isValidWeightInput = (text: string) => {
    if (!text) return false;
    if (!/^\d+(\.\d{1,2})?$/.test(text)) return false;
    const num = Number(text);
    return Number.isFinite(num) && num > 0 && num <= 300;
  };

  const powerTrimTextToDeviceValue = (trimText: string) => {
    const trimNum = Number(trimText);
    return Math.round(trimNum * 100);
  };

  const deviceValueToPowerTrimText = (deviceValue: number) => {
    return (deviceValue / 100).toFixed(2);
  };

  const isValidPowerTrimInput = (text: string) => {
    if (!text) return false;
    if (!/^\d+(\.\d{1,2})?$/.test(text)) return false;
    const num = Number(text);
    return Number.isFinite(num) && num >= 50 && num <= 200;
  };

  const bikeTypeToCode = (text: string) => {
    switch (text) {
      case t("info3.bikeTypeFollow"):
        return 0x00;
      case t("info3.bikeTypeRoad"):
        return 0x01;
      case t("info3.bikeTypeMtb26"):
        return 0x02;
      case t("info3.bikeTypeMtb275"):
        return 0x03;
      case t("info3.bikeTypeMtb29"):
        return 0x04;
      case t("info3.bikeTypeSmallWheel"):
        return 0x05;
      default:
        return 0x00;
    }
  };

  const codeToBikeType = (code: number) => {
    switch (code) {
      case 0x00:
        return t("info3.bikeTypeFollow");
      case 0x01:
        return t("info3.bikeTypeRoad");
      case 0x02:
        return t("info3.bikeTypeMtb26");
      case 0x03:
        return t("info3.bikeTypeMtb275");
      case 0x04:
        return t("info3.bikeTypeMtb29");
      case 0x05:
        return t("info3.bikeTypeSmallWheel");
      default:
        return t("info3.bikeTypeFollow");
    }
  };

  const ergSmoothToCode = (text: string) => {
    switch (text) {
      case t("info3.ergOff"):
        return 0x00;
      case t("info3.ergOn"):
        return 0x01;
      default:
        return 0x00;
    }
  };

  const codeToErgSmooth = (code: number) => {
    switch (code) {
      case 0x00:
        return t("info3.ergOff");
      case 0x01:
        return t("info3.ergOn");
      default:
        return t("info3.ergOff");
    }
  };

  const parseUsedHoursFromFff3 = (value: Uint8Array): number | null => {
    if (value.length < 3) return null;
    if (value[0] !== 0x0b) return null;
    const raw = value[1] | (value[2] << 8);
    return raw / 10.0;
  };

  const hasUnsavedWeight = pendingWeight.trim() !== weight;
  const hasUnsavedPowerTrim = pendingPowerTrim.trim() !== powerTrim;

  const subscribeFff3IfNeeded = async () => {
    if (notifySubscribedRef.current) return;

    try {
      await Central.unsubscribeCharacteristic(
        peripheral,
        fullUUID(powerServiceUuid),
        fullUUID(powerNotifyUuid)
      ).catch(() => {});

      await Central.subscribeCharacteristic(
        peripheral,
        fullUUID(powerServiceUuid),
        fullUUID(powerNotifyUuid),
        (notifyEv) => {
          try {
            const bytes = toUint8Array(notifyEv.value);
            if (!bytes || bytes.length === 0) return;

            if (fff3ResolverRef.current) {
              fff3ResolverRef.current(bytes);
            }
          } catch (err) {
            console.warn("处理 FFF3 通知失败:", err);
            if (fff3RejecterRef.current) {
              fff3RejecterRef.current(err);
            }
            clearFff3Waiter();
          }
        }
      );

      notifySubscribedRef.current = true;
      setIsFff3Ready(true);
    } catch (err) {
      console.warn("FFF3 notify 订阅失败:", err);
      setIsFff3Ready(false);
    }
  };

  const waitPowerTrimAck = (expectedValue: number, timeout = 3000) => {
    return new Promise<void>((resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "powerTrimAck";

      fff3ResolverRef.current = (value: Uint8Array) => {
        const low = expectedValue & 0xff;
        const high = (expectedValue >> 8) & 0xff;

        if (
          value.length >= 3 &&
          value[0] === 0x01 &&
          value[1] === low &&
          value[2] === high
        ) {
          clearFff3Waiter();
          resolve();
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.waitFff3Timeout")));
      }, timeout);
    });
  };

  const requestCurrentPowerTrim = (timeout = 3000) => {
    return enqueueFff3Operation(
      () =>
        new Promise<number>(async (resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "powerTrimRead";

      fff3ResolverRef.current = (value: Uint8Array) => {
        if (value.length >= 3 && value[0] === 0x01) {
          const currentTrim = value[1] | (value[2] << 8);
          clearFff3Waiter();
          resolve(currentTrim);
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.readPowerTrimTimeout")));
      }, timeout);

      try {
        await Central.writeCharacteristic(
          peripheral,
          fullUUID(powerServiceUuid),
          fullUUID(powerWriteUuid),
          new Uint8Array([0x02]).buffer,
          { withoutResponse: false }
        );
      } catch (err) {
        clearFff3Waiter();
        reject(err);
      }
        })
    );
  };

  const waitWeightAck = (expectedWeightValue: number, timeout = 3000) => {
    return new Promise<void>((resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "weightAck";

      fff3ResolverRef.current = (value: Uint8Array) => {
        const low = expectedWeightValue & 0xff;
        const high = (expectedWeightValue >> 8) & 0xff;

        if (
          (value.length >= 4 &&
            value[0] === 0x0e &&
            value[1] === low &&
            value[2] === high &&
            value[3] === 0x00) ||
          (value.length >= 4 &&
            value[0] === 0x0f &&
            value[1] === low &&
            value[2] === high &&
            value[3] === 0x00) ||
          (value.length >= 3 &&
            value[0] === 0x0e &&
            value[1] === low &&
            value[2] === high)
        ) {
          clearFff3Waiter();
          resolve();
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.waitWeightAckTimeout")));
      }, timeout);
    });
  };

  const requestCurrentWeight = (timeout = 3000) => {
    return enqueueFff3Operation(
      () =>
        new Promise<number>(async (resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "weightRead";

      fff3ResolverRef.current = (value: Uint8Array) => {
        if (value.length >= 4 && value[0] === 0x0e && value[3] === 0x00) {
          const weightDeviceValue = value[1] | (value[2] << 8);
          clearFff3Waiter();
          resolve(weightDeviceValue);
          return;
        }

        if (value.length >= 4 && value[0] === 0x0f && value[3] === 0x00) {
          const weightDeviceValue = value[1] | (value[2] << 8);
          clearFff3Waiter();
          resolve(weightDeviceValue);
          return;
        }

        if (value.length >= 3 && value[0] === 0x0e) {
          const weightDeviceValue = value[1] | (value[2] << 8);
          clearFff3Waiter();
          resolve(weightDeviceValue);
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.readWeightTimeout")));
      }, timeout);

      try {
        await Central.writeCharacteristic(
          peripheral,
          fullUUID(powerServiceUuid),
          fullUUID(powerWriteUuid),
          new Uint8Array([0x0f]).buffer,
          { withoutResponse: false }
        );
      } catch (err) {
        clearFff3Waiter();
        reject(err);
      }
        })
    );
  };

  const requestCurrentBikeType = (timeout = 3000) => {
    return enqueueFff3Operation(
      () =>
        new Promise<number>(async (resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "bikeTypeRead";

      fff3ResolverRef.current = (value: Uint8Array) => {
        if (
          value.length >= 3 &&
          ((value[0] === 0x05 && value[2] === 0x00) ||
            (value[0] === 0x06 && value[2] === 0x00))
        ) {
          clearFff3Waiter();
          resolve(value[1]);
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.readBikeTypeTimeout")));
      }, timeout);

      try {
        await Central.writeCharacteristic(
          peripheral,
          fullUUID(powerServiceUuid),
          fullUUID(powerWriteUuid),
          new Uint8Array([0x06]).buffer,
          { withoutResponse: false }
        );
      } catch (err) {
        clearFff3Waiter();
        reject(err);
      }
        })
    );
  };

  const waitBikeTypeAck = (expectedCode: number, timeout = 3000) => {
    return new Promise<void>((resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "bikeTypeAck";

      fff3ResolverRef.current = (value: Uint8Array) => {
        if (
          value.length >= 3 &&
          value[1] === expectedCode &&
          value[2] === 0x00 &&
          (value[0] === 0x05 || value[0] === 0x06)
        ) {
          clearFff3Waiter();
          resolve();
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.waitBikeTypeAckTimeout")));
      }, timeout);
    });
  };

  const requestCurrentErgSmooth = (timeout = 3000) => {
    return enqueueFff3Operation(
      () =>
        new Promise<number>(async (resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "ergSmoothRead";

      fff3ResolverRef.current = (value: Uint8Array) => {
        if (
          value.length >= 3 &&
          ((value[0] === 0x03 && value[2] === 0x00) ||
            (value[0] === 0x04 && value[2] === 0x00))
        ) {
          clearFff3Waiter();
          resolve(value[1]);
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.readErgTimeout")));
      }, timeout);

      try {
        await Central.writeCharacteristic(
          peripheral,
          fullUUID(powerServiceUuid),
          fullUUID(powerWriteUuid),
          new Uint8Array([0x04]).buffer,
          { withoutResponse: false }
        );
      } catch (err) {
        clearFff3Waiter();
        reject(err);
      }
        })
    );
  };

  const waitErgSmoothAck = (expectedCode: number, timeout = 3000) => {
    return new Promise<void>((resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "ergSmoothAck";

      fff3ResolverRef.current = (value: Uint8Array) => {
        if (
          value.length >= 3 &&
          value[1] === expectedCode &&
          value[2] === 0x00 &&
          (value[0] === 0x03 || value[0] === 0x04)
        ) {
          clearFff3Waiter();
          resolve();
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.waitErgAckTimeout")));
      }, timeout);
    });
  };

  const requestUsedHours = (timeout = 3000) => {
    return enqueueFff3Operation(
      () =>
        new Promise<number>(async (resolve, reject) => {
      clearFff3Waiter();
      fff3WaitTypeRef.current = "usedMileageRead";

      fff3ResolverRef.current = (value: Uint8Array) => {
        const parsedHours = parseUsedHoursFromFff3(value);
        if (parsedHours !== null) {
          clearFff3Waiter();
          resolve(parsedHours);
        }
      };

      fff3RejecterRef.current = reject;
      fff3TimeoutRef.current = setTimeout(() => {
        clearFff3Waiter();
        reject(new Error(t("info3.readUsedHoursTimeout")));
      }, timeout);

      try {
        await Central.writeCharacteristic(
          peripheral,
          fullUUID(powerServiceUuid),
          fullUUID(powerWriteUuid),
          new Uint8Array([0x0c]).buffer,
          { withoutResponse: false }
        );
      } catch (err) {
        clearFff3Waiter();
        reject(err);
      }
        })
    );
  };

  const applyPowerTrim = async (trimText: string) => {
    return enqueueFff3Operation(async () => {
    if (!isValidPowerTrimInput(trimText)) {
      throw new Error(t("info3.powerTrimRangeError"));
    }

    const trimValue = powerTrimTextToDeviceValue(trimText);
    const low = trimValue & 0xff;
    const high = (trimValue >> 8) & 0xff;

    if (!isConnected) {
      throw new Error(t("info.deviceNotConnected"));
    }

    if (!isFff3Ready) {
      throw new Error(t("info3.deviceNotReady"));
    }

    const ackPromise = waitPowerTrimAck(trimValue, 3000);

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x01, low, high]).buffer,
      { withoutResponse: false }
    );

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x02]).buffer,
      { withoutResponse: false }
    );

    await ackPromise;
    });
  };

  const applyWeight = async (weightText: string) => {
    return enqueueFff3Operation(async () => {
    if (!isValidWeightInput(weightText)) {
      throw new Error(t("info3.invalidWeight"));
    }

    if (!isConnected) {
      throw new Error(t("info.deviceNotConnected"));
    }

    if (!isFff3Ready) {
      throw new Error(t("info3.deviceNotReady"));
    }

    const deviceWeight = floatWeightToDeviceValue(weightText);
    const low = deviceWeight & 0xff;
    const high = (deviceWeight >> 8) & 0xff;

    const ackPromise = waitWeightAck(deviceWeight, 3000);

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x0e, low, high]).buffer,
      { withoutResponse: false }
    );

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x0f]).buffer,
      { withoutResponse: false }
    );

    await ackPromise;
    });
  };

  const applyBikeType = async (bikeTypeText: string) => {
    return enqueueFff3Operation(async () => {
    if (!isConnected) {
      throw new Error(t("info.deviceNotConnected"));
    }

    if (!isFff3Ready) {
      throw new Error(t("info3.deviceNotReady"));
    }

    const code = bikeTypeToCode(bikeTypeText);

    const ackPromise = waitBikeTypeAck(code, 3000);

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x05, code]).buffer,
      { withoutResponse: false }
    );

    await new Promise<void>((resolve) => setTimeout(resolve, 180));

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x06]).buffer,
      { withoutResponse: false }
    );

    await ackPromise;
    });
  };

  const applyErgSmooth = async (ergSmoothText: string) => {
    return enqueueFff3Operation(async () => {
    if (!isConnected) {
      throw new Error(t("info.deviceNotConnected"));
    }

    if (!isFff3Ready) {
      throw new Error(t("info3.deviceNotReady"));
    }

    const code = ergSmoothToCode(ergSmoothText);

    const ackPromise = waitErgSmoothAck(code, 3000);

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x03, code]).buffer,
      { withoutResponse: false }
    );

    await new Promise<void>((resolve) => setTimeout(resolve, 180));

    await Central.writeCharacteristic(
      peripheral,
      fullUUID(powerServiceUuid),
      fullUUID(powerWriteUuid),
      new Uint8Array([0x04]).buffer,
      { withoutResponse: false }
    );

    await ackPromise;
    });
  };

  /* eslint-disable react-hooks/exhaustive-deps */
  useEffect(() => {
    mountedRef.current = true;

    const connectionHandler = async (ev: {
      peripheral: ScannedPeripheral;
      connectionStatus: ConnectionStatus;
    }) => {
      const addr = ev.peripheral.address || ev.peripheral.systemId;
      if (addr !== deviceKey) return;

      setIsConnected(
        ev.connectionStatus === "connected" || ev.connectionStatus === "ready"
      );
      setIsFtmsSessionReady(ev.connectionStatus === "ready");

      const connected =
        ev.connectionStatus === "connected" || ev.connectionStatus === "ready";

      if (connected) {
        hasConnectedOnceRef.current = true;
      }

      if (ev.connectionStatus !== "ready") {
        setIsFff3Ready(false);
        notifySubscribedRef.current = false;
      }

      if (ev.connectionStatus === "ready" && !notifySubscribedRef.current) {
        await subscribeFff3IfNeeded();
      }

      if (
        !connected &&
        hasConnectedOnceRef.current &&
        mountedRef.current &&
        !disconnectingRef.current &&
        !hasNavigatedBackRef.current
      ) {
        hasNavigatedBackRef.current = true;
        skipDisconnectOnLeaveRef.current = true;

        Alert.alert(t("common.notice"), t("info2.disconnectedNeedReconnect"), [
          {
            text: t("info.confirm"),
            onPress: () => {
              navigation.reset({
                index: 0,
                routes: [{ name: "ScanScreen3" }],
              });
            },
          },
        ]);
      }
    };

    Central.addListener("peripheralConnectionStatus", connectionHandler);
    return () => {
      mountedRef.current = false;
      Central.removeListener("peripheralConnectionStatus", connectionHandler);
      notifySubscribedRef.current = false;
      setIsFff3Ready(false);
      clearFff3Waiter();
    };
  }, [deviceKey, navigation, peripheral, t]);
  /* eslint-enable react-hooks/exhaustive-deps */

  useEffect(() => {
    const unsubscribe = navigation.addListener("beforeRemove", async (e) => {
      if (skipDisconnectOnLeaveRef.current) return;
      if (disconnectingRef.current) return;
      disconnectingRef.current = true;

      const next = e.data.action?.type;
      const action = e.data.action as any;

      if (
        next === "NAVIGATE" &&
        (action?.payload?.name === "Dfu" ||
          action?.payload?.name === "Spindown")
      ) {
        disconnectingRef.current = false;
        return;
      }

      try {
        await Central.disconnectPeripheral(peripheral);
      } catch (err) {
        console.warn("断开失败：", err);
      } finally {
        disconnectingRef.current = false;
      }
    });

    return unsubscribe;
  }, [navigation, peripheral]);

  useEffect(() => {
    (async () => {
      setIsLoading(true);
      try {
        await Central.connectPeripheral(peripheral);
        await new Promise<void>((resolve) => setTimeout(resolve, 500));
        setIsFtmsSessionReady(true);

        const readStr = async (srv: string, char: string) => {
          try {
            const v = await Central.readCharacteristic(
              peripheral,
              fullUUID(srv),
              fullUUID(char)
            );
            return v ? String.fromCharCode(...(v as any)) : t("common.unknown");
          } catch {
            return t("common.unknown");
          }
        };

        setSerial(await readStr("180a", "2a25"));
        setFirmware(await readStr("180a", "2a28"));
        setHardware(await readStr("180a", "2a27"));

        try {
          const v = await Central.readCharacteristic(
            peripheral,
            fullUUID("180f"),
            fullUUID("2a19")
          );
          if (v && (v as any).length) setBattery(`${(v as any)[0]}%`);
          else setBattery(t("common.unknown"));
        } catch {
          setBattery(t("common.unknown"));
        }
      } catch (e) {
        console.warn("读取失败", e);
        setIsFtmsSessionReady(false);
        Alert.alert(t("common.notice"), t("info3.connectReadFailed"));
      } finally {
        setIsLoading(false);
      }
    })();
  }, [peripheral, t]);

  /* eslint-disable react-hooks/exhaustive-deps */
  useEffect(() => {
    if (!isConnected || !isFff3Ready) return;

    let cancelled = false;

    (async () => {
      if (!cancelled) {
        setIsInitializingSettings(true);
      }

      try {
        await new Promise<void>((resolve) => setTimeout(resolve, 250));
        const currentTrim = await requestCurrentPowerTrim(3000);
        if (!cancelled) {
          const trimStr = deviceValueToPowerTrimText(currentTrim);
          setPowerTrim(trimStr);
          setPendingPowerTrim(trimStr);
        }
      } catch (err) {
        console.warn("读取当前功率微调失败:", err);
      }

      try {
        await new Promise<void>((resolve) => setTimeout(resolve, 120));
        const currentWeightValue = await requestCurrentWeight(3000);
        if (!cancelled) {
          const weightStr = deviceValueToWeightText(currentWeightValue);
          setWeight(weightStr);
          setPendingWeight(weightStr);
        }
      } catch (err) {
        console.warn("读取当前体重失败:", err);
      }

      try {
        await new Promise<void>((resolve) => setTimeout(resolve, 120));
        const currentUsedHours = await requestUsedHours(3000);
        if (!cancelled) {
          setUsedMileage(`${currentUsedHours.toFixed(1)}km`);
        }
      } catch (err) {
        console.warn("读取使用时长失败:", err);
        if (!cancelled) {
          setUsedMileage(t("info3.readFailed"));
        }
      }

      try {
        await new Promise<void>((resolve) => setTimeout(resolve, 120));
        const currentBikeTypeCode = await requestCurrentBikeType(3000);
        if (!cancelled) {
          setBikeType(codeToBikeType(currentBikeTypeCode));
        }
      } catch (err) {
        console.warn("读取当前车型失败:", err);
      }

      try {
        await new Promise<void>((resolve) => setTimeout(resolve, 120));
        const currentErgSmoothCode = await requestCurrentErgSmooth(3000);
        if (!cancelled) {
          setErgSmooth(codeToErgSmooth(currentErgSmoothCode));
        }
      } catch (err) {
        console.warn("读取当前ERG功率平滑失败:", err);
      } finally {
        if (!cancelled) {
          setIsInitializingSettings(false);
        }
      }
    })();

    return () => {
      cancelled = true;
      setIsInitializingSettings(false);
    };
  }, [isConnected, isFff3Ready, t]);
  /* eslint-enable react-hooks/exhaustive-deps */

  useEffect(() => {
    if (!peripheral || !isFtmsSessionReady) return;

    let cancelled = false;
    let cleanup: (() => void) | null = null;

    observeFtmsIndoorBikeData(peripheral, (data) => {
      if (cancelled) return;
      setSpeedKph(data.speedKph);
      setCadence(data.cadence);
      setPower(data.power);
    })
      .then((unsubscribe) => {
        if (cancelled) {
          unsubscribe();
          return;
        }
        cleanup = unsubscribe;
      })
      .catch((err) => {
        console.warn("订阅共享 2AD2 失败", err);
      });

    return () => {
      cancelled = true;
      cleanup?.();
    };
  }, [peripheral, isFtmsSessionReady]);

  const goToDfu = () => {
    if (!isConnected) {
      Alert.alert(t("common.notice"), t("info2.disconnectedNeedReconnect"), [
        {
          text: t("info.confirm"),
          onPress: () => {
            if (navigation.canGoBack()) {
              navigation.goBack();
            }
          },
        },
      ]);
      return;
    }

    skipDisconnectOnLeaveRef.current = true;
    navigation.navigate("Dfu", {
      deviceId: String(deviceKey),
      systemId: peripheral.systemId,
      address: peripheral.address,
      name: peripheral.name ?? "",
      firmware: firmware ?? "",
    });
  };

  useEffect(() => {
    return () => {
      clearFff3Waiter();
    };
  }, []);

  const startPendingActionFlow = async (action: PendingAction) => {
    if (!action) return;

    setConfirmModalVisible(false);
    setPendingAction(action);

    if (action === "weight") {
      setProgressText(t("info3.settingWeight"));
      setProgressModalVisible(true);

      try {
        await applyWeight(pendingWeight);
        setWeight(pendingWeight.trim());
        setPendingWeight(pendingWeight.trim());
        setProgressModalVisible(false);
        Alert.alert(t("common.notice"), t("info3.weightSetDone"));
      } catch (err: any) {
        setProgressModalVisible(false);
        Alert.alert(
          t("common.notice"),
          err?.message || t("info3.weightSetFailed")
        );
      } finally {
        setPendingAction(null);
      }
      return;
    }

    setProgressText(t("info3.settingPowerTrim"));
    setProgressModalVisible(true);

    try {
      await applyPowerTrim(pendingPowerTrim);
      setPowerTrim(pendingPowerTrim.trim());
      setPendingPowerTrim(pendingPowerTrim.trim());
      setProgressModalVisible(false);
      Alert.alert(t("common.notice"), t("info3.powerTrimSetDone"));
    } catch (err: any) {
      setProgressModalVisible(false);
      Alert.alert(
        t("common.notice"),
        err?.message || t("info3.powerTrimSetFailed")
      );
    } finally {
      setPendingAction(null);
    }
  };

  const onPressWeightSave = () => {
    Keyboard.dismiss();
    const nextValue = pendingWeight.trim();

    if (!nextValue || nextValue === weight) {
      setPendingWeight(weight);
      return;
    }

    if (!isValidWeightInput(nextValue)) {
      Alert.alert(t("common.notice"), t("info3.invalidWeight"));
      return;
    }

    setPendingAction("weight");
    setConfirmModalVisible(true);
  };

  const onPressPowerTrimSave = () => {
    Keyboard.dismiss();
    const nextValue = pendingPowerTrim.trim();

    if (!nextValue || nextValue === powerTrim) {
      setPendingPowerTrim(powerTrim);
      return;
    }

    if (!isValidPowerTrimInput(nextValue)) {
      Alert.alert(t("common.notice"), t("info3.powerTrimRangeError"));
      return;
    }

    setPendingAction("powerTrim");
    setConfirmModalVisible(true);
  };

  const closeConfirmModal = () => {
    setConfirmModalVisible(false);
    setPendingAction(null);
  };

  const renderHelp = (key: Exclude<TooltipKey, null>) => {
    return (
      <Pressable
        onPressIn={() => setVisibleTooltip(key)}
        onPressOut={() => setVisibleTooltip(null)}
        style={styles.helpWrap}
      >
        <View style={styles.helpDot}>
          <Text style={styles.helpDotText}>?</Text>
        </View>
        {visibleTooltip === key && (
          <View style={styles.tooltipBubble}>
            <Text style={styles.tooltipText}>{t("info3.helpText")}</Text>
          </View>
        )}
      </Pressable>
    );
  };

  const renderStatusTag = (text: string, variant: "pending" | "active") => {
    return (
      <View
        style={[
          styles.statusTag,
          variant === "pending" ? styles.statusTagPending : styles.statusTagActive,
        ]}
      >
        <Text
          style={[
            styles.statusTagText,
            variant === "pending"
              ? styles.statusTagPendingText
              : styles.statusTagActiveText,
          ]}
        >
          {text}
        </Text>
      </View>
    );
  };

  const renderDropdown = (
    key: Exclude<DropdownKey, null>,
    value: string,
    options: string[],
    onSelect: (value: string) => void | Promise<void>,
    disabled = false,
    loading = false
  ) => {
    const isOpen = openDropdown === key;

    return (
      <View style={styles.dropdownWrap}>
        <TouchableOpacity
          activeOpacity={0.85}
          style={[styles.selectBox, disabled && styles.selectBoxDisabled]}
          onPress={() => {
            if (!isFff3Ready || disabled) return;
            Keyboard.dismiss();
            setDropdownOptions(options);
            setDropdownTitle(
              key === "bikeType" ? t("info3.bikeType") : t("info3.ergSmooth")
            );
            setDropdownOnSelect(() => onSelect);
            setOpenDropdown(isOpen ? null : key);
          }}
          disabled={!isFff3Ready || disabled}
        >
          <Text
            style={[styles.selectText, disabled && styles.selectTextDisabled]}
            numberOfLines={1}
          >
            {value}
          </Text>

          {loading ? (
            <ActivityIndicator size={18} color={RED} />
          ) : (
            <Icon
              name={isOpen ? "menu-up" : "menu-down"}
              size={24}
              color="#222"
            />
          )}
        </TouchableOpacity>
      </View>
    );
  };

  return (
    <Pressable
      style={styles.screen}
      onPress={() => {
        Keyboard.dismiss();
        setOpenDropdown(null);
      }}
    >
      <MyStatusbar backgroundColor="#ffffff" dark />
      <MyHeader
        title={t("info.title")}
        textColor="#333"
        backgroundColor="#ffffff"
        navigation={navigation}
      />

      <ScrollView
        contentContainerStyle={styles.scrollContent}
        keyboardShouldPersistTaps="handled"
      >
        <View style={styles.card}>
          <View style={styles.infoRow}>
            <Text style={styles.infoLabel}>{t("info.bluetoothName")}:</Text>
            <Text style={styles.infoValue}>
              {peripheral.name || t("common.unknownDevice")}
            </Text>
          </View>

          <View style={styles.infoRow}>
            <Text style={styles.infoLabel}>{t("info.idNumber")}:</Text>
            <Text style={styles.infoValue}>{serial}</Text>
          </View>

          <View style={styles.infoRow}>
            <Text style={styles.infoLabel}>{t("info.firmwareVersion")}:</Text>
            <Text style={styles.infoValue}>{firmware}</Text>
          </View>

          <View style={styles.infoRow}>
            <Text style={styles.infoLabel}>{t("info3.usedMileage")}:</Text>
            <Text style={styles.infoValue}>{usedMileage}</Text>
          </View>

          <View style={styles.infoRow}>
            <Text style={styles.infoLabel}>{t("info.connectionStatus")}:</Text>
            <Text style={styles.infoValue}>
              {isConnected
                ? isFff3Ready
                  ? t("info.connected")
                  : t("info3.connecting")
                : t("info.disconnected")}
            </Text>
          </View>

          <View style={styles.metricsWrap}>
            <View style={styles.metricCard}>
              <Icon name="flash" size={26} color="#eb3b3b" />
              <Text style={styles.metricLabel}>{t("info.power")}</Text>
              <Text style={styles.metricValue}>{power}</Text>
            </View>

            <View style={styles.metricCard}>
              <Icon name="sync" size={26} color="#eb3b3b" />
              <Text style={styles.metricLabel}>{t("info.cadence")}</Text>
              <Text style={styles.metricValue}>{cadence}</Text>
            </View>

            <View style={styles.metricCard}>
              <Icon name="speedometer" size={26} color="#eb3b3b" />
              <Text style={styles.metricLabel}>{t("info3.speedKph")}</Text>
              <Text style={styles.metricValue}>{speedKph}</Text>
            </View>
          </View>

          <View style={styles.formBlock}>
            <View style={styles.formRowInline}>
              <View style={styles.formLabelColumn}>
                <View style={styles.formLabelWrap}>
                  <Text style={styles.formLabel}>{t("info3.weightSetting")}</Text>
                  {renderHelp("weight")}
                </View>
                <Text style={styles.currentValueBelow}>
                  {t("info3.currentWeightValue", { value: weight })}
                </Text>
                {hasUnsavedWeight &&
                  renderStatusTag(t("info3.pendingTag"), "pending")}
              </View>
              <View style={styles.formControlRow}>
                <View
                  style={[
                    styles.inputWrap,
                    hasUnsavedWeight && styles.inputWrapPending,
                  ]}
                >
                  <TextInput
                    value={pendingWeight}
                    onChangeText={setPendingWeight}
                    style={styles.inputTextCenter}
                    keyboardType="decimal-pad"
                    placeholderTextColor="#888"
                    editable={isFff3Ready}
                  />
                  <Text style={styles.unitText}>kg</Text>
                </View>

                <TouchableOpacity
                  style={[
                    styles.inlineSaveButton,
                    (!isFff3Ready || !hasUnsavedWeight) &&
                      styles.inlineSaveButtonDisabled,
                  ]}
                  onPress={onPressWeightSave}
                  disabled={!isFff3Ready || !hasUnsavedWeight}
                >
                  <Icon name="check" size={20} color="#fff" />
                </TouchableOpacity>
              </View>
            </View>
          </View>

          <View style={styles.formBlock}>
            <View style={styles.formRowInline}>
              <View style={styles.formLabelColumn}>
                <View style={styles.formLabelWrap}>
                  <Text style={styles.formLabel}>{t("info3.powerTrim")}</Text>
                  {renderHelp("trim")}
                </View>
                <Text style={styles.currentValueBelow}>
                  {t("info3.currentPowerTrimValue", { value: powerTrim })}
                </Text>
                {hasUnsavedPowerTrim &&
                  renderStatusTag(t("info3.pendingTag"), "pending")}
              </View>
              <View style={styles.formControlRow}>
                <View
                  style={[
                    styles.inputWrap,
                    hasUnsavedPowerTrim && styles.inputWrapPending,
                  ]}
                >
                  <TextInput
                    value={pendingPowerTrim}
                    onChangeText={setPendingPowerTrim}
                    style={styles.inputTextCenter}
                    keyboardType="numeric"
                    placeholderTextColor="#888"
                    editable={isFff3Ready}
                  />
                  <Text style={styles.unitText}>%</Text>
                </View>

                <TouchableOpacity
                  style={[
                    styles.inlineSaveButton,
                    (!isFff3Ready || !hasUnsavedPowerTrim) &&
                      styles.inlineSaveButtonDisabled,
                  ]}
                  onPress={onPressPowerTrimSave}
                  disabled={!isFff3Ready || !hasUnsavedPowerTrim}
                >
                  <Icon name="check" size={20} color="#fff" />
                </TouchableOpacity>
              </View>
            </View>
          </View>

          <View style={styles.formBlock}>
            <View style={styles.formRowInline}>
              <View style={styles.formLabelColumn}>
                <View style={styles.formLabelWrap}>
                  <Text style={styles.formLabel}>{t("info3.bikeType")}</Text>
                  {renderHelp("bikeType")}
                </View>
                <Text style={styles.currentValueBelow} numberOfLines={1}>
                  {t("info3.currentTextValue", { value: bikeType })}
                </Text>
                {isSettingBikeType &&
                  renderStatusTag(t("info3.settingTag"), "active")}
              </View>
              <View style={styles.formControlRow}>
                {renderDropdown(
                  "bikeType",
                  bikeType,
                  [
                    t("info3.bikeTypeFollow"),
                    t("info3.bikeTypeRoad"),
                    t("info3.bikeTypeMtb26"),
                    t("info3.bikeTypeMtb275"),
                    t("info3.bikeTypeMtb29"),
                    t("info3.bikeTypeSmallWheel"),
                  ],
                  async (value) => {
                    try {
                      setIsSettingBikeType(true);
                      await applyBikeType(value);
                      setBikeType(value);
                    } catch (err: any) {
                      Alert.alert(
                        t("common.notice"),
                        err?.message || t("info3.bikeTypeSetFailed")
                      );
                    } finally {
                      setIsSettingBikeType(false);
                    }
                  },
                  isInitializingSettings || isSettingBikeType,
                  isInitializingSettings || isSettingBikeType
                )}
              </View>
            </View>
          </View>

          <View style={styles.formBlockLast}>
            <View style={styles.formRowInline}>
              <View style={styles.formLabelColumn}>
                <View style={styles.formLabelWrap}>
                  <Text style={styles.formLabel}>{t("info3.ergSmooth")}</Text>
                  {renderHelp("ergSmooth")}
                </View>
                <Text style={styles.currentValueBelow} numberOfLines={1}>
                  {t("info3.currentTextValue", { value: ergSmooth })}
                </Text>
                {isSettingErgSmooth &&
                  renderStatusTag(t("info3.settingTag"), "active")}
              </View>
              <View style={styles.formControlRow}>
                {renderDropdown(
                  "ergSmooth",
                  ergSmooth,
                  [t("info3.ergOn"), t("info3.ergOff")],
                  async (value) => {
                    try {
                      setIsSettingErgSmooth(true);
                      await applyErgSmooth(value);
                      setErgSmooth(value);
                    } catch (err: any) {
                      Alert.alert(
                        t("common.notice"),
                        err?.message || t("info3.ergSmoothSetFailed")
                      );
                    } finally {
                      setIsSettingErgSmooth(false);
                    }
                  },
                  isInitializingSettings || isSettingErgSmooth,
                  isInitializingSettings || isSettingErgSmooth
                )}
              </View>
            </View>
          </View>

          <TouchableOpacity
            activeOpacity={0.85}
            style={styles.upgradeButton}
            onPress={goToDfu}
          >
            <Text style={styles.upgradeButtonText}>
              {t("info.firmwareUpgrade")}
            </Text>
          </TouchableOpacity>

          <TouchableOpacity
            activeOpacity={0.85}
            style={styles.rotateCancelButton}
            onPress={() => navigation.navigate("Spindown", { peripheral })}
          >
            <Text style={styles.upgradeButtonText}>{t("spindown.title")}</Text>
          </TouchableOpacity>
        </View>

        <Modal
          transparent
          visible={confirmModalVisible}
          animationType="fade"
          onRequestClose={closeConfirmModal}
        >
          <View style={styles.modalOverlay}>
            <View style={styles.confirmModalBox}>
              <Text style={styles.confirmModalText}>
                {pendingAction === "weight"
                  ? t("info3.confirmWeightChange", {
                      value: pendingWeight.trim(),
                    })
                  : t("info3.confirmPowerTrimChange", {
                      value: pendingPowerTrim.trim(),
                    })}
              </Text>

              <View style={styles.confirmButtonRow}>
                <TouchableOpacity
                  style={[styles.modalBtn, styles.modalBtnGhost]}
                  onPress={closeConfirmModal}
                >
                  <Text style={styles.modalBtnGhostText}>{t("common.no")}</Text>
                </TouchableOpacity>

                <TouchableOpacity
                  style={[styles.modalBtn, styles.modalBtnPrimary]}
                  onPress={() => startPendingActionFlow(pendingAction)}
                >
                  <Text style={styles.modalBtnPrimaryText}>
                    {t("common.yes")}
                  </Text>
                </TouchableOpacity>
              </View>
            </View>
          </View>
        </Modal>

        <Modal
          transparent
          visible={progressModalVisible}
          animationType="fade"
          onRequestClose={() => {}}
        >
          <View style={styles.modalOverlay}>
            <View style={styles.progressModalBox}>
              <ActivityIndicator size={28} color="#eb3b3b" />
              <Text style={styles.progressModalText}>{progressText}</Text>
            </View>
          </View>
        </Modal>

        <Modal
          transparent
          visible={openDropdown !== null}
          animationType="fade"
          onRequestClose={() => setOpenDropdown(null)}
        >
          <Pressable
            style={styles.modalOverlay}
            onPress={() => setOpenDropdown(null)}
          >
            <Pressable
              style={styles.dropdownModalBox}
              onPress={(e) => e.stopPropagation()}
            >
              <Text style={styles.dropdownModalTitle}>{dropdownTitle}</Text>

              <ScrollView
                style={styles.dropdownScroll}
                contentContainerStyle={styles.dropdownScrollContent}
                nestedScrollEnabled
                keyboardShouldPersistTaps="handled"
                showsVerticalScrollIndicator
              >
                {dropdownOptions.map((item) => (
                  <TouchableOpacity
                    key={item}
                    style={styles.dropdownModalItem}
                    onPress={async () => {
                      const handler = dropdownOnSelect;
                      setOpenDropdown(null);
                      if (handler) {
                        await handler(item);
                      }
                    }}
                  >
                    <Text style={styles.dropdownModalItemText}>{item}</Text>
                  </TouchableOpacity>
                ))}
              </ScrollView>
            </Pressable>
          </Pressable>
        </Modal>

        {isLoading && (
          <View style={styles.loadingWrap}>
            <ActivityIndicator size={30} color="#eb3b3b" />
            <Text style={styles.loadingText}>{t("info.readingInfo")}</Text>
          </View>
        )}
      </ScrollView>
    </Pressable>
  );
}

const RED = "#eb3b3b";
const BG = "#ffffff";
const DARK = "#333333";
const ORANGE = "#f59e0b";
const ORANGE_BG = "#fff7e6";

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    backgroundColor: BG,
  },
  scrollContent: {
    paddingHorizontal: 18,
    paddingBottom: 28,
    backgroundColor: BG,
  },
  card: {
    backgroundColor: "#ffffff",
    marginTop: 8,
  },
  infoRow: {
    flexDirection: "row",
    alignItems: "center",
    borderBottomWidth: 1.5,
    borderBottomColor: RED,
    paddingVertical: 8,
  },
  infoLabel: {
    fontSize: 16,
    color: DARK,
    fontWeight: "500",
    minWidth: 92,
  },
  infoValue: {
    flex: 1,
    fontSize: 16,
    color: "#4a4a4a",
    fontWeight: "500",
  },
  metricsWrap: {
    flexDirection: "row",
    justifyContent: "center",
    gap: 12,
    paddingVertical: 12,
    borderBottomWidth: 1.5,
    borderBottomColor: RED,
    marginBottom: 8,
  },
  metricCard: {
    width: 104,
    height: 104,
    borderRadius: 20,
    borderWidth: 2,
    borderColor: RED,
    alignItems: "center",
    justifyContent: "center",
    backgroundColor: "#ffffff",
  },
  metricLabel: {
    fontSize: 14,
    color: DARK,
    marginTop: 6,
  },
  metricValue: {
    fontSize: 24,
    fontWeight: "700",
    color: DARK,
    marginTop: 6,
  },
  formBlock: {
    borderBottomWidth: 1.5,
    borderBottomColor: RED,
    paddingVertical: 8,
  },
  formBlockLast: {
    borderBottomWidth: 1.5,
    borderBottomColor: RED,
    paddingVertical: 8,
  },
  formRowInline: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    gap: 10,
  },
  formLabelColumn: {
    width: 118,
    justifyContent: "center",
  },
  formLabelWrap: {
    flexDirection: "row",
    alignItems: "center",
  },
  formLabel: {
    fontSize: 15,
    color: DARK,
    marginRight: 6,
  },
  formControlRow: {
    width: 146,
    flexDirection: "row",
    alignItems: "center",
    gap: 6,
    justifyContent: "flex-start",
  },
  currentValueBelow: {
    marginTop: 4,
    fontSize: 13,
    color: "#777",
    fontWeight: "500",
  },
  statusTag: {
    marginTop: 4,
    paddingHorizontal: 8,
    paddingVertical: 2,
    borderRadius: 999,
  },
  statusTagPending: {
    backgroundColor: ORANGE_BG,
  },
  statusTagActive: {
    backgroundColor: "#ffe9e9",
  },
  statusTagText: {
    fontSize: 12,
    fontWeight: "700",
  },
  statusTagPendingText: {
    color: ORANGE,
  },
  statusTagActiveText: {
    color: RED,
  },
  helpWrap: {
    position: "relative",
  },
  helpDot: {
    width: 24,
    height: 24,
    borderRadius: 12,
    backgroundColor: RED,
    alignItems: "center",
    justifyContent: "center",
  },
  helpDotText: {
    color: "white",
    fontSize: 16,
    fontWeight: "700",
    lineHeight: 18,
  },
  tooltipBubble: {
    position: "absolute",
    left: -8,
    bottom: 32,
    backgroundColor: "rgba(0,0,0,0.82)",
    paddingHorizontal: 10,
    paddingVertical: 7,
    borderRadius: 8,
    minWidth: 110,
    zIndex: 99999,
    elevation: 99999,
  },
  tooltipText: {
    color: "#ffffff",
    fontSize: 12,
    lineHeight: 16,
  },
  inputWrapWithButton: {
    width: "100%",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    gap: 8,
  },
  inputWrap: {
    width: 106,
    height: 36,
    borderWidth: 1.5,
    borderColor: "#666",
    backgroundColor: "#ffffff",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    paddingHorizontal: 10,
  },
  inputWrapPending: {
    borderColor: ORANGE,
    backgroundColor: ORANGE_BG,
  },
  inputTextCenter: {
    flex: 1,
    fontSize: 16,
    color: DARK,
    textAlign: "center",
    fontWeight: "700",
    paddingVertical: 0,
  },
  unitText: {
    fontSize: 14,
    color: "#666",
    marginLeft: 6,
  },
  inlineSaveButton: {
    width: 34,
    height: 34,
    borderRadius: 8,
    backgroundColor: RED,
    alignItems: "center",
    justifyContent: "center",
  },
  inlineSaveButtonDisabled: {
    backgroundColor: "#c9c9c9",
  },
  dropdownWrap: {
    width: 146,
  },
  selectBox: {
    width: 146,
    height: 36,
    borderWidth: 1.5,
    borderColor: "#666",
    backgroundColor: "#ffffff",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    paddingHorizontal: 10,
  },
  selectBoxDisabled: {
    backgroundColor: "#f6f6f6",
    borderColor: "#cfcfcf",
  },
  selectText: {
    flex: 1,
    fontSize: 15,
    color: DARK,
    fontWeight: "600",
    marginRight: 8,
  },
  selectTextDisabled: {
    color: "#999",
  },
  upgradeButton: {
    marginTop: 16,
    height: 44,
    borderRadius: 8,
    backgroundColor: RED,
    alignItems: "center",
    justifyContent: "center",
  },
  rotateCancelButton: {
    marginTop: 10,
    height: 44,
    borderRadius: 8,
    backgroundColor: RED,
    alignItems: "center",
    justifyContent: "center",
  },
  upgradeButtonText: {
    color: "white",
    fontSize: 20,
    fontWeight: "700",
  },
  loadingWrap: {
    alignItems: "center",
    justifyContent: "center",
    marginTop: 24,
  },
  loadingText: {
    marginTop: 10,
    fontSize: 15,
    color: DARK,
  },
  modalOverlay: {
    flex: 1,
    backgroundColor: "rgba(0,0,0,0.35)",
    alignItems: "center",
    justifyContent: "center",
    paddingHorizontal: 28,
  },
  confirmModalBox: {
    width: "100%",
    backgroundColor: "#ffffff",
    borderRadius: 14,
    paddingHorizontal: 18,
    paddingTop: 22,
    paddingBottom: 16,
  },
  confirmModalText: {
    fontSize: 17,
    color: DARK,
    textAlign: "center",
    lineHeight: 24,
    fontWeight: "500",
  },
  confirmButtonRow: {
    flexDirection: "row",
    justifyContent: "space-between",
    marginTop: 20,
    gap: 12,
  },
  modalBtn: {
    flex: 1,
    height: 42,
    borderRadius: 8,
    alignItems: "center",
    justifyContent: "center",
  },
  modalBtnGhost: {
    backgroundColor: "#f2f2f2",
  },
  modalBtnPrimary: {
    backgroundColor: RED,
  },
  modalBtnGhostText: {
    color: DARK,
    fontSize: 16,
    fontWeight: "600",
  },
  modalBtnPrimaryText: {
    color: "#ffffff",
    fontSize: 16,
    fontWeight: "600",
  },
  progressModalBox: {
    width: 180,
    backgroundColor: "#ffffff",
    borderRadius: 14,
    paddingVertical: 22,
    paddingHorizontal: 18,
    alignItems: "center",
    justifyContent: "center",
  },
  progressModalText: {
    marginTop: 12,
    fontSize: 16,
    color: DARK,
    fontWeight: "500",
    textAlign: "center",
  },
  dropdownModalBox: {
    width: "86%",
    maxHeight: "60%",
    backgroundColor: "#fff",
    borderRadius: 14,
    paddingTop: 16,
    paddingBottom: 10,
    paddingHorizontal: 14,
  },
  dropdownModalTitle: {
    fontSize: 17,
    color: DARK,
    fontWeight: "700",
    textAlign: "center",
    marginBottom: 12,
  },
  dropdownScroll: {
    maxHeight: 320,
  },
  dropdownScrollContent: {
    paddingBottom: 6,
  },
  dropdownModalItem: {
    minHeight: 46,
    borderBottomWidth: 1,
    borderBottomColor: "#ececec",
    justifyContent: "center",
    paddingHorizontal: 10,
    paddingVertical: 10,
  },
  dropdownModalItemText: {
    fontSize: 16,
    color: DARK,
  },
});
