# Copyright 2026 The etils Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Register path."""

from __future__ import annotations

import os
import typing
from typing import Callable, TypeVar

from etils.epath import abstract_path
from etils.epath import gpath
from etils.epath.typing import PathLike  # pylint: disable=g-multiple-import,g-importing-member

_T = TypeVar('_T')

# Classes and uri are registered in `gpath.py`
_PATHLIKE_CLS: tuple[type[abstract_path.Path], ...] = ()
_URI_PREFIXES_TO_CLS: dict[str, type[abstract_path.Path]] = {}


@typing.overload
def register_path_cls(
    path_cls_or_uri_prefix: str | list[str] | tuple[str, ...]
) -> Callable[[_T], _T]:
  ...


@typing.overload
def register_path_cls(path_cls_or_uri_prefix: _T) -> _T:
  ...


def register_path_cls(path_cls_or_uri_prefix):
  """Register the pathlib-like class.

  ```python
  @epath.register_path_cls('my_path://')
  class MyPath(pathlib.PurePosixPath):
    ...

  my_path = epath.Path('my_path://some-path')
  assert isinstance(my_path, MyPath)
  ```

  Args:
    path_cls_or_uri_prefix: If a uri prefix is given, then passing calling
      `tfds.core.as_path('prefix://path')` will call the decorated class.

  Returns:
    The decorator or decoratorated class
  """
  global _PATHLIKE_CLS
  if isinstance(path_cls_or_uri_prefix, (str, list, tuple)):

    def register_path_cls_decorator(cls: _T) -> _T:
      if isinstance(path_cls_or_uri_prefix, str):
        _URI_PREFIXES_TO_CLS[path_cls_or_uri_prefix] = cls
      elif isinstance(path_cls_or_uri_prefix, (list, tuple)):
        for uri_prefix in path_cls_or_uri_prefix:
          _URI_PREFIXES_TO_CLS[uri_prefix] = cls
      return register_path_cls(cls)

    return register_path_cls_decorator
  else:
    _PATHLIKE_CLS = _PATHLIKE_CLS + (path_cls_or_uri_prefix,)
    return path_cls_or_uri_prefix


def make_path(path: PathLike) -> abstract_path.Path:
  """Create a generic `pathlib.Path`-like abstraction.

  Depending on the input (e.g. `gs://`, `github://`, `ResourcePath`,...), the
  system (Windows, Linux,...), the function will create the right pathlib-like
  abstraction.

  Args:
    path: Pathlike object.

  Returns:
    path: The `pathlib.Path`-like abstraction.
  """
  is_windows = os.name == 'nt'
  if isinstance(path, str):
    uri_splits = path.split('://', maxsplit=1)
    if len(uri_splits) > 1:  # str is URI (e.g. `gs://`, `github://`,...)
      # On windows, `PosixGPath` is created for `gs://` paths
      return _URI_PREFIXES_TO_CLS[uri_splits[0] + '://'](path)  # pytype: disable=bad-return-type
    elif is_windows:
      return gpath.WindowsGPath(path)
    else:
      return gpath.PosixGPath(path)
  elif isinstance(path, _PATHLIKE_CLS):
    return path  # Forward resource path, gpath,... as-is  # pytype: disable=bad-return-type
  elif isinstance(path, os.PathLike):  # Other `os.fspath` compatible objects
    path_cls = gpath.WindowsGPath if is_windows else gpath.PosixGPath
    return path_cls(path)
  else:
    raise TypeError(f'Invalid path type: {path!r}')
