"""
Provider Locker API endpoints for BYO Data Provider credentials.

Requires Unleashed tier. Workspace admin role required for write operations.
"""

from __future__ import annotations

import asyncio
from datetime import datetime, timezone
from typing import Any, Dict, Optional

from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
import httpx
import logging

from ...auth_core import require_org_context, OrgContext
from ...db import get_session
from ...middleware.quota_checker import get_quota, TIER_LIMITS
from ...models import SecretAuditAction
from ...services.secret_storage import (
    get_saas_secret_backend,
    BYO_SUPPORTED_INTEGRATIONS,
)

log = logging.getLogger(__name__)

router = APIRouter(prefix="/enrichment/locker", tags=["Provider Locker"])


# =============================================================================
# Request/Response Models
# =============================================================================


class AddProviderRequest(BaseModel):
    """Request to add/update a provider credential."""
    provider: str = Field(..., description="Provider name (e.g., 'fullenrich', 'apify', 'pdl')")
    api_key: str = Field(..., description="API key or token (full value)")
    workspace_id: Optional[str] = Field(
        None,
        description="Optional workspace ID for providers that require it (e.g., HeyReach)",
    )


class ProviderStatus(BaseModel):
    """Status of a single provider."""
    provider: str
    status: str  # "connected", "not_connected", "available" (for foundrygraph)
    key_suffix: Optional[str] = None
    connected_at: Optional[str] = None
    last_validated_at: Optional[str] = None


class LockerListResponse(BaseModel):
    """Response for listing configured providers."""
    ok: bool = True
    data: Dict[str, Any]


class LockerAddResponse(BaseModel):
    """Response for adding a provider credential."""
    ok: bool = True
    data: ProviderStatus


class LockerDeleteResponse(BaseModel):
    """Response for deleting a provider credential."""
    ok: bool = True
    message: str


# =============================================================================
# Helper Functions
# =============================================================================


async def require_unleashed_tier(
    org: OrgContext,
    db: AsyncSession,
) -> None:
    """
    Verify the account has Unleashed tier with BYO enrichment enabled.
    Raises HTTPException 403 if not eligible.
    """
    quota = await get_quota(org.account_id, db)
    tier = quota.tier.lower()

    # Check tier limits for BYO enrichment
    limits = TIER_LIMITS.get(tier, TIER_LIMITS.get("free", {}))
    if not limits.get("byo_enrichment_enabled", False):
        raise HTTPException(
            status_code=403,
            detail={
                "error": "TIER_REQUIRED",
                "message": "BYO Data Provider Locker requires Unleashed tier",
                "current_tier": tier,
                "required_tier": "unleashed",
                "upgrade_url": "/pricing",
            },
        )


async def require_workspace_admin(
    org: OrgContext,
    db: AsyncSession,
) -> None:
    """
    Verify the user is a workspace admin.

    Security Model (MVP):
    - Currently allows all authenticated workspace users to manage credentials
    - Future: Check user role against workspace membership table

    When STRICT_LOCKER_ADMIN is set to true in settings, this will enforce
    that only workspace admins can manage credentials.
    """
    from ... import settings as saas_settings

    # Log all credential management attempts for audit trail
    log.info(
        "locker_admin_access",
        extra={
            "workspace_id": org.account_id,
            "user_id": org.user_id,
            "email": org.email,
            "action": "credential_management",
        },
    )

    # Check if strict admin mode is enabled
    strict_mode = getattr(saas_settings, "STRICT_LOCKER_ADMIN", False)
    if not strict_mode:
        # MVP: Allow all authenticated workspace users
        return

    # Future: Query workspace membership table to check role
    # For now, if strict mode is enabled without role system, reject all
    raise HTTPException(
        status_code=403,
        detail={
            "error": "ADMIN_REQUIRED",
            "message": "Only workspace administrators can manage API credentials",
        },
    )


def get_client_info(request: Request) -> Dict[str, Optional[str]]:
    """Extract client IP and user agent from request."""
    ip = None
    if request.client:
        ip = request.client.host

    user_agent = request.headers.get("User-Agent")

    return {
        "ip_address": ip,
        "user_agent": user_agent,
    }


# =============================================================================
# API Endpoints
# =============================================================================


@router.get("", response_model=LockerListResponse)
async def list_providers(
    org: OrgContext = Depends(require_org_context),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    List all BYO providers and their connection status.

    Returns providers available for this workspace's tier, including:
    - fullenrich: BYO API key (Unleashed only)
    - pdl: BYO API key (Unleashed only)
    - apify: BYO API token (coming soon)
    - foundrygraph: Always available (internal service)

    Authentication: Bearer token required
    Tier: Unleashed required for BYO providers
    """
    await require_unleashed_tier(org, db)

    backend = get_saas_secret_backend()
    statuses = await backend.get_provider_status(db, org.account_id)
    statuses = [item for item in statuses if item.get("provider") != "justcall"]

    # Get tier info
    quota = await get_quota(org.account_id, db)
    limits = TIER_LIMITS.get(quota.tier.lower(), {})

    return {
        "ok": True,
        "data": {
            "providers": statuses,
            "tier": quota.tier,
            "byo_enabled": limits.get("byo_enrichment_enabled", False),
            "supported_providers": [
                provider
                for provider in sorted(BYO_SUPPORTED_INTEGRATIONS)
                if provider != "justcall"
            ],
        },
    }


@router.post("", response_model=LockerAddResponse)
async def add_provider(
    request_body: AddProviderRequest,
    request: Request,
    org: OrgContext = Depends(require_org_context),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    Add or update a BYO provider credential.

    The API key is encrypted using KMS envelope encryption before storage.
    Only the last 4 characters are stored for display purposes.

    Request body:
    - provider: Provider name ('fullenrich', 'apify', or 'pdl')
    - api_key: Full API key (will be encrypted)

    Authentication: Bearer token required
    Tier: Unleashed required
    Role: Workspace admin required (future)
    """
    await require_unleashed_tier(org, db)
    await require_workspace_admin(org, db)

    provider = request_body.provider.lower()
    api_key = request_body.api_key
    workspace_id = request_body.workspace_id

    # Validate provider is supported
    if provider == "justcall":
        raise HTTPException(
            status_code=400,
            detail={
                "error": "JUSTCALL_USE_V2",
                "message": "JustCall credentials must be managed via /api/v2/justcall/credentials.",
            },
        )
    if provider not in BYO_SUPPORTED_INTEGRATIONS:
        raise HTTPException(
            status_code=400,
            detail={
                "error": "INVALID_PROVIDER",
                "message": f"Provider '{provider}' is not supported for BYO credentials",
                "supported_providers": sorted(BYO_SUPPORTED_INTEGRATIONS),
            },
        )
    if provider == "apollo":
        raise HTTPException(
            status_code=400,
            detail={
                "error": "OAUTH_ONLY",
                "message": "Apollo connections must be authorized via OAuth.",
            },
        )

    # Validate API key is not empty
    if not api_key or not api_key.strip():
        raise HTTPException(
            status_code=400,
            detail={
                "error": "INVALID_API_KEY",
                "message": "API key cannot be empty",
            },
        )

    # Determine key name based on provider
    key_name = "api_token" if provider == "apify" else "api_key"
    terms_accepted_at = datetime.now(timezone.utc) if provider == "pdl" else None

    backend = get_saas_secret_backend()

    # Check if this is an add or replace
    existing_secrets = await backend.list_secrets(db, org.account_id, provider)
    is_replace = len(existing_secrets) > 0
    action = SecretAuditAction.REPLACED if is_replace else SecretAuditAction.ADDED

    try:
        # TODO: Validate the API key with the provider before saving
        # For now, we trust the user's input and mark as unvalidated
        validated_at = None  # Will be set after validation

        # Store the encrypted secret
        record = await backend.set_secret(
            db=db,
            integration=provider,
            key=key_name,
            value=api_key.strip(),
            workspace_id=org.account_id,
            created_by=org.email,
            validated_at=validated_at,
            terms_accepted_at=terms_accepted_at,
        )
        if provider == "heyreach" and workspace_id:
            await backend.set_secret(
                db=db,
                integration=provider,
                key="workspace_id",
                value=workspace_id.strip(),
                workspace_id=org.account_id,
                created_by=org.email,
                validated_at=validated_at,
            )

        # Log audit entry
        client_info = get_client_info(request)
        await backend.log_audit(
            db=db,
            workspace_id=org.account_id,
            integration=provider,
            action=action,
            actor_email=org.email,
            actor_user_id=org.user_id,
            ip_address=client_info.get("ip_address"),
            user_agent=client_info.get("user_agent"),
        )

        await db.commit()

        log.info(
            f"[LOCKER] {action.value} credential for {provider} in workspace {org.account_id}"
        )

        return {
            "ok": True,
            "data": {
                "provider": provider,
                "status": "connected",
                "key_suffix": record.key_suffix,
                "connected_at": record.created_at.isoformat() if record.created_at else None,
                "last_validated_at": None,  # TODO: Implement validation
            },
        }

    except Exception as e:
        await db.rollback()
        log.error(
            f"[LOCKER] Failed to add credential for {provider} in workspace {org.account_id}: {e}",
            exc_info=True,
        )
        raise HTTPException(
            status_code=500,
            detail={
                "error": "STORAGE_ERROR",
                "message": "Failed to store credential. Please try again.",
            },
        )


@router.delete("/{provider}", response_model=LockerDeleteResponse)
async def delete_provider(
    provider: str,
    request: Request,
    org: OrgContext = Depends(require_org_context),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    Remove a BYO provider credential.

    The credential is permanently deleted. This action is logged in the audit trail.

    Path parameters:
    - provider: Provider name to disconnect ('fullenrich', 'apify', or 'pdl')

    Authentication: Bearer token required
    Tier: Unleashed required
    Role: Workspace admin required (future)
    """
    await require_unleashed_tier(org, db)
    await require_workspace_admin(org, db)

    provider = provider.lower()

    # Validate provider
    if provider == "justcall":
        raise HTTPException(
            status_code=400,
            detail={
                "error": "JUSTCALL_USE_V2",
                "message": "JustCall credentials must be managed via /api/v2/justcall/credentials.",
            },
        )
    if provider not in BYO_SUPPORTED_INTEGRATIONS:
        raise HTTPException(
            status_code=400,
            detail={
                "error": "INVALID_PROVIDER",
                "message": f"Provider '{provider}' is not supported",
            },
        )
    if provider == "apollo":
        raise HTTPException(
            status_code=400,
            detail={
                "error": "OAUTH_ONLY",
                "message": "Apollo must be disconnected via OAuth settings.",
            },
        )

    key_name = "api_token" if provider == "apify" else "api_key"

    backend = get_saas_secret_backend()

    try:
        # Delete the secret
        deleted = await backend.delete_secret(
            db=db,
            integration=provider,
            key=key_name,
            workspace_id=org.account_id,
        )
        if provider == "heyreach":
            await backend.delete_secret(
                db=db,
                integration=provider,
                key="workspace_id",
                workspace_id=org.account_id,
            )

        if not deleted:
            raise HTTPException(
                status_code=404,
                detail={
                    "error": "NOT_FOUND",
                    "message": f"No credential found for provider '{provider}'",
                },
            )

        # Log audit entry
        client_info = get_client_info(request)
        await backend.log_audit(
            db=db,
            workspace_id=org.account_id,
            integration=provider,
            action=SecretAuditAction.REMOVED,
            actor_email=org.email,
            actor_user_id=org.user_id,
            ip_address=client_info.get("ip_address"),
            user_agent=client_info.get("user_agent"),
        )

        await db.commit()

        log.info(
            f"[LOCKER] Removed credential for {provider} in workspace {org.account_id}"
        )

        return {
            "ok": True,
            "message": f"Provider {provider} disconnected",
        }

    except HTTPException:
        raise
    except Exception as e:
        await db.rollback()
        log.error(
            f"[LOCKER] Failed to delete credential for {provider} in workspace {org.account_id}: {e}",
            exc_info=True,
        )
        raise HTTPException(
            status_code=500,
            detail={
                "error": "STORAGE_ERROR",
                "message": "Failed to remove credential. Please try again.",
            },
        )


@router.post("/{provider}/validate")
async def validate_provider(
    provider: str,
    request: Request,
    org: OrgContext = Depends(require_org_context),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    Validate a stored provider credential by making a test API call.

    This endpoint tests the stored API key against the provider's API
    and updates the last_validated_at timestamp if successful.

    Path parameters:
    - provider: Provider name to validate ('fullenrich', 'apify', or 'pdl')

    Authentication: Bearer token required
    Tier: Unleashed required
    """
    await require_unleashed_tier(org, db)

    provider = provider.lower()

    if provider not in BYO_SUPPORTED_INTEGRATIONS:
        raise HTTPException(
            status_code=400,
            detail={
                "error": "INVALID_PROVIDER",
                "message": f"Provider '{provider}' is not supported",
            },
        )
    if provider == "justcall":
        raise HTTPException(
            status_code=400,
            detail={
                "error": "JUSTCALL_USE_V2",
                "message": "JustCall credentials must be managed via /api/v2/justcall/credentials.",
            },
        )

    key_name = "api_token" if provider == "apify" else "api_key"

    backend = get_saas_secret_backend()

    # Get the secret
    secret = await backend.get_secret(
        db=db,
        integration=provider,
        key=key_name,
        workspace_id=org.account_id,
    )

    if secret is None:
        raise HTTPException(
            status_code=404,
            detail={
                "error": "NOT_FOUND",
                "message": f"No credential found for provider '{provider}'",
            },
        )

    # TODO: Implement actual provider validation
    # For now, just mark as validated
    validation_error = None
    is_valid = True

    if provider == "fullenrich":
        # TODO: Call FullEnrich validation endpoint
        pass
    elif provider == "apify":
        # TODO: Call Apify user endpoint
        pass
    elif provider == "amplemarket":
        from g_gremlin.amplemarket import (
            AmplemarketAuthError,
            AmplemarketClient,
            AmplemarketError,
        )

        client: Optional[AmplemarketClient] = None
        try:
            client = AmplemarketClient(api_key=str(secret.value))
            await asyncio.to_thread(client.whoami)
            is_valid = True
        except AmplemarketAuthError:
            is_valid = False
            validation_error = "Invalid API key"
        except AmplemarketError as exc:
            is_valid = False
            validation_error = f"Amplemarket error: {exc}"
        except Exception as exc:  # noqa: BLE001
            is_valid = False
            validation_error = f"Validation failed: {exc}"
        finally:
            # AmplemarketClient does not expose a public close(); close its session if present.
            try:
                if client is not None and getattr(client, "_session", None) is not None:
                    client._session.close()
            except Exception:
                pass
    elif provider == "pdl":
        from fmatch.saas.integrations.people_data_labs import PeopleDataLabsAdapter

        try:
            adapter = PeopleDataLabsAdapter(api_key=secret.value, timeout=5.0)
            result = await asyncio.to_thread(
                adapter.search_companies,
                "SELECT COUNT(*) FROM company WHERE website='example.com'",
                None,
                0,
            )
            is_valid = isinstance(result, dict) and "error" not in result
            if not is_valid:
                err = result.get("error", "") if isinstance(result, dict) else ""
                if "401" in str(err) or "Unauthorized" in str(err):
                    validation_error = "Invalid API key"
                else:
                    validation_error = f"PDL error: {err}"
        except httpx.TimeoutException:
            is_valid = False
            validation_error = "PDL service unavailable (timeout)"
        except Exception as e:
            is_valid = False
            validation_error = f"Validation failed: {str(e)}"

    if is_valid:
        await backend.mark_validated(
            db=db,
            integration=provider,
            key=key_name,
            workspace_id=org.account_id,
        )

        # Log audit
        client_info = get_client_info(request)
        await backend.log_audit(
            db=db,
            workspace_id=org.account_id,
            integration=provider,
            action=SecretAuditAction.VALIDATED,
            actor_email=org.email,
            actor_user_id=org.user_id,
            ip_address=client_info.get("ip_address"),
            user_agent=client_info.get("user_agent"),
        )

        await db.commit()

        return {
            "ok": True,
            "data": {
                "provider": provider,
                "status": "connected",
                "validated_at": datetime.now(timezone.utc).isoformat(),
            },
        }
    else:
        return {
            "ok": False,
            "error": "VALIDATION_FAILED",
            "message": validation_error or "API key validation failed",
            "code": "INVALID_CREDENTIALS",
        }
