from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from typing import List
from pydantic import BaseModel

from ...db import get_session
from ...services.roles import require_steward, require_viewer
from ...models import FieldMappingPolicy, User
from ...services.policy_service import PolicyService, field_metadata_cache
from ...services.audit_service import AuditService
from ...services.salesforce_gateway import SalesforceGateway, get_salesforce_gateway

router = APIRouter(prefix="/api/v2/policies", tags=["policies"])


class PolicyRequest(BaseModel):
    salesforce_object: str
    matching_rules: dict
    writable_fields: List[str] = []
    duplicate_threshold: float = 0.85
    link_threshold: float = 0.75


class TestPolicyRequest(BaseModel):
    policy: PolicyRequest
    sample_size: int = 10


@router.get("")
async def list_policies(
    db: AsyncSession = Depends(get_session), user: User = Depends(require_viewer)
):
    """List all field mapping policies"""
    stmt = select(FieldMappingPolicy).where(
        FieldMappingPolicy.account_id == str(user.account_id),
        FieldMappingPolicy.is_active == True,
    )
    result = await db.execute(stmt)
    policies = result.scalars().all()

    return {
        "policies": [
            {
                "id": p.id,
                "object": p.salesforce_object,
                "version": p.version,
                "hash": p.policy_hash,
                "created_at": p.created_at.isoformat(),
                "rules_count": len(p.matching_rules.get("matching_fields", [])),
                "writable_fields_count": len(p.writable_fields),
            }
            for p in policies
        ]
    }


@router.get("/{salesforce_object}")
async def get_policy(
    salesforce_object: str,
    db: AsyncSession = Depends(get_session),
    user: User = Depends(require_viewer),
):
    """Get active policy for a Salesforce object"""
    stmt = select(FieldMappingPolicy).where(
        FieldMappingPolicy.account_id == str(user.account_id),
        FieldMappingPolicy.salesforce_object == salesforce_object,
        FieldMappingPolicy.is_active == True,
    )
    result = await db.execute(stmt)
    policy = result.scalar_one_or_none()

    if not policy:
        raise HTTPException(404, f"No active policy for {salesforce_object}")

    return {
        "id": policy.id,
        "object": policy.salesforce_object,
        "version": policy.version,
        "matching_rules": policy.matching_rules,
        "writable_fields": policy.writable_fields,
        "thresholds": {
            "duplicate": policy.duplicate_threshold,
            "link": policy.link_threshold,
        },
        "field_metadata": policy.field_metadata,
        "metadata_updated_at": policy.metadata_updated_at.isoformat()
        if policy.metadata_updated_at
        else None,
    }


@router.post("")
async def create_or_update_policy(
    body: PolicyRequest,
    request: Request,
    db: AsyncSession = Depends(get_session),
    user: User = Depends(require_steward),  # Require steward role for policy changes
):
    """Create or update a field mapping policy"""
    service = PolicyService(db)
    audit_service = AuditService(db)
    client_ip = request.client.host if request.client else None

    try:
        policy = await service.create_or_update_policy(
            body.dict(), str(user.account_id), str(user.id)
        )

        # Log audit event for policy change
        await audit_service.log_action(
            actor=user,
            action="policy_change",
            object_type=policy.salesforce_object,
            record_ids=[policy.id],
            changes={
                "policy": {
                    "version": {
                        "before": policy.version - 1 if policy.version > 1 else None,
                        "after": policy.version,
                    },
                    "rules": {"after": body.matching_rules},
                }
            },
            policy_hash=policy.policy_hash,
            request_ip=client_ip,
        )

        return {
            "id": policy.id,
            "version": policy.version,
            "hash": policy.policy_hash,
            "message": f"Policy v{policy.version} created for {policy.salesforce_object}",
        }
    except ValueError as e:
        raise HTTPException(400, str(e))


@router.get("/{salesforce_object}/fields")
async def get_available_fields(
    salesforce_object: str,
    db: AsyncSession = Depends(get_session),
    user: User = Depends(require_viewer),
    sf: SalesforceGateway = Depends(get_salesforce_gateway),
):
    """Get available fields for a Salesforce object"""
    # Get fields via gateway
    fields = await field_metadata_cache.get_fields(sf, salesforce_object)

    # Format for frontend
    return {
        "object": salesforce_object,
        "fields": [
            {
                "name": name,
                "label": info["label"],
                "type": info["type"],
                "updateable": info["updateable"],
                "algorithms": _get_compatible_algorithms(info["type"]),
            }
            for name, info in fields.items()
        ],
    }


def _get_compatible_algorithms(field_type: str) -> List[str]:
    """Get compatible matching algorithms for a field type"""
    if field_type in ["id", "reference"]:
        return ["exact"]
    elif field_type == "email":
        return ["exact", "domain", "fuzzy"]
    elif field_type == "phone":
        return ["exact", "fuzzy"]
    elif field_type in ["url", "string"]:
        return ["exact", "fuzzy", "domain", "token_set"]
    elif field_type in ["double", "currency", "percent", "int"]:
        return ["exact", "range"]
    else:
        return ["exact"]


@router.post("/test")
async def test_policy(
    body: TestPolicyRequest,
    db: AsyncSession = Depends(get_session),
    user: User = Depends(require_viewer),
):
    """Test a policy on sample data"""
    # This would fetch sample records and run matching simulation
    # For MVP, return mock results
    return {
        "sample_size": body.sample_size,
        "results": {
            "duplicates_found": 3,
            "link_candidates": 5,
            "confidence_distribution": {"high": 2, "medium": 4, "low": 2},
        },
        "examples": [
            {
                "type": "duplicate",
                "records": ["John Smith", "John Smyth"],
                "confidence": 0.92,
                "explanation": "Name similarity: 92%, Same phone number",
            }
        ],
    }
