from fastapi import HTTPException
from typing import Optional

from .salesforce_gateway import SalesforceGateway
from .sf_fls import assert_updateable


async def ensure_acr_available(sf: SalesforceGateway):
    """Soft-check that AccountContactRelation exists and is accessible."""
    try:
        desc = await sf.describe("AccountContactRelation")
        # Basic permission sanity: at least one createable field
        fields = desc.get("fields", []) if isinstance(desc, dict) else []
        if not any(bool(f.get("createable")) for f in fields):
            # Some orgs can read but not create ACR
            raise HTTPException(
                403, "Cannot create AccountContactRelation with this user"
            )
    except HTTPException:
        raise
    except Exception:
        raise HTTPException(
            400, "AccountContactRelation not available for this org/user"
        )


async def apply_accountid(
    sf: SalesforceGateway, contact_id: str, account_id: str
) -> dict:
    """Set Contact.AccountId after FLS checks; return audit info."""
    await assert_updateable(sf, "Contact", "AccountId")
    await sf.update("Contact", contact_id, {"AccountId": account_id})
    return {
        "applied_field": "AccountId",
        "applied_value": account_id,
        "relation_id": None,
    }


async def apply_acr(sf: SalesforceGateway, contact_id: str, account_id: str) -> dict:
    """Create or reactivate AccountContactRelation and return relation id."""
    await ensure_acr_available(sf)
    # Avoid duplicate relations
    q = (
        "SELECT Id, IsActive FROM AccountContactRelation "
        f"WHERE ContactId='{contact_id}' AND AccountId='{account_id}' LIMIT 1"
    )
    res = await sf.soql(q)
    try:
        if (res or {}).get("totalSize", 0) > 0:
            rid = (res.get("records") or [{}])[0].get("Id")
            if rid:
                await sf.update("AccountContactRelation", rid, {"IsActive": True})
                return {
                    "applied_field": "AccountContactRelation",
                    "applied_value": account_id,
                    "relation_id": rid,
                }
    except Exception:
        # Proceed to create
        pass

    payload = {
        "ContactId": contact_id,
        "AccountId": account_id,
        "IsActive": True,
        "IsDirect": False,
    }
    result = await sf.create("AccountContactRelation", payload)
    rid = None
    if isinstance(result, dict):
        rid = result.get("id") or result.get("Id")
    return {
        "applied_field": "AccountContactRelation",
        "applied_value": account_id,
        "relation_id": rid,
    }


async def undo_accountid(
    sf: SalesforceGateway, contact_id: str, previous_value: Optional[str]
):
    """Revert Contact.AccountId to previous value (or clear)."""
    await assert_updateable(sf, "Contact", "AccountId")
    await sf.update("Contact", contact_id, {"AccountId": previous_value or None})


async def undo_acr(
    sf: SalesforceGateway, relation_id: Optional[str], contact_id: str, account_id: str
):
    """Delete the specific AccountContactRelation when possible, otherwise best-effort lookup.

    Note: It is safest to use the exact relation_id returned from creation.
    """
    await ensure_acr_available(sf)
    if relation_id:
        try:
            return await sf.delete("AccountContactRelation", relation_id)
        except Exception:
            # Fall back to a lookup delete below
            pass

    q = (
        "SELECT Id FROM AccountContactRelation "
        f"WHERE ContactId='{contact_id}' AND AccountId='{account_id}' AND IsActive=true LIMIT 1"
    )
    res = await sf.soql(q)
    if (res or {}).get("totalSize", 0) > 0:
        rid = (res.get("records") or [{}])[0].get("Id")
        if rid:
            return await sf.delete("AccountContactRelation", rid)
    return None
