"""
CNP Solutions — license activation, renewal, and local node registration.
"""
from datetime import datetime
from typing import List, Optional

from fastapi import APIRouter, Depends, Header, HTTPException, Request, status
from sqlalchemy.orm import Session

from app.core.config import settings
from app.core.database import get_db
from app.core.dependencies import get_current_user
from app.models.user import User
from app.models.licensing import LicenseKey, DeploymentNode
from app.schemas.licensing import (
    LicenseValidateRequest,
    LicenseValidateResponse,
    LicenseVerifySetupRequest,
    LicenseVerifySetupResponse,
    LicenseClaimSetupRequest,
    LicenseClaimSetupResponse,
    CloudMirrorProvisionRequest,
    CloudMirrorProvisionResponse,
    LicenseIssueRequest,
    LicenseIssueResponse,
    LicenseRenewRequest,
    LicenseKeySummary,
    LicenseStatusResponse,
    NodeRegisterRequest,
    NodeRegisterResponse,
    NodeHeartbeatRequest,
)
from app.services.licensing_service import (
    LicensingService,
    create_setup_token,
    uses_cloud_license_authority,
    verify_setup_token,
)
from app.services.cloud_mirror_service import CloudMirrorProvisioner

router = APIRouter()


def _require_super_admin(current_user: User = Depends(get_current_user)) -> User:
    role = (getattr(current_user, "role", None) or "").lower()
    if role != "super_admin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Only super administrators can manage licenses in Settings.",
        )
    return current_user


def _require_license_admin(x_cnp_license_admin: Optional[str] = Header(None, alias="X-CNP-License-Admin")):
    """Protect key issuance — set CNP_LICENSE_ADMIN_SECRET in server .env."""
    expected = settings.license_admin_secret
    if not expected:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="License admin API is not configured on this server.",
        )
    if not x_cnp_license_admin or x_cnp_license_admin != expected:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid license admin credentials")


@router.post("/validate", response_model=LicenseValidateResponse)
def validate_license(payload: LicenseValidateRequest, db: Session = Depends(get_db)):
    """Public — verify a purchase token before starting company setup."""
    svc = LicensingService(db)
    deployment = payload.deployment or settings.deployment_mode or "cloud"
    if uses_cloud_license_authority():
        result = svc._validate_for_setup_via_cloud(payload.license_key, deployment=deployment)
    else:
        result = svc._validate_for_setup_local(payload.license_key, deployment=deployment)
    if result.get("valid") and not uses_cloud_license_authority():
        result["setup_token"] = create_setup_token(payload.license_key, deployment)
    return LicenseValidateResponse(**result)


@router.post("/verify-setup", response_model=LicenseVerifySetupResponse)
def verify_setup_token_endpoint(payload: LicenseVerifySetupRequest):
    """Confirm setup wizard completed license validation (cloud hub only issues tokens)."""
    dep = (payload.deployment or "cloud").lower()
    if not verify_setup_token(payload.license_key, payload.setup_token, deployment=dep):
        return LicenseVerifySetupResponse(
            valid=False,
            message="License validation expired or invalid. Return to step 1 and validate again.",
        )
    return LicenseVerifySetupResponse(valid=True)


@router.post("/claim-setup", response_model=LicenseClaimSetupResponse)
def claim_setup_license(
    payload: LicenseClaimSetupRequest,
    db: Session = Depends(get_db),
):
    """
    Mark a license as consumed after a local server completes setup.
    Called by on-premise installs — prevents reusing the same purchase token.
    """
    dep = (payload.deployment or "local").lower()
    if not verify_setup_token(payload.license_key, payload.setup_token, deployment=dep):
        return LicenseClaimSetupResponse(
            success=False,
            message="Invalid or expired setup authorization.",
        )

    svc = LicensingService(db)
    record = svc.find_by_key(payload.license_key)
    if not record:
        return LicenseClaimSetupResponse(success=False, message="License key not found.")

    blocked = svc._check_license_record_blocks(record, dep)
    if blocked:
        return LicenseClaimSetupResponse(success=False, message=blocked.get("message", "License unavailable."))

    now = datetime.utcnow()
    record.status = "active"
    record.activated_at = now
    if payload.site_name:
        note = f"Local site: {payload.site_name}"
        record.notes = f"{record.notes}\n{note}".strip() if record.notes else note
    db.commit()
    return LicenseClaimSetupResponse(
        success=True,
        message="License registered for this local server.",
    )


@router.post("/provision-cloud-mirror", response_model=CloudMirrorProvisionResponse)
def provision_cloud_mirror(
    payload: CloudMirrorProvisionRequest,
    db: Session = Depends(get_db),
):
    """
    Create or update a cloud tenant mirroring a local install (same UUIDs + admin password).
    Secured by setup_token from license validation. Enables cloud login backup + transaction sync.
    """
    if (settings.deployment_mode or "cloud").lower() != "cloud":
        raise HTTPException(
            status_code=400,
            detail="provision-cloud-mirror is only available on the cloud hub",
        )
    result = CloudMirrorProvisioner(db).provision(
        license_key=payload.license_key,
        setup_token=payload.setup_token,
        mirror_provision_token=payload.mirror_provision_token,
        deployment=payload.deployment,
        company_id=payload.company_id,
        company_code=payload.company_code,
        company_name=payload.company_name,
        admin_user_id=payload.admin_user_id,
        admin_username=payload.admin_username,
        admin_password_digest=payload.admin_password_digest,
        hq_branch_id=payload.hq_branch_id,
        hq_branch_code=payload.hq_branch_code,
        legal_name=payload.legal_name,
        admin_email=payload.admin_email,
        admin_first_name=payload.admin_first_name,
        admin_last_name=payload.admin_last_name,
        phone=payload.phone,
        email=payload.email,
        industry=payload.industry,
        currency=payload.currency,
        country=payload.country,
        timezone=payload.timezone,
        accounting_codes=payload.accounting_codes,
        site_name=payload.site_name,
    )
    if result.get("success"):
        db.commit()
    return CloudMirrorProvisionResponse(**result)


@router.get("/status", response_model=LicenseStatusResponse)
def license_status(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    """Current tenant license status (authenticated)."""
    company_id = getattr(current_user, "company_id", None)
    if not company_id:
        raise HTTPException(status_code=404, detail="No company on this account")
    svc = LicensingService(db)
    return LicenseStatusResponse(**svc.company_license_status(company_id))


@router.post("/nodes/register", response_model=NodeRegisterResponse)
def register_deployment_node(
    payload: NodeRegisterRequest,
    request: Request,
    db: Session = Depends(get_db),
):
    """
    Register a local/on-prem CNPERP node with the cloud license hub.
    Returns a sync_token — store securely on the local server for heartbeats.
    """
    svc = LicensingService(db)
    try:
        node, sync_token = svc.register_node(
            payload.license_key,
            node_name=payload.node_name,
            public_base_url=payload.public_base_url,
            machine_fingerprint=payload.machine_fingerprint,
            app_version=payload.app_version,
            client_ip=request.client.host if request.client else None,
        )
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    return NodeRegisterResponse(
        node_id=node.id,
        sync_token=sync_token,
        company_id=node.company_id,
        message="Node registered. Send heartbeats every few minutes while online.",
    )


@router.post("/nodes/heartbeat")
def node_heartbeat(
    payload: NodeHeartbeatRequest,
    request: Request,
    db: Session = Depends(get_db),
):
    """Local server heartbeat — keeps cloud visibility; may flag sync_requested for LAN push."""
    svc = LicensingService(db)
    try:
        node = svc.heartbeat_node(
            payload.node_id,
            payload.sync_token,
            app_version=payload.app_version,
            client_ip=request.client.host if request.client else None,
        )
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=401, detail=str(e))

    sync_requested = bool(node.sync_requested_at)
    return {
        "success": True,
        "message": "Heartbeat recorded",
        "sync_requested": sync_requested,
        "realtime_sync_enabled": node.realtime_sync_enabled if node.realtime_sync_enabled is not None else True,
    }


# ── CNP admin (issue / renew / revoke) ───────────────────────────────────────

@router.post("/keys", response_model=LicenseIssueResponse, dependencies=[Depends(_require_license_admin)])
def issue_license_key(payload: LicenseIssueRequest, db: Session = Depends(get_db)):
    """CNP staff — create a new yearly license key for a customer."""
    svc = LicensingService(db)
    record, full_key = svc.issue_license(
        plan=payload.plan,
        years=payload.years,
        deployment_mode=payload.deployment_mode,
        customer_name=payload.customer_name,
        customer_email=payload.customer_email,
        max_users=payload.max_users,
        max_branches=payload.max_branches,
        notes=payload.notes,
    )
    db.commit()
    return LicenseIssueResponse(
        license_id=record.id,
        license_key=full_key,
        key_prefix=record.key_prefix,
        valid_until=record.valid_until,
        plan=record.plan,
        message="License created. Send the full key to the customer once — it cannot be retrieved again.",
    )


@router.get("/keys", response_model=List[LicenseKeySummary], dependencies=[Depends(_require_license_admin)])
def list_license_keys(
    status_filter: Optional[str] = None,
    db: Session = Depends(get_db),
):
    from app.models.licensing import LicenseKey

    q = db.query(LicenseKey).order_by(LicenseKey.created_at.desc())
    if status_filter:
        q = q.filter(LicenseKey.status == status_filter)
    return q.limit(200).all()


@router.post("/keys/{license_id}/renew", dependencies=[Depends(_require_license_admin)])
def renew_license_key(
    license_id: str,
    payload: LicenseRenewRequest,
    db: Session = Depends(get_db),
):
    svc = LicensingService(db)
    try:
        record = svc.renew_license(license_id, years=payload.years)
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return {
        "success": True,
        "license_id": record.id,
        "valid_until": record.valid_until.isoformat(),
        "renewals_count": record.renewals_count,
    }


@router.post("/keys/{license_id}/revoke", dependencies=[Depends(_require_license_admin)])
def revoke_license_key(license_id: str, db: Session = Depends(get_db)):
    svc = LicensingService(db)
    try:
        record = svc.revoke_license(license_id)
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return {"success": True, "license_id": record.id, "status": record.status}


# ── Settings UI (authenticated super_admin) ───────────────────────────────────

@router.get("/admin/access")
def licensing_admin_access(current_user: User = Depends(get_current_user)):
    role = (getattr(current_user, "role", None) or "").lower()
    return {
        "can_manage_licenses": role == "super_admin",
        "deployment_mode": settings.deployment_mode,
        "license_enforcement": settings.license_enforcement,
    }


@router.get("/admin/keys", response_model=List[LicenseKeySummary])
def admin_list_license_keys(
    status_filter: Optional[str] = None,
    db: Session = Depends(get_db),
    _user: User = Depends(_require_super_admin),
):
    q = db.query(LicenseKey).order_by(LicenseKey.created_at.desc())
    if status_filter:
        q = q.filter(LicenseKey.status == status_filter)
    return q.limit(200).all()


@router.post("/admin/keys", response_model=LicenseIssueResponse)
def admin_issue_license_key(
    payload: LicenseIssueRequest,
    db: Session = Depends(get_db),
    _user: User = Depends(_require_super_admin),
):
    svc = LicensingService(db)
    record, full_key = svc.issue_license(
        plan=payload.plan,
        years=payload.years,
        deployment_mode=payload.deployment_mode,
        customer_name=payload.customer_name,
        customer_email=payload.customer_email,
        max_users=payload.max_users,
        max_branches=payload.max_branches,
        notes=payload.notes,
    )
    db.commit()
    return LicenseIssueResponse(
        license_id=record.id,
        license_key=full_key,
        key_prefix=record.key_prefix,
        valid_until=record.valid_until,
        plan=record.plan,
        message="License created. Copy the key now — it cannot be shown again.",
    )


@router.post("/admin/keys/{license_id}/renew")
def admin_renew_license_key(
    license_id: str,
    payload: LicenseRenewRequest,
    db: Session = Depends(get_db),
    _user: User = Depends(_require_super_admin),
):
    svc = LicensingService(db)
    try:
        record = svc.renew_license(license_id, years=payload.years)
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return {
        "success": True,
        "license_id": record.id,
        "valid_until": record.valid_until.isoformat(),
        "renewals_count": record.renewals_count,
    }


@router.post("/admin/keys/{license_id}/revoke")
def admin_revoke_license_key(
    license_id: str,
    db: Session = Depends(get_db),
    _user: User = Depends(_require_super_admin),
):
    svc = LicensingService(db)
    try:
        record = svc.revoke_license(license_id)
        db.commit()
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return {"success": True, "license_id": record.id, "status": record.status}


@router.get("/admin/nodes")
def admin_list_deployment_nodes(
    db: Session = Depends(get_db),
    _user: User = Depends(_require_super_admin),
):
    nodes = (
        db.query(DeploymentNode)
        .order_by(DeploymentNode.last_heartbeat_at.desc().nullslast())
        .limit(100)
        .all()
    )
    out = []
    for n in nodes:
        lic = db.query(LicenseKey).filter(LicenseKey.id == n.license_key_id).first()
        out.append({
            "id": n.id,
            "node_name": n.node_name,
            "deployment_type": n.deployment_type,
            "public_base_url": n.public_base_url,
            "is_active": n.is_active,
            "last_heartbeat_at": n.last_heartbeat_at.isoformat() if n.last_heartbeat_at else None,
            "last_ip": n.last_ip,
            "app_version": n.app_version,
            "company_id": n.company_id,
            "license_prefix": lic.key_prefix if lic else None,
        })
    return {"success": True, "data": out}
