"""
License Management API Routes

Provides endpoints for:
- Getting license status
- Refreshing license validation
- Setting offline license file
- Checking feature availability
- Recording license purchases with price snapshots
- Retrieving purchase history
"""

from fastapi import APIRouter, HTTPException, Depends, UploadFile, File
from pydantic import BaseModel
from typing import Dict, Any, Optional, List
from datetime import datetime, timezone
import logging
import shutil
from pathlib import Path
import uuid

from sqlmodel import select

from app.auth import get_current_user
from app.database import SessionDep
from app.models import LicenseRecord, LicenseRecordCreate, LicenseRecordRead, LicenseStatus, BillingInterval
from app.services.license_validator import LicenseValidator, LicenseValidationError
from app.services.email_service import EmailService
from app.services.device_manager import DeviceManager
from app.config.pricing import get_all_tiers_for_display, get_tier, create_purchase_snapshot

logger = logging.getLogger(__name__)

router = APIRouter(tags=["license"])


class LicenseKeyRequest(BaseModel):
    """Request to set a new license key"""
    license_key: str


class LicenseRevocationRequest(BaseModel):
    """Request to revoke a license"""
    license_key: str
    reason: str = "refund_issued"


class DeviceTransferRequest(BaseModel):
    """Request to transfer license to new device"""
    license_key: str
    reason: str = "hardware_change"


class FeatureCheckRequest(BaseModel):
    """Request to check if a feature is enabled"""
    feature_name: str


class OfflineLicenseRequest(BaseModel):
    """Request to set offline license file path"""
    file_path: str


@router.get("/license/status")
async def get_license_status(
    force: bool = False,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    try:
        if force:
            status = LicenseValidator.get_status_fresh()
        else:
            status = LicenseValidator.get_status()
        return {"success": True, "license": status}
    except Exception as e:
        logger.error(f"Failed to get license status: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to retrieve license status: {str(e)}")



@router.post("/license/revoke")
async def revoke_license(
    request: LicenseRevocationRequest,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Revoke a license (for refunds, violations, etc.).
    
    This immediately invalidates the license across all devices.
    Used for refunds, policy violations, or security incidents.
    """
    try:
        result = DeviceManager.revoke_license(
            license_key=request.license_key,
            reason=request.reason
        )
        
        if result["success"]:
            # Send revocation notification email
            try:
                EmailService.send_license_revocation_notification(
                    license_key=request.license_key,
                    reason=request.reason
                )
            except Exception as e:
                logger.warning(f"Failed to send revocation notification: {e}")
            
            return {
                "success": True,
                "message": result["message"]
            }
        else:
            raise HTTPException(
                status_code=400,
                detail=result["error"]
            )
            
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"License revocation error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to revoke license: {str(e)}"
        )


@router.post("/license/deactivate")
async def deactivate_license(
    request: LicenseKeyRequest,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Detach license from current device, freeing the seat.
    """
    try:
        import app.services.license_validator as lv
        license_key = lv.LICENSE_KEY if request.license_key == "current" else request.license_key
        # For offline-only setups, license_key may be empty — still need to clear local state
        result = DeviceManager.deactivate_current_device(license_key or "")

        if result["success"]:
            return {"success": True, "message": result["message"]}
        else:
            raise HTTPException(status_code=400, detail=result["error"])

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"License detach error: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to detach license: {str(e)}")


@router.post("/license/transfer")
async def request_license_transfer(
    request: DeviceTransferRequest,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Request license transfer to current device.
    
    For hardware changes, computer replacements, etc.
    May be auto-approved based on similarity to previous device.
    """
    try:
        result = DeviceManager.request_device_transfer(
            license_key=request.license_key,
            reason=request.reason
        )
        
        return {
            "success": result["success"],
            "message": result["message"],
            "auto_approved": result.get("auto_approved", False),
            "transfer_id": result.get("transfer_id")
        }
        
    except Exception as e:
        logger.error(f"License transfer request error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to request license transfer: {str(e)}"
        )


@router.get("/license/devices/{license_key}")
async def get_license_devices(
    license_key: str,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Get list of devices where license is activated.
    """
    try:
        result = DeviceManager.get_device_activations(license_key)
        return result
        
    except Exception as e:
        logger.error(f"Failed to get license devices: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to retrieve device list: {str(e)}"
        )


@router.post("/license/set-key")
async def set_license_key(
    request: LicenseKeyRequest,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Set a new license key for trial to subscription upgrade.
    
    This allows upgrading from trial to subscription without restart.
    The new key is validated and persisted to .env file.
    
    Args:
        license_key: New license key to activate
    """
    try:
        result = LicenseValidator.set_license_key(request.license_key)
        
        # Send activation notification email
        try:
            from datetime import datetime, timezone
            EmailService.send_license_activation_notification(
                to_email=current_user.email,
                customer_name=current_user.name or "User",
                license_key=request.license_key,
                device_name=result.get("device_name", "Unknown Device"),
                activation_time=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
            )
        except Exception as e:
            logger.warning(f"Failed to send activation notification: {e}")
        
        return {
            "success": True,
            "message": "License key updated successfully",
            "license": {
                "valid": result["valid"],
                "license_type": result["license_type"],
                "expires_at": result.get("expires_at"),
                "seats_remaining": result.get("seats_remaining"),
                "features": result.get("features", {}),
                "message": result.get("message")
            }
        }
    except LicenseValidationError as e:
        logger.warning(f"License key validation failed: {e}")
        raise HTTPException(
            status_code=400,
            detail=str(e)
        )
    except Exception as e:
        logger.error(f"License key update error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to update license key: {str(e)}"
        )


@router.post("/license/refresh")
async def refresh_license(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
    """
    Force refresh license validation from server.
    
    Bypasses cache and performs online validation.
    Useful for:
    - Manual refresh after license update
    - Troubleshooting license issues
    - Verifying license changes
    """
    try:
        result = LicenseValidator.force_refresh()
        return {
            "success": True,
            "message": "License refreshed successfully",
            "license": {
                "valid": result["valid"],
                "license_type": result["license_type"],
                "expires_at": result.get("expires_at"),
                "seats_remaining": result.get("seats_remaining"),
                "features": result.get("features", {}),
                "message": result.get("message")
            }
        }
    except LicenseValidationError as e:
        logger.warning(f"License refresh failed: {e}")
        return {
            "success": False,
            "error": str(e),
            "message": "License validation failed"
        }
    except Exception as e:
        logger.error(f"License refresh error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to refresh license: {str(e)}"
        )


@router.post("/license/check-feature")
async def check_feature(
    request: FeatureCheckRequest,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Check if a specific feature is enabled in the current license.
    
    Args:
        feature_name: Name of the feature to check
        
    Returns:
        enabled: Boolean indicating if feature is available
    """
    try:
        enabled = LicenseValidator.check_feature(request.feature_name)
        return {
            "success": True,
            "feature": request.feature_name,
            "enabled": enabled
        }
    except Exception as e:
        logger.error(f"Feature check error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to check feature: {str(e)}"
        )


@router.post("/license/offline-file")
async def set_offline_license_file(
    request: OfflineLicenseRequest,
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Set path to offline license file.
    
    For air-gapped environments without internet access.
    The file should be a license-{uuid}.json downloaded from admin console.
    
    Args:
        file_path: Path to offline license JSON file
    """
    try:
        result = LicenseValidator.set_offline_license_file(request.file_path)
        return {
            "success": True,
            "message": "Offline license file validated and set",
            "license": {
                "valid": result["valid"],
                "license_type": result["license_type"],
                "expires_at": result.get("expires_at"),
                "features": result.get("features", {})
            }
        }
    except LicenseValidationError as e:
        logger.warning(f"Offline license file validation failed: {e}")
        raise HTTPException(
            status_code=400,
            detail=str(e)
        )
    except Exception as e:
        logger.error(f"Offline license file error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to set offline license file: {str(e)}"
        )


@router.post("/license/upload-offline")
async def upload_offline_license(
    file: UploadFile = File(...),
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """
    Upload an offline license file.
    
    Saves the uploaded license-{uuid}.json file to the user's
    .rivet directory and validates it.
    
    Args:
        file: Uploaded license JSON file
    """
    if not file.filename.endswith('.json'):
        raise HTTPException(status_code=400, detail="Invalid file type. Please upload a .json license file.")

    rivet_dir = Path.home() / ".rivet"
    rivet_dir.mkdir(parents=True, exist_ok=True)
    safe_name = Path(file.filename.replace("\\", "/")).name
    file_path = rivet_dir / safe_name

    try:
        with open(file_path, 'wb') as f:
            shutil.copyfileobj(file.file, f)

        logger.info(f"Offline license file uploaded: {file_path}")

        # Strict validation — raises LicenseValidationError immediately on any failure
        result = LicenseValidator.set_offline_license_file(str(file_path))

        return {
            "success": True,
            "message": "Offline license file uploaded and validated",
            "file_path": str(file_path),
            "license": {
                "valid": result["valid"],
                "license_type": result["license_type"],
                "expires_at": result.get("expires_at"),
                "features": result.get("features", {})
            }
        }

    except LicenseValidationError as e:
        logger.warning(f"Uploaded license file validation failed: {e}")
        if file_path.exists():
            file_path.unlink()
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"License file upload error: {e}")
        if file_path.exists():
            file_path.unlink()
        raise HTTPException(status_code=500, detail=f"Failed to upload license file: {str(e)}")


@router.get("/license/fingerprint")
async def get_device_fingerprint(
    current_user: dict = Depends(get_current_user)
) -> Dict[str, Any]:
    """Return the hardware fingerprint for this machine (used for air-gap license generation)."""
    from app.services.device_fingerprint import DeviceFingerprintGenerator
    return {"success": True, "fingerprint": DeviceFingerprintGenerator.generate()}


@router.get("/license/pricing")
async def get_pricing() -> Dict[str, Any]:
    """
    Get pricing tiers and feature comparison.

    Public endpoint (no auth required) — used for pricing display.
    Returns all tiers with prices, features, and limits.
    """
    return {
        "success": True,
        "tiers": get_all_tiers_for_display(),
        "contact_sales_email": "sales@cerebion.com",
    }


# ─────────────────────────────────────────────────────────────────────
# License Purchase Tracking
# ─────────────────────────────────────────────────────────────────────


@router.post("/license/purchases")
async def record_license_purchase(
    purchase: LicenseRecordCreate,
    session: SessionDep,
    current_user: dict = Depends(get_current_user),
) -> Dict[str, Any]:
    """
    Record a license purchase with a price snapshot.

    Captures the current price at time of purchase so that future
    pricing changes or promotions don't affect existing customers.

    For enterprise/manual sales, set price_at_purchase directly.
    For self-service, omit it and the current tier price is used.
    """
    tier = get_tier(purchase.tier)
    if not tier:
        raise HTTPException(status_code=400, detail=f"Unknown tier: {purchase.tier}")

    # Build snapshot from current pricing
    snapshot = create_purchase_snapshot(
        tier_name=purchase.tier,
        billing_interval=purchase.billing_interval.value,
        promotion_code=purchase.promotion_code,
    )

    # Allow explicit price override (enterprise / manual sales)
    price = purchase.price_at_purchase if purchase.price_at_purchase is not None else snapshot["price_at_purchase"]
    original_price = snapshot["original_price"]

    now = datetime.now(timezone.utc)
    validity_start = purchase.validity_start or datetime.fromisoformat(snapshot["validity_start"])
    validity_end = (
        purchase.validity_end
        or (datetime.fromisoformat(snapshot["validity_end"]) if snapshot["validity_end"] else None)
    )

    record = LicenseRecord(
        id=str(uuid.uuid4()),
        user_id=current_user["id"],
        license_key=purchase.license_key,
        tier=purchase.tier,
        price_at_purchase=price,
        billing_interval=purchase.billing_interval,
        currency=purchase.currency,
        validity_start=validity_start,
        validity_end=validity_end,
        purchased_at=now,
        status=LicenseStatus.ACTIVE,
        seats=purchase.seats,
        promotion_code=purchase.promotion_code,
        original_price=original_price,
        features_snapshot=snapshot["features"],
        notes=purchase.notes,
    )

    session.add(record)
    session.commit()
    session.refresh(record)

    logger.info(f"License purchase recorded: {record.id} tier={record.tier} user={record.user_id}")

    return {
        "success": True,
        "message": "License purchase recorded",
        "purchase": {
            "id": record.id,
            "tier": record.tier,
            "price_at_purchase": record.price_at_purchase,
            "original_price": record.original_price,
            "billing_interval": record.billing_interval,
            "currency": record.currency,
            "validity_start": record.validity_start.isoformat(),
            "validity_end": record.validity_end.isoformat() if record.validity_end else None,
            "seats": record.seats,
            "features": record.features_snapshot,
        },
    }


@router.get("/license/purchases")
async def get_license_purchases(
    session: SessionDep,
    current_user: dict = Depends(get_current_user),
) -> Dict[str, Any]:
    """
    Get all license purchases for the current user.

    Returns purchase history ordered by most recent first,
    including price snapshots and validity windows.
    """
    statement = (
        select(LicenseRecord)
        .where(LicenseRecord.user_id == current_user["id"])
        .order_by(LicenseRecord.purchased_at.desc())
    )
    results = session.exec(statement).all()

    purchases = []
    for record in results:
        purchases.append({
            "id": record.id,
            "license_key": record.license_key,
            "tier": record.tier,
            "price_at_purchase": record.price_at_purchase,
            "original_price": record.original_price,
            "billing_interval": record.billing_interval,
            "currency": record.currency,
            "validity_start": record.validity_start.isoformat(),
            "validity_end": record.validity_end.isoformat() if record.validity_end else None,
            "purchased_at": record.purchased_at.isoformat(),
            "status": record.status,
            "seats": record.seats,
            "promotion_code": record.promotion_code,
            "features": record.features_snapshot,
            "notes": record.notes,
        })

    return {
        "success": True,
        "purchases": purchases,
        "total": len(purchases),
    }


@router.get("/license/purchases/{purchase_id}")
async def get_license_purchase(
    purchase_id: str,
    session: SessionDep,
    current_user: dict = Depends(get_current_user),
) -> Dict[str, Any]:
    """Get a specific license purchase by ID."""
    record = session.get(LicenseRecord, purchase_id)

    if not record or record.user_id != current_user["id"]:
        raise HTTPException(status_code=404, detail="Purchase not found")

    return {
        "success": True,
        "purchase": {
            "id": record.id,
            "license_key": record.license_key,
            "tier": record.tier,
            "price_at_purchase": record.price_at_purchase,
            "original_price": record.original_price,
            "billing_interval": record.billing_interval,
            "currency": record.currency,
            "validity_start": record.validity_start.isoformat(),
            "validity_end": record.validity_end.isoformat() if record.validity_end else None,
            "purchased_at": record.purchased_at.isoformat(),
            "status": record.status,
            "seats": record.seats,
            "promotion_code": record.promotion_code,
            "features": record.features_snapshot,
            "notes": record.notes,
        },
    }
