"""
ID Wispera Passport Management
CRUD operations for passports
"""

from datetime import datetime, timedelta
from typing import Optional

from id_wispera.types import (
    Passport,
    PassportStatus,
    VisaType,
    CredentialType,
    Platform,
    CreatePassportInput,
    AuditAction,
)
from id_wispera.vault import Vault
from id_wispera.audit import log_action


# Days before expiry to mark as "expiring"
EXPIRING_THRESHOLD_DAYS = 14


def create_passport(vault: Vault, input_data: CreatePassportInput) -> Passport:
    """
    Create a new passport in the vault.

    Args:
        vault: The unlocked vault
        input_data: Passport creation input

    Returns:
        The created Passport
    """
    now = datetime.utcnow()

    passport = Passport(
        name=input_data.name,
        agent_id=input_data.agent_id,
        credential_type=input_data.credential_type,
        credential_value=input_data.credential_value,
        visa_type=input_data.visa_type,
        issuing_authority=input_data.issuing_authority or "Manual",
        scope=input_data.scope,
        platforms=input_data.platforms,
        status=PassportStatus.ACTIVE,
        issued_at=now,
        valid_from=input_data.valid_from or now,
        valid_until=input_data.valid_until,
        delegation_chain=input_data.delegation_chain,
        human_owner=input_data.human_owner,
        tags=input_data.tags,
        notes=input_data.notes,
        created_at=now,
        updated_at=now,
    )

    # Update status based on validity
    passport = _update_status(passport)

    vault.add_passport(passport)

    # Log creation
    log_action(
        vault,
        passport_id=passport.id,
        action=AuditAction.CREATED,
        actor=input_data.human_owner,
        details=f"Created passport: {passport.name}",
    )

    return passport


def get_passport(vault: Vault, passport_id: str) -> Passport:
    """
    Get a passport by ID.

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

    Returns:
        The Passport

    Raises:
        ValueError: If passport not found
    """
    passport = vault.get_passport(passport_id)
    if passport is None:
        raise ValueError(f"Passport not found: {passport_id}")

    # Update status in case it expired
    updated = _update_status(passport)
    if updated.status != passport.status:
        vault.update_passport(updated)
        passport = updated

    return passport


def list_passports(
    vault: Vault,
    *,
    status: Optional[PassportStatus] = None,
    visa_type: Optional[VisaType] = None,
    platform: Optional[Platform] = None,
    credential_type: Optional[CredentialType] = None,
    human_owner: Optional[str] = None,
    tags: Optional[list[str]] = None,
    search: Optional[str] = None,
) -> list[Passport]:
    """
    List passports with optional filters.

    Args:
        vault: The unlocked vault
        status: Filter by status
        visa_type: Filter by visa type
        platform: Filter by platform
        credential_type: Filter by credential type
        human_owner: Filter by human owner
        tags: Filter by tags (any match)
        search: Search in name and notes

    Returns:
        List of matching Passports
    """
    passports = vault.list_passports()

    # Update statuses
    updated_passports = []
    for p in passports:
        updated = _update_status(p)
        if updated.status != p.status:
            vault.update_passport(updated)
        updated_passports.append(updated)

    # Apply filters
    result = updated_passports

    if status is not None:
        result = [p for p in result if p.status == status]

    if visa_type is not None:
        result = [p for p in result if p.visa_type == visa_type]

    if platform is not None:
        result = [p for p in result if platform in p.platforms]

    if credential_type is not None:
        result = [p for p in result if p.credential_type == credential_type]

    if human_owner is not None:
        result = [p for p in result if p.human_owner == human_owner]

    if tags:
        result = [p for p in result if any(t in p.tags for t in tags)]

    if search:
        search_lower = search.lower()
        result = [
            p for p in result
            if search_lower in p.name.lower() or
               (p.notes and search_lower in p.notes.lower())
        ]

    return result


def update_passport(
    vault: Vault,
    passport_id: str,
    *,
    name: Optional[str] = None,
    scope: Optional[list[str]] = None,
    platforms: Optional[list[Platform]] = None,
    valid_until: Optional[datetime] = None,
    tags: Optional[list[str]] = None,
    notes: Optional[str] = None,
    actor: str = "system",
) -> Passport:
    """
    Update a passport.

    Args:
        vault: The unlocked vault
        passport_id: The passport ID
        name: New name
        scope: New scope
        platforms: New platforms
        valid_until: New expiration
        tags: New tags
        notes: New notes
        actor: Who is making the update

    Returns:
        The updated Passport
    """
    passport = get_passport(vault, passport_id)

    if name is not None:
        passport.name = name
    if scope is not None:
        passport.scope = scope
    if platforms is not None:
        passport.platforms = platforms
    if valid_until is not None:
        passport.valid_until = valid_until
    if tags is not None:
        passport.tags = tags
    if notes is not None:
        passport.notes = notes

    passport.updated_at = datetime.utcnow()
    passport = _update_status(passport)

    vault.update_passport(passport)

    log_action(
        vault,
        passport_id=passport.id,
        action=AuditAction.MODIFIED,
        actor=actor,
        details=f"Updated passport: {passport.name}",
    )

    return passport


def revoke_passport(
    vault: Vault,
    passport_id: str,
    *,
    reason: str,
    actor: str,
) -> Passport:
    """
    Revoke a passport.

    Args:
        vault: The unlocked vault
        passport_id: The passport ID
        reason: Revocation reason
        actor: Who is revoking

    Returns:
        The revoked Passport
    """
    passport = get_passport(vault, passport_id)

    if passport.status == PassportStatus.REVOKED:
        raise ValueError("Passport is already revoked")

    passport.status = PassportStatus.REVOKED
    passport.revoked_at = datetime.utcnow()
    passport.revoked_by = actor
    passport.revoked_reason = reason
    passport.updated_at = datetime.utcnow()

    vault.update_passport(passport)

    log_action(
        vault,
        passport_id=passport.id,
        action=AuditAction.REVOKED,
        actor=actor,
        details=f"Revoked: {reason}",
    )

    return passport


def _update_status(passport: Passport) -> Passport:
    """Update passport status based on validity dates"""
    if passport.status == PassportStatus.REVOKED:
        return passport

    now = datetime.utcnow()

    if passport.valid_until:
        if passport.valid_until <= now:
            passport.status = PassportStatus.EXPIRED
        elif passport.valid_until <= now + timedelta(days=EXPIRING_THRESHOLD_DAYS):
            passport.status = PassportStatus.EXPIRING
        else:
            passport.status = PassportStatus.ACTIVE
    else:
        passport.status = PassportStatus.ACTIVE

    return passport
