import importlib
from typing import Optional, Any
from .parquet import ParquetReader


class BaseReader(ParquetReader):
    """
    Base reader for Data Objects that supports lazy configuration resolution.

    This class avoids import-time side effects by resolving the filesystem and
    storage paths at runtime (initialization) using declarative configuration keys.

    Class Attributes:
        fs_name (str): The name of the filesystem to retrieve via `get_fs_instance`.
                       Defaults to "etl_source".
        storage_variable (Optional[str]): The variable name in the configuration module
                                          that holds the base path.
        storage_subpath (Optional[str]): The sub-path to append to the base path.
        storage_module_path (str): The python path to the configuration module.
                                   Defaults to "solutions.conf.storage".
    """

    # Declarative Defaults (Modernized)
    fs_name: str = "source"
    storage_variable: Optional[str] = None
    storage_subpath: Optional[str] = None
    storage_module_path: Optional[str] = None

    def __init__(
        self,
        parquet_start_date: Optional[str] = None,
        parquet_end_date: Optional[str] = None,
        **kwargs: Any,
    ) -> None:
        # 1. Resolve Configuration (Class attributes + kwargs overrides)
        fs_name = kwargs.pop("fs_name", self.fs_name)
        storage_variable = kwargs.pop("storage_variable", self.storage_variable)
        storage_subpath = kwargs.pop("storage_subpath", self.storage_subpath)
        storage_module_path = kwargs.pop("storage_module_path", self.storage_module_path)

        # 2. Lazy Discovery of Storage Configuration Module
        storage = None
        if storage_module_path:
            try:
                storage = importlib.import_module(storage_module_path)
            except ImportError:
                print(f"[warning] Explicit storage_module_path '{storage_module_path}' not found.")
        else:
            # Automatic Probing of common patterns
            # Priority: conf.storage (Standard) -> solutions.conf.storage (Legacy)
            for cand in ["conf.storage", "solutions.conf.storage"]:
                try:
                    storage = importlib.import_module(cand)
                    storage_module_path = cand
                    break
                except ImportError:
                    continue

        # 3. Resolve Filesystem
        if "fs" in kwargs:
            kw_fs = kwargs.pop("fs")
        elif storage and hasattr(storage, "get_fs_instance"):
            try:
                kw_fs = storage.get_fs_instance(fs_name)
            except Exception as e:
                # If specific name fails, try falling back to matching short IDs
                if fs_name == "source" or fs_name == "etl_source":
                    # Projects often register 'etl', 'source', or 'etl_source'
                    for alt in ["etl", "source", "etl_source"]:
                        try:
                            kw_fs = storage.get_fs_instance(alt)
                            break
                        except Exception:
                            continue
                    else:
                        kw_fs = None
                        print(f"[warning] Could not resolve filesystem '{fs_name}' from {storage_module_path}: {e}")
                else:
                    kw_fs = None
                    print(f"[warning] Could not resolve filesystem '{fs_name}' from {storage_module_path}: {e}")
        else:
            kw_fs = None

        # 4. Resolve Storage Path
        # Only resolve if not explicitly provided in kwargs (ParquetReader arg)
        if "parquet_storage_path" not in kwargs and storage:
            if storage_variable:
                try:
                    base_path = getattr(storage, storage_variable)
                    if storage_subpath:
                        kwargs["parquet_storage_path"] = f"{base_path}/{storage_subpath}"
                    else:
                        kwargs["parquet_storage_path"] = str(base_path)
                except AttributeError:
                    print(f"[warning] Storage variable '{storage_variable}' not found in '{storage_module_path}'")

        # 5. Initialize Parent
        super().__init__(
            parquet_start_date=parquet_start_date,
            parquet_end_date=parquet_end_date,
            fs=kw_fs,
            **kwargs,
        )

