"""
PIT Constants Module
====================
Provides access to PIT language enum constants for keyword parameters.
All constant data is embedded directly in this module (converted from pit_constants.json).

Usage:
    from pit_constants import PITConstants
    
    # Check if value is valid
    PITConstants.is_valid_frequency("3M")  # True
    PITConstants.is_valid_daycount("Act/360")  # True (alias)
    
    # Get all values for a parameter
    freqs = PITConstants.get_frequency_values()
    
    # Get default value
    default_freq = PITConstants.get_frequency_default()  # "6M"
    
    # Resolve aliases
    PITConstants.resolve_frequency("Quarterly")  # "3M"
"""

import re
from typing import Any, Dict, List, Optional

# ========== Embedded Constants Data (converted from pit_constants.json) ==========

_PIT_CONSTANTS_DATA: Dict[str, Any] = {
    "Frequency": {
        "type": "Freq::Type",
        "default": "6M",
        "values": [
            "DefaultFreq",
            "Zerocoupon",
            "1Y",
            "2Y",
            "3Y",
            "4Y",
            "5Y",
            "10Y",
            "15Y",
            "20Y",
            "30Y",
            "1M",
            "2M",
            "3M",
            "4M",
            "6M",
            "18M",
            "1W",
            "2W",
            "3W",
            "4W",
            "2D",
            "3D",
            "4D",
            "5D",
            "6D",
            "8D",
            "9D",
            "10D",
            "11D",
            "12D",
            "13D",
            "15D",
            "16D",
            "17D",
            "18D",
            "19D",
            "20D",
            "22D",
            "23D",
            "24D",
            "25D",
            "26D",
            "27D",
            "28D",
            "1B",
            "2B",
            "3B",
            "4B",
            "5B",
            "6B",
            "7B",
            "8B",
            "9B",
            "10B",
            "11B",
            "12B",
            "13B",
            "14B",
            "15B",
            "16B",
            "17B",
            "18B",
            "19B",
            "20B",
            "21B",
            "22B",
            "23B",
            "BusinessDays",
            "Daily",
            "NotSpecified",
        ],
        "aliases": {
            "Annual": "1Y",
            "SemiAnnual": "6M",
            "Quarterly": "3M",
            "Monthly": "1M",
            "Weekly": "1W",
            "Biweekly": "2W",
        },
    },
    "DayCount": {
        "type": "DayCount::Type",
        "default": "Actual/360",
        "values": [
            "Actual/Actual",
            "Actual/365",
            "Actual/360",
            "30/360",
            "30E/360",
            "30E/360.ISDA",
            "Act/R",
            "Actual/Feb29",
            "Actual/Isma",
            "DefaultDayCount",
            "BusinessDays/252",
            "Actual/365(JPY)",
            "Actual/365.25",
            "Actual+1/360",
            "Actual+1/365",
            "Actual/365(366)",
            "Actual/364",
            "Actual+1/365(Thai)",
            "Actual/Actual(RUS)",
            "30E+1/360(ITL)",
        ],
        "aliases": {
            "Act/360": "Actual/360",
            "Act/365": "Actual/365",
            "Act/Act": "Actual/Actual",
        },
    },
    "Calendar": {
        "type": "Calendar",
        "default": "Target",
        "values": [
            "Target",
            "TARGET",
            "AED",
            "ARS",
            "AUD",
            "AUME",
            "AUSY",
            "BGN",
            "BHD",
            "BMD",
            "BOB",
            "BRL",
            "BSD",
            "CAD",
            "CATO",
            "CHF",
            "CHZU",
            "CLP",
            "CNH",
            "CNY",
            "COP",
            "CZK",
            "CZPR",
            "DKCO",
            "DKK",
            "EUR",
            "EUTA",
            "GBLO",
            "GBP",
            "HKD",
            "HKHK",
            "HRK",
            "HUBU",
            "HUF",
            "IDR",
            "ILS",
            "INR",
            "ISK",
            "JPTO",
            "JPY",
            "KES",
            "KRW",
            "KRW_WL",
            "KWD",
            "MAINL",
            "MXMC",
            "MXN",
            "MYR",
            "NOK",
            "NOOS",
            "NYSE",
            "NZAU",
            "NZD",
            "NZWE",
            "PHP",
            "PLN",
            "PLWA",
            "QAR",
            "RON",
            "RUB",
            "SAR",
            "SEK",
            "SEST",
            "SGD",
            "SGSI",
            "SVK",
            "THB",
            "TRY",
            "TWD",
            "UAH",
            "USD",
            "USNY",
            "VND",
            "ZAJO",
            "ZAR",
            "Alger",
            "Algeria",
            "Amsterdam",
            "Argentina",
            "Athens",
            "Australia",
            "Austria",
            "Bahamas",
            "Bahrain",
            "Bangkok",
            "Beijing",
            "Belgium",
            "Bermuda",
            "Bogota",
            "Bolivia",
            "Bombay",
            "Brazil",
            "Brussels",
            "Bucharest",
            "Budapest",
            "BuenosAeres",
            "Bulgaria",
            "Canada",
            "Chile",
            "China",
            "Colombia",
            "Copenhagen",
            "CopenhagenStockExchange",
            "Croatia",
            "CzechRepublic",
            "Denmark",
            "Doha",
            "Dubai",
            "Dublin",
            "Eurex",
            "FAST",
            "Finland",
            "France",
            "Frankfurt",
            "Germany",
            "Greece",
            "Helsinki",
            "Holland",
            "HongKong",
            "Hungary",
            "Iceland",
            "India",
            "Indonesia",
            "Ireland",
            "Israel",
            "Italy",
            "ItalyExchange",
            "Japan",
            "Johannesburg",
            "Kenya",
            "KualaLumpur",
            "Kuwait",
            "Lisbon",
            "London",
            "LondonExchange",
            "LondonMetals",
            "Luxembourg",
            "Madrid",
            "MainlandChina",
            "Malaysia",
            "Mexico",
            "Milan",
            "Moscow",
            "Nairobi",
            "NewYork",
            "NewZealand",
            "NoWeekend",
            "Norway",
            "OffshoreChina",
            "Oslo",
            "Paris",
            "Philippines",
            "Poland",
            "Portugal",
            "Prague",
            "Qatar",
            "Reykjavik",
            "Riad",
            "Romania",
            "Russia",
            "SanPaolo",
            "Santiago",
            "SaudiArabia",
            "Singapore",
            "Slovakia",
            "Sofia",
            "SouthAfrica",
            "SouthKorea",
            "SouthKorea_WL",
            "Spain",
            "Stockholm",
            "Sweden",
            "Switzerland",
            "Sydney",
            "Taiwan",
            "TelAviv",
            "Thailand",
            "Tokyo",
            "Toronto",
            "Turkey",
            "UK",
            "US",
            "US_Bond",
            "US_NERC",
            "Ukraine",
            "UnitedArabEmirates",
            "UnitedKingdom",
            "UnitedStates",
            "Vienna",
            "Vietnam",
            "Warsaw",
            "Weekend",
            "Wellington",
            "Xetra",
            "Zagreb",
            "Zurich",
            "nullCalendar",
            "NULL",
        ],
        "common": [
            "Target",
            "TARGET",
            "USD",
            "US",
            "NewYork",
            "EUR",
            "Germany",
            "Frankfurt",
            "GBP",
            "UK",
            "London",
            "JPY",
            "Japan",
            "Tokyo",
            "CHF",
            "Switzerland",
            "Zurich",
            "CAD",
            "Canada",
            "Toronto",
            "AUD",
            "Australia",
            "Sydney",
            "HKD",
            "HongKong",
            "SGD",
            "Singapore",
            "NoWeekend",
        ],
    },
    "ResetTiming": {
        "type": "ResetTiming::Type",
        "default": "Advance",
        "values": [
            "Advance",
            "Arrears",
        ],
        "aliases": {
            "Yes": "Arrears",
            "No": "Advance",
            "True": "Arrears",
            "False": "Advance",
        },
    },
    "PayTiming": {
        "type": "ResetTiming::Type",
        "default": "Arrears",
        "values": [
            "Advance",
            "Arrears",
        ],
        "aliases": {
            "Yes": "Advance",
            "No": "Arrears",
            "True": "Advance",
            "False": "Arrears",
        },
    },
    "Stub": {
        "type": "StubRule::Type",
        "default": "ShortStart",
        "values": [
            "ShortStart",
            "LongStart",
            "ShortEnd",
            "LongEnd",
            "IMM_Quarterly",
            "SpecificBoth",
            "Default",
            "RollDateSpecified",
        ],
        "abbreviations": {
            "SS": "ShortStart",
            "LS": "LongStart",
            "SE": "ShortEnd",
            "LE": "LongEnd",
        },
    },
    "DateRule": {
        "type": "DateAdjRule::Type",
        "default": "ModifiedFollowing",
        "values": [
            "Unadjusted",
            "Previous",
            "ModifiedPrevious",
            "Following",
            "ModifiedFollowing",
            "EndOfMonth",
            "FollowingUnadjusted",
            "ModifiedFollowingUnadjusted",
            "UnadjustedFollowing",
        ],
        "aliases": {
            "ModFollowing": "ModifiedFollowing",
            "ModPrevious": "ModifiedPrevious",
            "Foll": "Following",
            "Prev": "Previous",
        },
    },
    "PaymentDateRule": {
        "type": "DateAdjRule::Type",
        "default": "ModifiedFollowing",
        "values": [
            "Unadjusted",
            "Previous",
            "ModifiedPrevious",
            "Following",
            "ModifiedFollowing",
            "EndOfMonth",
            "FollowingUnadjusted",
            "ModifiedFollowingUnadjusted",
            "UnadjustedFollowing",
        ],
        "aliases": {
            "ModFollowing": "ModifiedFollowing",
            "ModPrevious": "ModifiedPrevious",
            "Foll": "Following",
            "Prev": "Previous",
        },
    },
    "GapFormat": {
        "description": "Format specification for ResetGap and PayGap parameters",
        "examples": ["-2b", "0b", "5d", "-1w", "DEFAULT"],
        "rules": {
            "b": "Business days (e.g., '-2b' = 2 business days before)",
            "d": "Calendar days (e.g., '5d' = 5 calendar days after)",
            "w": "Weeks (e.g., '1w' = 1 week)",
            "DEFAULT": "Use system default value",
        },
    },
}


# ========== Legacy Keyword Classification (kept for backward compatibility) ==========

# PIT Language Keyword Classification Structure
# Main Categories: Programming vs Financial
# Each with specific sub-types
KEYWORD_CATEGORIES = {
    "Programming": {
        "Conditional": [],  # If, Then, Else, etc.
        "Date": [],  # Date manipulation keywords
        "Mathematical": [],  # Math functions
        "Sort Algos": [],  # Sorting algorithms
    },
    "Financial": {
        "General": [],  # General financial keywords
        "IR": [],  # Interest Rate products
        "Equity": [],  # Equity products
        "FX": [],  # Foreign Exchange products
        "Credit": [],  # Credit products
        "Inflation": [],  # Inflation products
        "Hybrid": [],  # Hybrid/cross-asset products
    },
}

# Flat list of all valid keyword types for validation
VALID_KEYWORD_TYPES = [
    # Programming types
    "Conditional",
    "Date",
    "Mathematical",
    "Sort Algos",
    # Financial types
    "General",
    "IR",
    "Equity",
    "FX",
    "Credit",
    "Inflation",
    "Hybrid",
]

# Legacy: Financial sub-types only (for backward compatibility)
VALID_ASSET_CLASSES = ["General", "IR", "Equity", "FX", "Credit", "Inflation", "Hybrid"]
DEFAULT_KEYWORD_TYPE = "Unknown"
DEFAULT_ASSET_CLASS = "Unknown"  # Legacy


# ========== PIT Enum Constants (New) ==========


class PITConstants:
    """Provides access to PIT enum constants and validation."""

    _constants: Dict[str, Any] = _PIT_CONSTANTS_DATA

    # ========== Frequency ==========

    @classmethod
    def get_frequency_values(cls) -> List[str]:
        """Get all valid Frequency values."""
        return cls._constants["Frequency"]["values"]

    @classmethod
    def get_frequency_default(cls) -> str:
        """Get default Frequency value."""
        return cls._constants["Frequency"]["default"]

    @classmethod
    def is_valid_frequency(cls, value: str) -> bool:
        """Check if value is a valid Frequency (including aliases)."""
        freq = cls._constants["Frequency"]
        return value in freq["values"] or value in freq.get("aliases", {})

    @classmethod
    def resolve_frequency(cls, value: str) -> str:
        """Resolve frequency alias to canonical value."""
        freq = cls._constants["Frequency"]
        return freq.get("aliases", {}).get(value, value)

    # ========== DayCount ==========

    @classmethod
    def get_daycount_values(cls) -> List[str]:
        """Get all valid DayCount values."""
        return cls._constants["DayCount"]["values"]

    @classmethod
    def get_daycount_default(cls) -> str:
        """Get default DayCount value."""
        return cls._constants["DayCount"]["default"]

    @classmethod
    def is_valid_daycount(cls, value: str) -> bool:
        """Check if value is a valid DayCount (including aliases)."""
        dc = cls._constants["DayCount"]
        return value in dc["values"] or value in dc.get("aliases", {})

    @classmethod
    def resolve_daycount(cls, value: str) -> str:
        """Resolve daycount alias to canonical value."""
        dc = cls._constants["DayCount"]
        return dc.get("aliases", {}).get(value, value)

    # ========== Calendar ==========

    @classmethod
    def get_calendar_values(cls) -> List[str]:
        """Get all valid Calendar values."""
        return cls._constants["Calendar"]["values"]

    @classmethod
    def get_calendar_common(cls) -> List[str]:
        """Get commonly used Calendar values."""
        return cls._constants["Calendar"].get("common", [])

    @classmethod
    def get_calendar_default(cls) -> str:
        """Get default Calendar value."""
        return cls._constants["Calendar"]["default"]

    @classmethod
    def is_valid_calendar(cls, value: str) -> bool:
        """Check if value is a valid Calendar."""
        return value in cls._constants["Calendar"]["values"]

    # ========== ResetTiming ==========

    @classmethod
    def get_resettiming_values(cls) -> List[str]:
        """Get all valid ResetTiming values."""
        return cls._constants["ResetTiming"]["values"]

    @classmethod
    def get_resettiming_default(cls) -> str:
        """Get default ResetTiming value."""
        return cls._constants["ResetTiming"]["default"]

    @classmethod
    def is_valid_resettiming(cls, value: str) -> bool:
        """Check if value is a valid ResetTiming (including aliases)."""
        rt = cls._constants["ResetTiming"]
        return value in rt["values"] or value in rt.get("aliases", {})

    @classmethod
    def resolve_resettiming(cls, value: str) -> str:
        """Resolve ResetTiming alias to canonical value."""
        rt = cls._constants["ResetTiming"]
        return rt.get("aliases", {}).get(value, value)

    # ========== PayTiming ==========

    @classmethod
    def get_paytiming_values(cls) -> List[str]:
        """Get all valid PayTiming values."""
        return cls._constants["PayTiming"]["values"]

    @classmethod
    def get_paytiming_default(cls) -> str:
        """Get default PayTiming value."""
        return cls._constants["PayTiming"]["default"]

    @classmethod
    def is_valid_paytiming(cls, value: str) -> bool:
        """Check if value is a valid PayTiming (including aliases)."""
        pt = cls._constants["PayTiming"]
        return value in pt["values"] or value in pt.get("aliases", {})

    @classmethod
    def resolve_paytiming(cls, value: str) -> str:
        """Resolve PayTiming alias to canonical value."""
        pt = cls._constants["PayTiming"]
        return pt.get("aliases", {}).get(value, value)

    # ========== Stub ==========

    @classmethod
    def get_stub_values(cls) -> List[str]:
        """Get all valid Stub values."""
        return cls._constants["Stub"]["values"]

    @classmethod
    def get_stub_default(cls) -> str:
        """Get default Stub value."""
        return cls._constants["Stub"]["default"]

    @classmethod
    def is_valid_stub(cls, value: str) -> bool:
        """Check if value is a valid Stub (including abbreviations)."""
        stub = cls._constants["Stub"]
        return value in stub["values"] or value in stub.get("abbreviations", {})

    @classmethod
    def resolve_stub(cls, value: str) -> str:
        """Resolve Stub abbreviation to full name."""
        stub = cls._constants["Stub"]
        return stub.get("abbreviations", {}).get(value, value)

    # ========== DateRule ==========

    @classmethod
    def get_daterule_values(cls) -> List[str]:
        """Get all valid DateRule values."""
        return cls._constants["DateRule"]["values"]

    @classmethod
    def get_daterule_default(cls) -> str:
        """Get default DateRule value."""
        return cls._constants["DateRule"]["default"]

    @classmethod
    def is_valid_daterule(cls, value: str) -> bool:
        """Check if value is a valid DateRule (including aliases)."""
        dr = cls._constants["DateRule"]
        return value in dr["values"] or value in dr.get("aliases", {})

    @classmethod
    def resolve_daterule(cls, value: str) -> str:
        """Resolve DateRule alias to canonical value."""
        dr = cls._constants["DateRule"]
        return dr.get("aliases", {}).get(value, value)

    # ========== PaymentDateRule ==========

    @classmethod
    def get_paymentdaterule_values(cls) -> List[str]:
        """Get all valid PaymentDateRule values."""
        return cls._constants["PaymentDateRule"]["values"]

    @classmethod
    def get_paymentdaterule_default(cls) -> str:
        """Get default PaymentDateRule value."""
        return cls._constants["PaymentDateRule"]["default"]

    @classmethod
    def is_valid_paymentdaterule(cls, value: str) -> bool:
        """Check if value is a valid PaymentDateRule (including aliases)."""
        pdr = cls._constants["PaymentDateRule"]
        return value in pdr["values"] or value in pdr.get("aliases", {})

    @classmethod
    def resolve_paymentdaterule(cls, value: str) -> str:
        """Resolve PaymentDateRule alias to canonical value."""
        pdr = cls._constants["PaymentDateRule"]
        return pdr.get("aliases", {}).get(value, value)

    # ========== Gap Format ==========

    @classmethod
    def is_valid_gap_format(cls, value: str) -> bool:
        """
        Check if value is a valid gap format.
        Valid formats: '-2b', '0b', '5d', '-1w', 'DEFAULT'
        """
        if value == "DEFAULT":
            return True
        if not value:
            return False
        return bool(re.match(r"^-?\d+[bdw]$", value))

    @classmethod
    def get_gap_examples(cls) -> List[str]:
        """Get example gap format values."""
        return cls._constants["GapFormat"]["examples"]

    # ========== Generic Methods ==========

    @classmethod
    def get_values(cls, param_name: str) -> List[str]:
        """Get values for any parameter by name."""
        param_data = cls._constants.get(param_name)
        if param_data:
            return param_data.get("values", [])
        return []

    @classmethod
    def get_default(cls, param_name: str) -> Optional[str]:
        """Get default value for any parameter by name."""
        param_data = cls._constants.get(param_name)
        if param_data:
            return param_data.get("default")
        return None

    @classmethod
    def is_valid(cls, param_name: str, value: str) -> bool:
        """Generic validation for any parameter."""
        method_name = f"is_valid_{param_name.lower()}"
        if hasattr(cls, method_name):
            return getattr(cls, method_name)(value)
        return False

    @classmethod
    def get_all_constants(cls) -> Dict[str, Any]:
        """Get the entire constants dictionary."""
        return cls._constants


# Convenience functions for DateTable parameters
def validate_datetable_params(params: Dict[str, str]) -> Dict[str, bool]:
    """
    Validate all DateTable parameters.

    Args:
        params: Dictionary of parameter names to values

    Returns:
        Dictionary of parameter names to validation results (True/False)
    """
    results = {}

    validators = {
        "Frequency": PITConstants.is_valid_frequency,
        "DayCount": PITConstants.is_valid_daycount,
        "Calendar": PITConstants.is_valid_calendar,
        "ResetTiming": PITConstants.is_valid_resettiming,
        "PayTiming": PITConstants.is_valid_paytiming,
        "Stub": PITConstants.is_valid_stub,
        "DateRule": PITConstants.is_valid_daterule,
        "PaymentDateRule": PITConstants.is_valid_paymentdaterule,
        "ResetGap": PITConstants.is_valid_gap_format,
        "PayGap": PITConstants.is_valid_gap_format,
    }

    for param_name, value in params.items():
        validator = validators.get(param_name)
        if validator:
            results[param_name] = validator(value)
        else:
            results[param_name] = True  # Unknown parameter, assume valid

    return results


def get_datetable_defaults() -> Dict[str, str]:
    """Get all default values for DateTable parameters."""
    return {
        "Frequency": PITConstants.get_frequency_default(),
        "DayCount": PITConstants.get_daycount_default(),
        "Calendar": PITConstants.get_calendar_default(),
        "ResetTiming": PITConstants.get_resettiming_default(),
        "PayTiming": PITConstants.get_paytiming_default(),
        "Stub": PITConstants.get_stub_default(),
        "DateRule": PITConstants.get_daterule_default(),
        "PaymentDateRule": PITConstants.get_paymentdaterule_default(),
        "ResetGap": "DEFAULT",
        "PayGap": "DEFAULT",
    }
