"""Flask extension for the WooCommerce REST API."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import msgspec
from woocommerce import (  # type: ignore[import-untyped]
    API as WooCommerceAPIClient,  # noqa: N811
)

from flask_woocommerce import exceptions
from flask_woocommerce.structs import Config

if TYPE_CHECKING:
    from flask import Flask

__all__ = ("WooCommerce",)

logger = logging.getLogger(__name__)


class WooCommerce:
    """WooCommerce API client integrated as a Flask extension.

    Reads its configuration from ``app.config["WOOCOMMERCE"]`` and validates
    it against :class:`~flask_woocommerce.structs.Config`.  Supports both
    eager and deferred initialisation via the standard Flask extension pattern.

    A single flat dict is treated as one client stored under ``"default"``.
    A dict of dicts creates multiple named clients.
    """

    def __init__(self, app: Flask | None = None) -> None:
        """Create the extension, optionally binding it to *app*.

        Args:
            app: Optional Flask application.  If provided, :meth:`init_app` is
                called immediately.

        """
        self.clients: dict[str, WooCommerceAPIClient] = {}
        if app is None:
            return
        self.init_app(app)

    def init_app(self, app: Flask) -> None:
        """Initialise the extension with a Flask application.

        Reads the ``WOOCOMMERCE`` key from *app*'s config, validates it, and
        creates the underlying API client(s).

        If the config value is a flat dict containing a ``"url"`` key, a single
        client is created under the name ``"default"``.  Otherwise the value is
        treated as a mapping of *name -> config-dict* and one client is created
        per entry.

        Args:
            app: The Flask application to bind to.

        Raises:
            ConfigError: If the configuration is missing or invalid.

        """
        logger.debug("Initializing Flask-WooCommerce extension")

        raw_config = app.config.get("WOOCOMMERCE")
        if not isinstance(raw_config, dict):
            msg = "Missing or invalid WOOCOMMERCE configuration."
            logger.error(msg)
            raise exceptions.ConfigError(msg)

        if "url" in raw_config:
            logger.debug("Creating single WooCommerce client (default)")
            self.clients["default"] = self._make_client(raw_config)
        else:
            logger.debug(
                "Creating multiple WooCommerce clients: %s",
                list(raw_config.keys()),
            )
            for name, client_config in raw_config.items():
                logger.debug("Creating client: %s", name)
                self.clients[name] = self._make_client(client_config)

        logger.info(
            "Flask-WooCommerce initialized with %d client(s)",
            len(self.clients),
        )
        app.extensions["woocommerce"] = self

    @property
    def client(self) -> WooCommerceAPIClient:
        """The default WooCommerce API client.

        Returns:
            The client stored under the ``"default"`` key.

        Raises:
            ConfigError: If no default client has been registered.

        """
        try:
            return self.clients["default"]
        except KeyError:
            msg = (
                "No default client configured. "
                'Use wc.clients["<name>"] for named clients.'
            )
            raise exceptions.ConfigError(msg) from None

    @staticmethod
    def _make_client(raw: object) -> WooCommerceAPIClient:
        """Validate a raw config dict and return an API client.

        Args:
            raw: A dict to validate against :class:`Config`.

        Returns:
            A configured :class:`WooCommerceAPIClient` instance.

        Raises:
            ConfigError: If validation fails.

        """
        try:
            config = msgspec.convert(raw, Config)
            logger.debug("Configuration validated successfully")
        except msgspec.ValidationError as validation_error:
            logger.exception("Configuration validation failed")
            msg = f"Invalid WooCommerce configuration: {validation_error}"
            raise exceptions.ConfigError(msg) from validation_error
        client_config = {
            k: v
            for k, v in msgspec.structs.asdict(config).items()
            if v is not msgspec.UNSET
        }
        client = WooCommerceAPIClient(**client_config)
        logger.info("Client created with config: %s", {
            k: "<redacted>" if k in ("consumer_key", "consumer_secret") else v
            for k, v in client_config.items()
        })  # fmt: skip
        return client
