"use client";

import { useCallback, useEffect, useMemo, useState } from "react";

import {
  userManagementService,
  type ApiRole,
  type AdminUserStatus,
  type ApiUser,
  type CreateUserPayload,
  type LoginTrail,
  type UpdateUserPayload,
} from "@/features/admin/user-management/service/user-management-service";

export type UserRow = {
  id: string;
  name: string;
  email: string;
  roles: ApiRole[];
  position: string;
  status: AdminUserStatus;
  avatar?: string;
  isActive?: boolean;
  deletedAt?: string | null;
  createdAt?: string;
  updatedAt?: string;
  lastLoginDevice?: string | null;
  lastLoginDeviceType?: string | null;
  lastLoginIp?: string | null;
  lastLoginAt?: string;
};

const API_URL =
  process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001/api/v1";

function getBackendAssetUrl(path: string) {
  const backendUrl = API_URL.replace(/\/api(?:\/v\d+)?\/?$/, "");

  return `${backendUrl}${path}`;
}

function normalizeAvatar(value?: string | null) {
  if (!value) return undefined;

  if (value.startsWith("/uploads/")) {
    return getBackendAssetUrl(value);
  }

  return value;
}

// Fallback parser for OLD sessions that were saved before the backend started
// parsing the device. Turns a raw User-Agent into a short "Browser on OS" label.
// Order matters: Edge/Opera UAs also contain "Chrome", and Chrome contains "Safari".
function parseUserAgentFallback(userAgent?: string | null): string | null {
  if (!userAgent) return null;

  let browser = "Unknown browser";
  if (/edg/i.test(userAgent)) browser = "Edge";
  else if (/opr|opera/i.test(userAgent)) browser = "Opera";
  else if (/chrome|crios/i.test(userAgent)) browser = "Chrome";
  else if (/firefox|fxios/i.test(userAgent)) browser = "Firefox";
  else if (/safari/i.test(userAgent)) browser = "Safari";

  let os = "";
  if (/windows/i.test(userAgent)) os = "Windows";
  else if (/iphone|ipad|ios/i.test(userAgent)) os = "iOS";
  else if (/mac os x|macintosh/i.test(userAgent)) os = "macOS";
  else if (/android/i.test(userAgent)) os = "Android";
  else if (/linux/i.test(userAgent)) os = "Linux";

  return os ? `${browser} on ${os}` : browser;
}

// Build the "Browser on OS" label shown in the table's Log Device column.
// Newer sessions already have browser/os parsed on the backend; if those are
// missing (older session) we fall back to parsing the raw User-Agent here.
function buildDeviceLabel(lastLogin: ApiUser["lastLogin"]): string | null {
  if (!lastLogin) return null;

  const deviceParts: string[] = [];
  if (lastLogin.browser) deviceParts.push(lastLogin.browser);
  if (lastLogin.os) deviceParts.push(lastLogin.os);

  if (deviceParts.length > 0) {
    return deviceParts.join(" on ");
  }

  return parseUserAgentFallback(lastLogin.userAgent);
}

function normalizeStatus(user: ApiUser): AdminUserStatus {
  if (user.isActive === false) {
    return "Inactive";
  }

  if (user.status?.toLowerCase() === "inactive") {
    return "Inactive";
  }

  return "Active";
}

function normalizeUser(user: ApiUser): UserRow {
  const status = normalizeStatus(user);
  const lastLogin = user.lastLogin ?? null;

  return {
    id: String(user.id),
    name: user.name ?? "",
    email: user.email ?? "",
    roles: (user.roles ?? []).map((role) => ({
      id: Number(role.id),
      name: role.name,
    })),
    position: user.position ?? "",
    status,
    avatar: normalizeAvatar(user.avatar ?? user.avatarUrl ?? user.profileImage),
    isActive: status === "Active",
    deletedAt: user.deletedAt ?? null,
    createdAt: user.createdAt,
    updatedAt: user.updatedAt,
    lastLoginDevice: buildDeviceLabel(lastLogin),
    lastLoginDeviceType: lastLogin?.deviceType ?? null,
    lastLoginIp: lastLogin?.ipAddress ?? null,
    lastLoginAt: lastLogin?.createdAt,
  };
}

export function useUserManagement() {
  const [users, setUsers] = useState<UserRow[]>([]);
  const [availableRoles, setAvailableRoles] = useState<ApiRole[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState("");

  const [loginTrails, setLoginTrails] = useState<LoginTrail[]>([]);
  const [loginTrailsUser, setLoginTrailsUser] = useState<UserRow | null>(null);
  const [loadingLoginTrails, setLoadingLoginTrails] = useState(false);

  const loadUsers = useCallback(async () => {
    setLoading(true);
    setError("");

    try {
      const response = await userManagementService.getUsers();
      setUsers(response.map(normalizeUser));
    } catch (requestError) {
      setError(
        requestError instanceof Error
          ? requestError.message
          : "Cannot load users.",
      );
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    let ignore = false;

    userManagementService
      .getUsers()
      .then((response) => {
        if (!ignore) {
          setUsers(response.map(normalizeUser));
          setError("");
        }
      })
      .catch((requestError) => {
        if (!ignore) {
          setError(
            requestError instanceof Error
              ? requestError.message
              : "Cannot load users.",
          );
        }
      })
      .finally(() => {
        if (!ignore) setLoading(false);
      });

    return () => {
      ignore = true;
    };
  }, []);

  useEffect(() => {
    let ignore = false;

    userManagementService
      .getRoles()
      .then((roles) => {
        if (!ignore) setAvailableRoles(roles);
      })
      .catch(() => undefined);

    return () => {
      ignore = true;
    };
  }, []);

  const createUser = useCallback(
    async (payload: CreateUserPayload) => {
      setSaving(true);
      setError("");

      try {
        await userManagementService.createUser(payload);
        await loadUsers();
      } catch (requestError) {
        const message =
          requestError instanceof Error
            ? requestError.message
            : "Cannot create user.";

        setError(message);
        throw new Error(message);
      } finally {
        setSaving(false);
      }
    },
    [loadUsers],
  );

  const updateUser = useCallback(
    async (id: string | number, payload: UpdateUserPayload) => {
      setSaving(true);
      setError("");

      const userId = String(id);

      try {
        const response = await userManagementService.updateUser(id, payload);
        const normalizedResponse = normalizeUser(response);

        setUsers((previousUsers) =>
          previousUsers.map((user) => {
            if (user.id !== userId) return user;

            const nextStatus =
              payload.isActive === false
                ? "Inactive"
                : payload.isActive === true
                  ? "Active"
                  : normalizedResponse.status;

            return {
              ...user,
              ...normalizedResponse,
              id: user.id,
              status: nextStatus,
              isActive: nextStatus === "Active",
              deletedAt: null,
            };
          }),
        );
      } catch (requestError) {
        const message =
          requestError instanceof Error
            ? requestError.message
            : "Cannot update user.";

        setError(message);
        throw new Error(message);
      } finally {
        setSaving(false);
      }
    },
    [],
  );

  const activateUser = useCallback(async (id: string | number) => {
    setSaving(true);
    setError("");

    const userId = String(id);

    try {
      await userManagementService.activateUser(id);

      setUsers((previousUsers) =>
        previousUsers.map((user) =>
          user.id === userId
            ? {
                ...user,
                status: "Active",
                isActive: true,
                deletedAt: null,
              }
            : user,
        ),
      );
    } catch (requestError) {
      const message =
        requestError instanceof Error
          ? requestError.message
          : "Cannot activate user.";

      setError(message);
      throw new Error(message);
    } finally {
      setSaving(false);
    }
  }, []);

  const deactivateUser = useCallback(async (id: string | number) => {
    setSaving(true);
    setError("");

    const userId = String(id);

    try {
      await userManagementService.deactivateUser(id);

      setUsers((previousUsers) =>
        previousUsers.map((user) =>
          user.id === userId
            ? {
                ...user,
                status: "Inactive",
                isActive: false,
                deletedAt: null,
              }
            : user,
        ),
      );
    } catch (requestError) {
      const message =
        requestError instanceof Error
          ? requestError.message
          : "Cannot deactivate user.";

      setError(message);
      throw new Error(message);
    } finally {
      setSaving(false);
    }
  }, []);

  const deleteUser = useCallback(async (id: string | number) => {
    setSaving(true);
    setError("");

    const deletedId = String(id);

    try {
      await userManagementService.deleteUser(id);

      setUsers((previousUsers) =>
        previousUsers.filter((user) => user.id !== deletedId),
      );
    } catch (requestError) {
      const message =
        requestError instanceof Error
          ? requestError.message
          : "Cannot delete user.";

      setError(message);
      throw new Error(message);
    } finally {
      setSaving(false);
    }
  }, []);

  const openLoginTrails = useCallback(async (user: UserRow) => {
    setLoadingLoginTrails(true);
    setError("");

    try {
      const response = await userManagementService.getUserLoginTrails(user.id);
      setLoginTrails(response);
      setLoginTrailsUser(user);
    } catch (requestError) {
      const message =
        requestError instanceof Error
          ? requestError.message
          : "Cannot load login trails.";

      setError(message);
      throw new Error(message);
    } finally {
      setLoadingLoginTrails(false);
    }
  }, []);

  const closeLoginTrails = useCallback(() => {
    setLoginTrails([]);
    setLoginTrailsUser(null);
  }, []);

  return useMemo(
    () => ({
      users,
      availableRoles,
      loading,
      saving,
      error,
      setError,
      loadUsers,
      createUser,
      updateUser,
      activateUser,
      deactivateUser,
      deleteUser,
      loginTrails,
      loginTrailsUser,
      loadingLoginTrails,
      openLoginTrails,
      closeLoginTrails,
    }),
    [
      users,
      availableRoles,
      loading,
      saving,
      error,
      loadUsers,
      createUser,
      updateUser,
      activateUser,
      deactivateUser,
      deleteUser,
      loginTrails,
      loginTrailsUser,
      loadingLoginTrails,
      openLoginTrails,
      closeLoginTrails,
    ],
  );
}