from fastapi import APIRouter, Depends, HTTPException, status
from app.dependencies.security import require_auth

router = APIRouter(prefix="/config", tags=["Configuration Service"], dependencies=[require_auth])
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.schemas.configuration import (
    LenderCreate, LenderOut, LenderUpdate, ProductCreate, ProductOut, ProductUpdate,
    InterestConfigCreate, AllocationConfigCreate, ChargeConfigCreate,
    CollectionConfigCreate, ForeclosureConfigCreate, NpaConfigCreate, DisbursementConfigCreate,
    WaiveOffConfigCreate, RpsConfigCreate, SpecificCollectionConfigCreate, EmiConfigCreate
)
from app.models.lender import Lender, Product
from app.models.configuration import (
    InterestConfiguration, AllocationConfiguration, ChargeConfiguration,
    CollectionConfiguration, ForeclosureConfiguration, NpaConfiguration, DisbursementConfiguration,
    WaiveOffConfiguration, RpsConfiguration, SpecificCollectionConfiguration, EmiConfiguration
)
from app.services.configuration_service import ConfigurationService
from sqlalchemy import select
from typing import List



@router.post("/lenders", response_model=LenderOut, status_code=status.HTTP_201_CREATED)
async def create_lender(lender: LenderCreate, db: AsyncSession = Depends(get_db)):
    db_lender = Lender(name=lender.name, code=lender.code)
    db.add(db_lender)
    await db.commit()
    await db.refresh(db_lender)
    return db_lender

@router.put("/lenders/{lender_id}", response_model=LenderOut)
async def update_lender(lender_id: int, lender_update: LenderUpdate, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Lender).where(Lender.id == lender_id))
    db_lender = result.scalars().first()
    if not db_lender:
        raise HTTPException(status_code=404, detail="Lender not found")
        
    if lender_update.name is not None:
        db_lender.name = lender_update.name
    if lender_update.code is not None:
        if lender_update.code != db_lender.code:
            code_check = await db.execute(select(Lender).where(Lender.code == lender_update.code))
            if code_check.scalars().first():
                raise HTTPException(status_code=400, detail="Lender code already exists")
        db_lender.code = lender_update.code
    if lender_update.is_active is not None:
        db_lender.is_active = lender_update.is_active
        
    await db.commit()
    await db.refresh(db_lender)
    return db_lender

@router.delete("/lenders/{lender_id}", status_code=status.HTTP_200_OK)
async def delete_lender(lender_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Lender).where(Lender.id == lender_id))
    lender = result.scalars().first()
    if not lender:
        raise HTTPException(status_code=404, detail="Lender not found")
        
    # Get all products under this lender
    products_res = await db.execute(select(Product).where(Product.lender_id == lender_id))
    products = products_res.scalars().all()
    
    from sqlalchemy import delete
    from app.models.configuration import (
        InterestConfiguration, AllocationConfiguration, ChargeConfiguration,
        CollectionConfiguration, ForeclosureConfiguration, NpaConfiguration,
        DisbursementConfiguration, SettlementConfiguration, WorkflowConfiguration, ProductConfiguration,
        EmiConfiguration
    )
    
    for product in products:
        await db.execute(delete(InterestConfiguration).where(InterestConfiguration.product_id == product.id))
        await db.execute(delete(AllocationConfiguration).where(AllocationConfiguration.product_id == product.id))
        await db.execute(delete(ChargeConfiguration).where(ChargeConfiguration.product_id == product.id))
        await db.execute(delete(CollectionConfiguration).where(CollectionConfiguration.product_id == product.id))
        await db.execute(delete(ForeclosureConfiguration).where(ForeclosureConfiguration.product_id == product.id))
        await db.execute(delete(NpaConfiguration).where(NpaConfiguration.product_id == product.id))
        await db.execute(delete(DisbursementConfiguration).where(DisbursementConfiguration.product_id == product.id))
        await db.execute(delete(EmiConfiguration).where(EmiConfiguration.product_id == product.id))
        await db.execute(delete(SettlementConfiguration).where(SettlementConfiguration.product_id == product.id))
        await db.execute(delete(WorkflowConfiguration).where(WorkflowConfiguration.product_id == product.id))
        await db.execute(delete(ProductConfiguration).where(ProductConfiguration.product_id == product.id))
        await db.execute(delete(SpecificCollectionConfiguration).where(SpecificCollectionConfiguration.product_id == product.id))
        await db.execute(delete(WaiveOffConfiguration).where(WaiveOffConfiguration.product_id == product.id))
        await db.execute(delete(RpsConfiguration).where(RpsConfiguration.product_id == product.id))
        await db.delete(product)
        
    await db.delete(lender)
    await db.commit()
    return {"status": "SUCCESS", "message": "Lender, associated products and configs deleted successfully."}


@router.post("/products", response_model=ProductOut, status_code=status.HTTP_201_CREATED)
async def create_product(product: ProductCreate, db: AsyncSession = Depends(get_db)):
    # Verify lender exists
    result = await db.execute(select(Lender).where(Lender.id == product.lender_id))
    lender = result.scalars().first()
    if not lender:
        raise HTTPException(status_code=404, detail="Lender not found")
        
    db_product = Product(lender_id=product.lender_id, name=product.name, code=product.code)
    db.add(db_product)
    await db.commit()
    await db.refresh(db_product)
    return db_product

@router.put("/products/{product_id}", response_model=ProductOut)
async def update_product(product_id: int, product_update: ProductUpdate, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Product).where(Product.id == product_id))
    db_product = result.scalars().first()
    if not db_product:
        raise HTTPException(status_code=404, detail="Product not found")
        
    if product_update.name is not None:
        db_product.name = product_update.name
    if product_update.code is not None:
        db_product.code = product_update.code
    if product_update.is_active is not None:
        db_product.is_active = product_update.is_active
        
    await db.commit()
    await db.refresh(db_product)
    return db_product

@router.delete("/products/{product_id}", status_code=status.HTTP_200_OK)
async def delete_product(product_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Product).where(Product.id == product_id))
    product = result.scalars().first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
        
    from sqlalchemy import delete
    from app.models.configuration import (
        InterestConfiguration, AllocationConfiguration, ChargeConfiguration,
        CollectionConfiguration, ForeclosureConfiguration, NpaConfiguration,
        DisbursementConfiguration, SettlementConfiguration, WorkflowConfiguration, ProductConfiguration,
        EmiConfiguration
    )
    
    await db.execute(delete(InterestConfiguration).where(InterestConfiguration.product_id == product_id))
    await db.execute(delete(AllocationConfiguration).where(AllocationConfiguration.product_id == product_id))
    await db.execute(delete(ChargeConfiguration).where(ChargeConfiguration.product_id == product_id))
    await db.execute(delete(CollectionConfiguration).where(CollectionConfiguration.product_id == product_id))
    await db.execute(delete(ForeclosureConfiguration).where(ForeclosureConfiguration.product_id == product_id))
    await db.execute(delete(NpaConfiguration).where(NpaConfiguration.product_id == product_id))
    await db.execute(delete(DisbursementConfiguration).where(DisbursementConfiguration.product_id == product_id))
    await db.execute(delete(EmiConfiguration).where(EmiConfiguration.product_id == product_id))
    await db.execute(delete(SettlementConfiguration).where(SettlementConfiguration.product_id == product_id))
    await db.execute(delete(WorkflowConfiguration).where(WorkflowConfiguration.product_id == product_id))
    await db.execute(delete(ProductConfiguration).where(ProductConfiguration.product_id == product_id))
    await db.execute(delete(SpecificCollectionConfiguration).where(SpecificCollectionConfiguration.product_id == product_id))
    await db.execute(delete(WaiveOffConfiguration).where(WaiveOffConfiguration.product_id == product_id))
    await db.execute(delete(RpsConfiguration).where(RpsConfiguration.product_id == product_id))
    
    await db.delete(product)
    await db.commit()
    return {"status": "SUCCESS", "message": "Product and associated configurations deleted successfully."}


@router.post("/interest", status_code=status.HTTP_201_CREATED)
async def save_interest_config(config: InterestConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = InterestConfiguration(
        product_id=config.product_id,
        calculation_basis=config.calculation_basis,
        emi_type=config.emi_type,
        repayment_frequency=config.repayment_frequency,
        grace_period_days=config.grace_period_days,
        bpi_applicable=config.bpi_applicable,
        bpi_treatment=config.bpi_treatment,
        bpi_day_convention=config.bpi_day_convention,
        emi_rounding=config.emi_rounding,
        emi_rounding_digits=config.emi_rounding_digits,
        interest_rounding=config.interest_rounding,
        interest_rounding_digits=config.interest_rounding_digits,
        bpi_rounding=config.bpi_rounding,
        bpi_rounding_digits=config.bpi_rounding_digits,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id, "version": saved.version}

@router.post("/allocation", status_code=status.HTTP_201_CREATED)
async def save_allocation_config(config: AllocationConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = AllocationConfiguration(
        product_id=config.product_id,
        sequence_type=config.sequence_type,
        priority_sequence=config.priority_sequence,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.post("/specific-collection", status_code=status.HTTP_201_CREATED)
async def save_specific_collection_config(config: SpecificCollectionConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = SpecificCollectionConfiguration(
        product_id=config.product_id,
        collection_type=config.collection_type,
        park_collection=config.park_collection,
        metadata_config=config.metadata_config,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.post("/charge", status_code=status.HTTP_201_CREATED)
async def save_charge_config(config: ChargeConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = ChargeConfiguration(
        product_id=config.product_id,
        charge_master_id=config.charge_master_id,
        charge_type=config.charge_type,
        value=config.value,
        is_taxable=config.is_taxable,
        tax_percentage=config.tax_percentage,
        partner_split=config.partner_split,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.get("/lenders", response_model=List[LenderOut])
async def list_lenders(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Lender))
    return list(result.scalars().all())

@router.get("/products", response_model=List[ProductOut])
async def list_products(lender_id: int | None = None, db: AsyncSession = Depends(get_db)):
    stmt = select(Product)
    if lender_id is not None:
        stmt = stmt.where(Product.lender_id == lender_id)
    result = await db.execute(stmt)
    return list(result.scalars().all())

@router.get("/products/{product_id}/configs")
async def get_product_configs(product_id: int, db: AsyncSession = Depends(get_db)):
    def model_to_dict(model_instance):
        if not model_instance:
            return None
        d = {}
        for col in model_instance.__table__.columns:
            val = getattr(model_instance, col.name)
            if hasattr(val, "isoformat"):
                d[col.name] = val.isoformat()
            elif hasattr(val, "to_eng_string") or hasattr(val, "as_tuple") or (type(val).__name__ == "Decimal"):
                d[col.name] = float(val)
            else:
                d[col.name] = val
        return d

    interest = await ConfigurationService.get_config(db, InterestConfiguration, product_id)
    allocations = await ConfigurationService.get_all_configs(db, AllocationConfiguration, product_id)
    charges = await ConfigurationService.get_all_configs(db, ChargeConfiguration, product_id)
    collection = await ConfigurationService.get_config(db, CollectionConfiguration, product_id)
    npa = await ConfigurationService.get_config(db, NpaConfiguration, product_id)
    foreclosure = await ConfigurationService.get_config(db, ForeclosureConfiguration, product_id)
    waive_off = await ConfigurationService.get_config(db, WaiveOffConfiguration, product_id)
    rps = await ConfigurationService.get_config(db, RpsConfiguration, product_id)
    disbursement = await ConfigurationService.get_config(db, DisbursementConfiguration, product_id)
    emi = await ConfigurationService.get_config(db, EmiConfiguration, product_id)
    specific_collections = await ConfigurationService.get_all_configs(db, SpecificCollectionConfiguration, product_id)

    return {
        "interest": model_to_dict(interest),
        "allocation": [model_to_dict(a) for a in allocations],
        "charges": [model_to_dict(c) for c in charges],

        "collection": model_to_dict(collection),
        "npa": model_to_dict(npa),
        "foreclosure": model_to_dict(foreclosure),
        "waive_off": model_to_dict(waive_off),
        "rps": model_to_dict(rps),
        "disbursement": model_to_dict(disbursement),
        "emi": model_to_dict(emi),
        "specific_collections": [model_to_dict(x) for x in specific_collections] if specific_collections else []
    }


@router.get("/products/{product_id}/rps-options")
async def get_rps_options(product_id: int, db: AsyncSession = Depends(get_db)):
    """
    Returns the active RPS (Repayment Schedule) split options configured for a product.
    Each option is a list of integers summing to 100, representing the POS % allocation
    for each sub-RPS document.
    """
    # Verify product exists
    result = await db.execute(select(Product).where(Product.id == product_id))
    if not result.scalars().first():
        raise HTTPException(status_code=404, detail="Product not found")

    rps_config = await ConfigurationService.get_config(db, RpsConfiguration, product_id)
    if not rps_config:
        return {"product_id": product_id, "allowed_splits": [], "configured": False}

    return {
        "product_id": product_id,
        "configured": True,
        "version": rps_config.version,
        "effective_from": rps_config.effective_from.isoformat() if hasattr(rps_config.effective_from, 'isoformat') else rps_config.effective_from,
        "allowed_splits": rps_config.allowed_splits,
        "split_count": len(rps_config.allowed_splits)
    }

@router.post("/collection", status_code=status.HTTP_201_CREATED)
async def save_collection_config(config: CollectionConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = CollectionConfiguration(
        product_id=config.product_id,
        allocation_strategy=config.allocation_strategy,
        advance_collection_strategy=config.advance_collection_strategy,
        excess_refund_required=config.excess_refund_required,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.post("/foreclosure", status_code=status.HTTP_201_CREATED)
async def save_foreclosure_config(config: ForeclosureConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = ForeclosureConfiguration(
        product_id=config.product_id,
        charge_type=config.charge_type,
        charge_value=config.charge_value,
        lock_in_period_months=config.lock_in_period_months,
        rebate_percentage=config.rebate_percentage,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.post("/npa", status_code=status.HTTP_201_CREATED)
async def save_npa_config(config: NpaConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = NpaConfiguration(
        product_id=config.product_id,
        dpd_threshold=config.dpd_threshold,
        bucket_classification_rules=config.bucket_classification_rules,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.post("/disbursement", status_code=status.HTTP_201_CREATED)
async def save_disbursement_config(config: DisbursementConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = DisbursementConfiguration(
        product_id=config.product_id,
        disbursement_logic=config.disbursement_logic,
        fixed_day_of_month=config.fixed_day_of_month,
        cut_off_hour=config.cut_off_hour,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}

@router.post("/emi", status_code=status.HTTP_201_CREATED)
async def save_emi_config(config: EmiConfigCreate, db: AsyncSession = Depends(get_db)):
    db_config = EmiConfiguration(
        product_id=config.product_id,
        emi_due_day=config.emi_due_day,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {"status": "SUCCESS", "id": saved.id}


@router.post("/waive-off", status_code=status.HTTP_201_CREATED)
async def save_waive_off_config(config: WaiveOffConfigCreate, db: AsyncSession = Depends(get_db)):
    """
    Save a waive-off configuration for a product.
    Defines which charge categories (BOUNCE, PENAL) are eligible for waiver
    and the priority order in which they are waived (index 0 = waived first).
    """
    db_config = WaiveOffConfiguration(
        product_id=config.product_id,
        waiveable_charge_categories=config.waiveable_charge_categories,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {
        "status": "SUCCESS",
        "id": saved.id,
        "version": saved.version,
        "waiveable_charge_categories": saved.waiveable_charge_categories
    }


@router.post("/rps", status_code=status.HTTP_201_CREATED)
async def save_rps_config(config: RpsConfigCreate, db: AsyncSession = Depends(get_db)):
    """
    Save an RPS (Repayment Schedule) generation configuration for a product.
    Each split option has a 'parts' list: [{pct, party, hurdle_rate?}].
    party must be 'primary_lender' or 'co-lender'. hurdle_rate (%) is optional
    and only applies to primary_lender tranches.
    """
    # Serialize Pydantic models → plain dicts so SQLAlchemy JSON column can store them
    serialized_splits = [
        {
            "parts": [
                {
                    "pct": part.pct,
                    "party": part.party,
                    **({"hurdle_rate": part.hurdle_rate} if part.hurdle_rate is not None else {})
                }
                for part in split.parts
            ]
        }
        for split in config.allowed_splits
    ]
    db_config = RpsConfiguration(
        product_id=config.product_id,
        allowed_splits=serialized_splits,
        version=config.version,
        effective_from=config.effective_from,
        effective_to=config.effective_to
    )
    saved = await ConfigurationService.save_config(db, db_config)
    await db.commit()
    return {
        "status": "SUCCESS",
        "id": saved.id,
        "version": saved.version,
        "split_count": len(saved.allowed_splits)
    }

