import { useCallback, useState } from 'react';

interface ApiTokenRecord {
  id?: string | number;
  label?: string;
  expires_at?: string;
  created_at?: string;
  last_used_at?: string;
  capability_set_id?: string | number;
  [key: string]: unknown;
}

interface ApiTokensResponse {
  token_info?: ApiTokenRecord;
  token?: string;
}

interface CreateTokenArgs {
  label?: string;
  expires_at?: string;
  capability_set_id?: string | number;
}

interface UseApiTokensArgs {
  api: {
    get: <T = unknown>(url: string) => Promise<{ data: T }>;
    post: <T = unknown>(url: string, payload?: unknown) => Promise<{ data: T }>;
    delete: (url: string) => Promise<unknown>;
  };
  notifyApiError?: (error: unknown, message: string) => void;
  setStatusMessage?: (message: string, variant?: string) => void;
  token?: string | null;
}

interface UseApiTokensResult {
  tokens: ApiTokenRecord[];
  loading: boolean;
  creating: boolean;
  deletingId: string | number | null;
  regeneratingId: string | number | null;
  createdSecret: string | null;
  refresh: () => Promise<void>;
  create: (args?: CreateTokenArgs) => Promise<ApiTokensResponse | false>;
  revoke: (tokenId?: string | number | null) => Promise<boolean>;
  regenerate: (tokenId?: string | number | null) => Promise<boolean>;
  dismissSecret: () => void;
}

const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTokensArgs): UseApiTokensResult => {
  const [tokens, setTokens] = useState<ApiTokenRecord[]>([]);
  const [loading, setLoading] = useState(false);
  const [creating, setCreating] = useState(false);
  const [deletingId, setDeletingId] = useState<string | number | null>(null);
  const [regeneratingId, setRegeneratingId] = useState<string | number | null>(null);
  const [createdSecret, setCreatedSecret] = useState<string | null>(null);

  const refresh = useCallback(async () => {
    if (!token) {
      return;
    }
    setLoading(true);
    try {
      const { data } = await api.get<ApiTokenRecord[]>('/profile/api-tokens');
      setTokens(Array.isArray(data) ? data : []);
    } catch (error) {
      notifyApiError?.(error, 'Failed to load API tokens.');
    } finally {
      setLoading(false);
    }
  }, [api, notifyApiError, token]);

  const create = useCallback(
    async ({ label, expires_at, capability_set_id }: CreateTokenArgs = {}) => {
      if (creating || !capability_set_id) {
        return false;
      }
      setCreating(true);
      try {
        const payload: Record<string, unknown> = { capability_set_id };
        if (label) {
          payload.label = label;
        }
        if (expires_at) {
          payload.expires_at = expires_at;
        }

        const { data } = await api.post<ApiTokensResponse>('/profile/api-tokens', payload);
        if (data?.token_info) {
          setTokens((previous) => {
            const filtered = previous.filter((entry) => entry.id !== data.token_info?.id);
            return data.token_info ? [data.token_info, ...filtered] : filtered;
          });
        } else {
          await refresh();
        }

        if (data?.token) {
          setCreatedSecret(data.token);
        }

        setStatusMessage?.('API token created.', 'success');
        return data;
      } catch (error) {
        notifyApiError?.(error, 'Failed to create API token.');
        return false;
      } finally {
        setCreating(false);
      }
    },
    [api, creating, notifyApiError, refresh, setStatusMessage],
  );

  const revoke = useCallback(
    async (tokenId?: string | number | null) => {
      if (!tokenId) {
        return false;
      }
      setDeletingId(tokenId);
      try {
        await api.delete(`/profile/api-tokens/${tokenId}`);
        await refresh();
        setStatusMessage?.('API token revoked.', 'success');
        return true;
      } catch (error) {
        notifyApiError?.(error, 'Failed to revoke API token.');
        return false;
      } finally {
        setDeletingId(null);
      }
    },
    [api, notifyApiError, refresh, setStatusMessage],
  );

  const regenerate = useCallback(
    async (tokenId?: string | number | null) => {
      if (!tokenId) {
        return false;
      }
      setRegeneratingId(tokenId);
      try {
        const { data } = await api.post<ApiTokensResponse>(`/profile/api-tokens/${tokenId}/regenerate`);
        if (data?.token_info) {
          setTokens((previous) => {
            let found = false;
            const next = previous.map((entry) => {
              if (entry.id === data.token_info?.id) {
                found = true;
                return data.token_info;
              }
              return entry;
            });
            if (!found && data.token_info) {
              return [data.token_info, ...previous];
            }
            return next;
          });
        } else {
          await refresh();
        }

        if (data?.token) {
          setCreatedSecret(data.token);
        }

        setStatusMessage?.('API token regenerated.', 'success');
        return true;
      } catch (error) {
        notifyApiError?.(error, 'Failed to regenerate API token.');
        return false;
      } finally {
        setRegeneratingId(null);
      }
    },
    [api, notifyApiError, refresh, setStatusMessage],
  );

  const dismissSecret = useCallback(() => {
    setCreatedSecret(null);
  }, []);

  return {
    tokens,
    loading,
    creating,
    deletingId,
    regeneratingId,
    createdSecret,
    refresh,
    create,
    revoke,
    regenerate,
    dismissSecret,
  };
};

export default useApiTokens;
