"""Table grouping strategies for ERP systems."""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Dict, List, Optional


# Built-in prefix maps for common ERP systems
BUILTIN_MAPS: Dict[str, Dict[str, str]] = {
    "enertia": {
        "AA": "Accounts / AP",
        "GL": "General Ledger",
        "RV": "Revenue",
        "JB": "Joint Interest Billing",
        "PD": "Production / Decline",
        "AE": "AFE / Capital",
        "FB": "Fixed Assets / Budget",
        "LC": "Land / Contracts",
        "TX": "Tax",
        "PR": "Payroll",
    },
    "sap": {
        "BKPF": "FI Document Headers",
        "BSEG": "FI Document Line Items",
        "BSIK": "FI Vendor Open Items",
        "BSID": "FI Customer Open Items",
        "BSAK": "FI Vendor Cleared Items",
        "BSAD": "FI Customer Cleared Items",
        "MARA": "MM Material Master",
        "MARC": "MM Plant Data",
        "MARD": "MM Storage Location",
        "MAKT": "MM Material Descriptions",
        "EKKO": "MM Purchase Order Header",
        "EKPO": "MM Purchase Order Item",
        "VBAK": "SD Sales Order Header",
        "VBAP": "SD Sales Order Item",
        "VBRK": "SD Billing Header",
        "VBRP": "SD Billing Item",
        "LIKP": "SD Delivery Header",
        "LIPS": "SD Delivery Item",
        "KNA1": "FI Customer Master",
        "KNB1": "FI Customer Company",
        "LFA1": "FI Vendor Master",
        "LFB1": "FI Vendor Company",
        "CSKS": "CO Cost Center Master",
        "CSKA": "CO Cost Element",
        "ANLC": "FI Asset Values",
        "ANLA": "FI Asset Master",
        "MSEG": "MM Material Movements",
        "COEP": "CO Line Items",
        "ACDOCA": "FI Universal Journal",
        "T001": "FI Company Codes",
    },
    "oracle_ebs": {
        "AP": "Accounts Payable",
        "AR": "Accounts Receivable",
        "GL": "General Ledger",
        "FA": "Fixed Assets",
        "PO": "Purchasing",
        "INV": "Inventory",
        "OM": "Order Management",
        "PA": "Projects",
    },
    "wolfepak": {
        "WP_": "WolfePak Core",
        "WPK_": "WolfePak Core",
        "WOLF_": "WolfePak Core",
        "REV_": "Revenue",
        "PROD_": "Production",
        "JIB_": "Joint Interest Billing",
        "AFE_": "AFE / Capital",
        "GL_": "General Ledger",
        "AP_": "Accounts Payable",
        "AR_": "Accounts Receivable",
    },
    "netsuite": {
        "TRANSACTION": "Transactions",
        "ACCOUNT": "Chart of Accounts",
        "CUSTOMER": "Customers",
        "VENDOR": "Vendors",
        "ITEM": "Items / Inventory",
        "DEPARTMENT": "Departments",
        "LOCATION": "Locations",
        "SUBSIDIARY": "Subsidiaries",
    },
    "quickbooks": {
        "INVOICE": "Invoicing",
        "BILL": "Bills / AP",
        "PAYMENT": "Payments",
        "ACCOUNT": "Chart of Accounts",
        "CUSTOMER": "Customers",
        "VENDOR": "Vendors",
        "ITEM": "Items",
        "JOURNAL": "Journal Entries",
        "ESTIMATE": "Estimates",
        "PURCHASE": "Purchases",
    },
}


class TableGroupingStrategy(ABC):
    """Abstract strategy for grouping tables into logical modules."""

    @abstractmethod
    def group(self, tables: List[str]) -> Dict[str, List[str]]:
        """Group table names into named modules.

        Args:
            tables: List of table names.

        Returns:
            Dict mapping module name to list of tables in that module.
        """


class PrefixGroupingStrategy(TableGroupingStrategy):
    """Group tables by their leading prefix characters."""

    def __init__(self, prefix_map: Dict[str, str], separator: str = "_"):
        """
        Args:
            prefix_map: Mapping of prefix -> module name (e.g. {"GL": "General Ledger"}).
            separator: Character that separates prefix from rest of table name.
        """
        self.prefix_map = prefix_map
        self.separator = separator

    @classmethod
    def from_builtin(cls, erp_name: str) -> "PrefixGroupingStrategy":
        """Create a strategy from a built-in ERP prefix map.

        Args:
            erp_name: One of 'enertia', 'sap', 'oracle_ebs'.
        """
        key = erp_name.lower()
        if key not in BUILTIN_MAPS:
            raise ValueError(f"Unknown ERP: {erp_name}. Available: {list(BUILTIN_MAPS)}")
        return cls(prefix_map=BUILTIN_MAPS[key])

    def group(self, tables: List[str]) -> Dict[str, List[str]]:
        groups: Dict[str, List[str]] = {}
        # Sort prefixes longest-first so 'INV' matches before 'IN'
        sorted_prefixes = sorted(self.prefix_map.keys(), key=len, reverse=True)

        for table in tables:
            upper = table.upper()
            matched = False
            for prefix in sorted_prefixes:
                if upper.startswith(prefix + self.separator) or upper.startswith(prefix):
                    module = self.prefix_map[prefix]
                    groups.setdefault(module, []).append(table)
                    matched = True
                    break
            if not matched:
                groups.setdefault("Ungrouped", []).append(table)

        return groups


def list_builtin_strategies() -> List[Dict[str, object]]:
    """List available built-in ERP grouping strategies."""
    return [
        {"erp": name, "prefixes": list(pmap.keys()), "modules": list(pmap.values())}
        for name, pmap in BUILTIN_MAPS.items()
    ]
