"""
ID Wispera Audit Logging
Track all credential access and modifications
"""

from datetime import datetime, timedelta
from typing import Optional

from id_wispera.types import AuditEntry, AuditAction, Platform
from id_wispera.vault import Vault


def log_action(
    vault: Vault,
    *,
    passport_id: str,
    action: AuditAction,
    actor: str,
    platform: Optional[Platform] = None,
    details: Optional[str] = None,
    metadata: Optional[dict] = None,
) -> AuditEntry:
    """
    Log an action to the audit trail.

    Args:
        vault: The unlocked vault
        passport_id: The passport this action relates to
        action: The action performed
        actor: Who performed the action
        platform: Which platform (optional)
        details: Human-readable description
        metadata: Additional structured data

    Returns:
        The created AuditEntry
    """
    entry = AuditEntry(
        passport_id=passport_id,
        action=action,
        actor=actor,
        timestamp=datetime.utcnow(),
        platform=platform,
        details=details,
        metadata=metadata,
    )

    vault.add_audit_entry(entry)
    return entry


def get_audit_log(
    vault: Vault,
    *,
    passport_id: Optional[str] = None,
    action: Optional[AuditAction] = None,
    actor: Optional[str] = None,
    platform: Optional[Platform] = None,
    start_date: Optional[datetime] = None,
    end_date: Optional[datetime] = None,
    limit: Optional[int] = None,
    offset: int = 0,
) -> list[AuditEntry]:
    """
    Query the audit log with filters.

    Args:
        vault: The unlocked vault
        passport_id: Filter by passport ID
        action: Filter by action type
        actor: Filter by actor
        platform: Filter by platform
        start_date: Filter by start date
        end_date: Filter by end date
        limit: Maximum entries to return
        offset: Number of entries to skip

    Returns:
        List of matching AuditEntry objects
    """
    entries = vault.get_audit_log(passport_id)

    # Apply filters
    if action is not None:
        entries = [e for e in entries if e.action == action]

    if actor is not None:
        entries = [e for e in entries if e.actor == actor]

    if platform is not None:
        entries = [e for e in entries if e.platform == platform]

    if start_date is not None:
        entries = [e for e in entries if e.timestamp >= start_date]

    if end_date is not None:
        entries = [e for e in entries if e.timestamp <= end_date]

    # Sort by timestamp descending (most recent first)
    entries.sort(key=lambda e: e.timestamp, reverse=True)

    # Apply pagination
    if offset > 0:
        entries = entries[offset:]

    if limit is not None:
        entries = entries[:limit]

    return entries


def get_passport_history(vault: Vault, passport_id: str) -> list[AuditEntry]:
    """
    Get the complete history of a passport.

    Args:
        vault: The unlocked vault
        passport_id: The passport ID

    Returns:
        List of AuditEntry objects in chronological order
    """
    entries = vault.get_audit_log(passport_id)
    entries.sort(key=lambda e: e.timestamp)
    return entries


def get_recent_activity(
    vault: Vault,
    limit: int = 50,
) -> list[AuditEntry]:
    """
    Get recent audit activity across all passports.

    Args:
        vault: The unlocked vault
        limit: Maximum entries to return

    Returns:
        List of recent AuditEntry objects
    """
    return get_audit_log(vault, limit=limit)


# ============================================================================
# Audit Export
# ============================================================================

def export_audit_log(
    vault: Vault,
    format: str = "json",
    *,
    passport_id: Optional[str] = None,
    action: Optional[AuditAction] = None,
    actor: Optional[str] = None,
    platform: Optional[Platform] = None,
    start_date: Optional[datetime] = None,
    end_date: Optional[datetime] = None,
) -> str:
    """
    Export audit log to a file format.

    Args:
        vault: The unlocked vault
        format: Output format ("json" or "csv")
        passport_id: Filter by passport ID
        action: Filter by action type
        actor: Filter by actor
        platform: Filter by platform
        start_date: Filter by start date
        end_date: Filter by end date

    Returns:
        Formatted string (JSON or CSV)
    """
    import json as json_module

    entries = get_audit_log(
        vault,
        passport_id=passport_id,
        action=action,
        actor=actor,
        platform=platform,
        start_date=start_date,
        end_date=end_date,
    )

    if format == "json":
        return json_module.dumps(
            {
                "auditLog": [
                    e.model_dump(by_alias=True, mode="json") for e in entries
                ],
                "exportedAt": datetime.utcnow().isoformat(),
                "totalEntries": len(entries),
            },
            indent=2,
            default=str,
        )

    # CSV format
    headers = ["id", "passportId", "action", "actor", "timestamp", "platform", "details"]
    rows = []
    for e in entries:
        row = [
            e.id,
            e.passport_id,
            e.action if isinstance(e.action, str) else e.action.value,
            e.actor,
            e.timestamp.isoformat() if isinstance(e.timestamp, datetime) else str(e.timestamp),
            (e.platform if isinstance(e.platform, str) else e.platform.value) if e.platform else "",
            (e.details or "").replace(",", ";").replace("\n", " "),
        ]
        rows.append(",".join(f'"{v}"' for v in row))

    return "\n".join([",".join(headers)] + rows)


# ============================================================================
# Audit Statistics
# ============================================================================

def get_audit_stats(
    vault: Vault,
    passport_id: Optional[str] = None,
) -> dict:
    """
    Get audit statistics for a passport or all passports.

    Args:
        vault: The unlocked vault
        passport_id: Optional passport ID to filter by

    Returns:
        Dict with totalActions, byAction, byActor, byPlatform,
        firstAction, lastAction, actionsLast24h, actionsLast7d
    """
    entries = get_audit_log(vault, passport_id=passport_id)

    by_action: dict[str, int] = {
        "created": 0,
        "viewed": 0,
        "shared": 0,
        "modified": 0,
        "revoked": 0,
        "renewed": 0,
        "accessed": 0,
        "policy-checked": 0,
        "detected": 0,
        "exported": 0,
        "imported": 0,
    }
    by_actor: dict[str, int] = {}
    by_platform: dict[str, int] = {}

    now = datetime.utcnow()
    one_day_ago = now - timedelta(hours=24)
    seven_days_ago = now - timedelta(days=7)

    actions_last_24h = 0
    actions_last_7d = 0

    for entry in entries:
        action_val = entry.action if isinstance(entry.action, str) else entry.action.value
        by_action[action_val] = by_action.get(action_val, 0) + 1
        by_actor[entry.actor] = by_actor.get(entry.actor, 0) + 1

        if entry.platform:
            platform_val = entry.platform if isinstance(entry.platform, str) else entry.platform.value
            by_platform[platform_val] = by_platform.get(platform_val, 0) + 1

        entry_date = entry.timestamp if isinstance(entry.timestamp, datetime) else datetime.fromisoformat(str(entry.timestamp))
        if entry_date >= one_day_ago:
            actions_last_24h += 1
        if entry_date >= seven_days_ago:
            actions_last_7d += 1

    # Sort entries by timestamp
    sorted_entries = sorted(
        entries,
        key=lambda e: e.timestamp if isinstance(e.timestamp, datetime) else datetime.fromisoformat(str(e.timestamp)),
    )

    return {
        "totalActions": len(entries),
        "byAction": by_action,
        "byActor": by_actor,
        "byPlatform": by_platform,
        "firstAction": sorted_entries[0] if sorted_entries else None,
        "lastAction": sorted_entries[-1] if sorted_entries else None,
        "actionsLast24h": actions_last_24h,
        "actionsLast7d": actions_last_7d,
    }


# ============================================================================
# Access History
# ============================================================================

def get_access_history(
    vault: Vault,
    passport_id: str,
) -> list[AuditEntry]:
    """
    Get access history for a specific passport.

    Args:
        vault: The unlocked vault
        passport_id: The passport ID

    Returns:
        List of AuditEntry objects with 'accessed' action
    """
    return get_audit_log(vault, passport_id=passport_id, action=AuditAction.ACCESSED)


def was_accessed_recently(
    vault: Vault,
    passport_id: str,
    within_minutes: int = 5,
) -> bool:
    """
    Check if a passport has been accessed recently.

    Args:
        vault: The unlocked vault
        passport_id: The passport ID
        within_minutes: Time window in minutes

    Returns:
        True if accessed within the time window
    """
    entries = get_audit_log(vault, passport_id=passport_id, action=AuditAction.ACCESSED)
    if not entries:
        return False

    most_recent = entries[0]
    threshold = datetime.utcnow() - timedelta(minutes=within_minutes)
    entry_time = most_recent.timestamp if isinstance(most_recent.timestamp, datetime) else datetime.fromisoformat(str(most_recent.timestamp))
    return entry_time >= threshold


# ============================================================================
# Audit Search
# ============================================================================

def search_audit_log(
    vault: Vault,
    search_term: str,
    passport_id: Optional[str] = None,
) -> list[AuditEntry]:
    """
    Search audit log by text in details, actor, action, or platform.

    Args:
        vault: The unlocked vault
        search_term: Text to search for
        passport_id: Optional passport ID to filter by

    Returns:
        List of matching AuditEntry objects
    """
    entries = get_audit_log(vault, passport_id=passport_id)
    term = search_term.lower()

    results = []
    for e in entries:
        if e.details and term in e.details.lower():
            results.append(e)
            continue
        if term in e.actor.lower():
            results.append(e)
            continue
        action_val = e.action if isinstance(e.action, str) else e.action.value
        if term in action_val.lower():
            results.append(e)
            continue
        if e.platform:
            platform_val = e.platform if isinstance(e.platform, str) else e.platform.value
            if term in platform_val.lower():
                results.append(e)
                continue

    return results


# ============================================================================
# Audit Alerting Helpers
# ============================================================================

def get_suspicious_activity(
    vault: Vault,
    passport_id: Optional[str] = None,
) -> dict:
    """
    Get suspicious activity patterns.
    Returns entries that might indicate security concerns.

    Args:
        vault: The unlocked vault
        passport_id: Optional passport ID to filter by

    Returns:
        Dict with rapidAccess, unusualActors, afterHoursAccess
    """
    entries = get_audit_log(vault, passport_id=passport_id)
    access_entries = [
        e for e in entries
        if (e.action if isinstance(e.action, str) else e.action.value) == "accessed"
    ]

    # Rapid access: multiple accesses within 1 minute
    rapid_access: list[AuditEntry] = []
    for i in range(1, len(access_entries)):
        current = access_entries[i]
        previous = access_entries[i - 1]
        current_time = current.timestamp if isinstance(current.timestamp, datetime) else datetime.fromisoformat(str(current.timestamp))
        previous_time = previous.timestamp if isinstance(previous.timestamp, datetime) else datetime.fromisoformat(str(previous.timestamp))
        time_diff = abs((previous_time - current_time).total_seconds())
        if time_diff < 60:
            if previous not in rapid_access:
                rapid_access.append(previous)
            rapid_access.append(current)

    # Unusual actors: actors that appear only once
    actor_counts: dict[str, int] = {}
    for entry in access_entries:
        actor_counts[entry.actor] = actor_counts.get(entry.actor, 0) + 1

    unusual_actors = [
        e for e in access_entries
        if actor_counts.get(e.actor, 0) == 1
    ]

    # After hours access (between 10 PM and 6 AM)
    after_hours_access: list[AuditEntry] = []
    for e in access_entries:
        entry_time = e.timestamp if isinstance(e.timestamp, datetime) else datetime.fromisoformat(str(e.timestamp))
        hour = entry_time.hour
        if hour >= 22 or hour < 6:
            after_hours_access.append(e)

    return {
        "rapidAccess": rapid_access,
        "unusualActors": unusual_actors,
        "afterHoursAccess": after_hours_access,
    }
