import json, math, os, requests, calendar, pytz
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta
from functools import lru_cache

# Initialize a requests Session for connection pooling
_session = requests.Session()

def _fetch_product_config_internal(product_id: int):
    config_url = os.getenv("MASTER_LMS_API_URL")
    config_token = os.getenv("MASTER_LMS_API_TOKEN")
    if not config_url or not config_token:
        raise ValueError("MASTER_LMS_API_URL or MASTER_LMS_API_TOKEN environment variables are missing.")
    
    url = f"https://{config_url}/config/products/{product_id}/configs"
    headers = {"Authorization": f"Bearer {config_token}"}
    
    response = _session.get(url, headers=headers)
    response.raise_for_status()
    data = response.json()
    
    # Extract split info
    try:
        if not data.get("rps"):
            raise ValueError(f"product_id {product_id} not available")
            
        allowed_splits = data["rps"]["allowed_splits"]
        
        # Robustly find the split configuration that has multiple parties (the actual co-lending split)
        parts = None
        for split in allowed_splits:
            if len(split.get("parts", [])) > 1:
                parts = split["parts"]
                break
                
        # Fallback to the first one if we can't find a multi-part split
        if not parts and allowed_splits:
            parts = allowed_splits[0].get("parts", [])
            
        # Find primary_lender
        primary = next(p for p in parts if p["party"] == "primary_lender")
        pct = primary["pct"]
        hurdle_rate = primary.get("hurdle_rate", 0)
        calculation_basis = data.get("interest", {}).get("calculation_basis", "ACTUAL_365")
        bpi_day_convention = data.get("interest", {}).get("bpi_day_convention", "ACTUAL_365")
        
        bpi_app_raw = data.get("interest", {}).get("bpi_applicable", True)
        if isinstance(bpi_app_raw, str):
            bpi_applicable = bpi_app_raw.lower() == 'true'
        else:
            bpi_applicable = bool(bpi_app_raw)
            
        bpi_treatment = data.get("interest", {}).get("bpi_treatment", "SEPARATE_BEFORE_EMI")
        rounding_config = {
            "emi_rounding": data.get("interest", {}).get("emi_rounding", "ROUND"),
            "emi_rounding_digits": data.get("interest", {}).get("emi_rounding_digits", 0),
            "interest_rounding": data.get("interest", {}).get("interest_rounding", "ROUND"),
            "interest_rounding_digits": data.get("interest", {}).get("interest_rounding_digits", 0),
            "bpi_rounding": data.get("interest", {}).get("bpi_rounding", "ROUND"),
            "bpi_rounding_digits": data.get("interest", {}).get("bpi_rounding_digits", 0),
        }
        
        disbursement_logic = data.get("disbursement", {}).get("disbursement_logic", "FIXED_DATE")
        fixed_day_of_month = data.get("disbursement", {}).get("fixed_day_of_month", 6)
        emi_due_day = data.get("emi", {}).get("emi_due_day", 3)
        
        return pct / 100.0, hurdle_rate / 100.0, calculation_basis, bpi_day_convention, bpi_applicable, bpi_treatment, rounding_config, data, disbursement_logic, fixed_day_of_month, emi_due_day
    except (KeyError, IndexError, StopIteration, TypeError) as e:
        if "not available" in str(e):
            raise
        raise ValueError(f"Failed to parse config for product_id {product_id}: {e}")

def fetch_product_config_no_cache(product_id: int):
    return _fetch_product_config_internal(product_id)

@lru_cache(maxsize=128)
def fetch_product_config(product_id: int):
    return _fetch_product_config_internal(product_id)

def _fetch_master_config_internal(product_id, allocation_type="REGULAR_EMI"):
    config_url = os.getenv("MASTER_LMS_API_URL")
    config_token = os.getenv("MASTER_LMS_API_TOKEN")
    if not config_url or not config_token:
        raise ValueError("MASTER_LMS_API_URL or MASTER_LMS_API_TOKEN environment variables are missing.")
    
    url = f"https://{config_url}/config/products/{product_id}/configs"
    headers = {"Authorization": f"Bearer {config_token}"}
    
    response = _session.get(url, headers=headers)
    response.raise_for_status()
    data = response.json()
    
    try:
        allocation_config = data.get("allocation", [])
        active_config = next((c for c in allocation_config if c["sequence_type"] == allocation_type), None)
        if not active_config:
            # Fallback to REGULAR_EMI if specific config not found
            active_config = next((c for c in allocation_config if c["sequence_type"] == "REGULAR_EMI"), None)
            
        if not active_config:
            raise ValueError(f"{allocation_type} allocation config not found")
            
        priority_sequence = active_config["priority_sequence"]
        
        rounding_config = {
            "emi_rounding": data.get("interest", {}).get("emi_rounding", "ROUND"),
            "emi_rounding_digits": data.get("interest", {}).get("emi_rounding_digits", 0),
            "interest_rounding": data.get("interest", {}).get("interest_rounding", "ROUND"),
            "interest_rounding_digits": data.get("interest", {}).get("interest_rounding_digits", 0),
            "bpi_rounding": data.get("interest", {}).get("bpi_rounding", "ROUND"),
            "bpi_rounding_digits": data.get("interest", {}).get("bpi_rounding_digits", 0),
            "principal_rounding": data.get("interest", {}).get("emi_rounding", "ROUND"),
            "principal_rounding_digits": data.get("interest", {}).get("emi_rounding_digits", 0),
        }
        
        park_advance_collection = False
        if "specific_collections" in data and isinstance(data["specific_collections"], list):
            specific_config = next((item for item in data["specific_collections"] if item.get("collection_type") == "ADVANCE_COLLECTION"), None)
            if specific_config:
                park_advance_collection = specific_config.get("park_collection", False)
        
        return priority_sequence, rounding_config, park_advance_collection
    except (KeyError, IndexError, StopIteration, TypeError) as e:
        raise ValueError(f"Failed to parse master config for product_id {product_id}: {e}")

def fetch_master_config_no_cache(product_id, allocation_type="REGULAR_EMI"):
    return _fetch_master_config_internal(product_id, allocation_type)

@lru_cache(maxsize=128)
def fetch_master_config(product_id, allocation_type="REGULAR_EMI"):
    return _fetch_master_config_internal(product_id, allocation_type)
