"""Tenant configuration management with encrypted secret storage.

Secrets (client_secret) are encrypted with Fernet symmetric encryption before
being stored in the database.

Key resolution order:
  1. ZSCALER_SECRET_KEY environment variable (explicit override)
  2. Key file at ~/.config/zs-config/secret.key (auto-created on first run)

On first launch the key is generated automatically — no manual setup required.
"""

import os
import sys
from pathlib import Path
from typing import List, Optional

from cryptography.fernet import Fernet, InvalidToken

from db.database import get_session
from db.models import TenantConfig

_KEY_FILE = Path.home() / ".config" / "zs-config" / "secret.key"
_KEY_FILE_LEGACY = Path.home() / ".config" / "z-config" / "secret.key"
_KEY_FILE_LEGACY2 = Path.home() / ".config" / "zscaler-cli" / "secret.key"


def _chmod_600(path: Path) -> None:
    """Set file permissions to 600 on platforms that support it."""
    if sys.platform != "win32":
        path.chmod(0o600)


def _get_fernet() -> Fernet:
    # 1. Explicit env var override
    key = os.environ.get("ZSCALER_SECRET_KEY")
    if key:
        return Fernet(key.encode() if isinstance(key, str) else key)

    # 2. Migrate from legacy key paths (zscaler-cli → z-config → zs-config)
    for _legacy in (_KEY_FILE_LEGACY, _KEY_FILE_LEGACY2):
        if not _KEY_FILE.exists() and _legacy.exists():
            _KEY_FILE.parent.mkdir(parents=True, exist_ok=True)
            _KEY_FILE.write_text(_legacy.read_text())
            _chmod_600(_KEY_FILE)
            break

    # 3. Persisted key file
    if _KEY_FILE.exists():
        key = _KEY_FILE.read_text().strip()
        return Fernet(key.encode())

    # 4. First run — auto-generate and save
    key = Fernet.generate_key().decode()
    _KEY_FILE.parent.mkdir(parents=True, exist_ok=True)
    _KEY_FILE.write_text(key)
    _chmod_600(_KEY_FILE)
    return Fernet(key.encode())


def generate_key() -> str:
    """Generate a new Fernet encryption key and persist it to the key file."""
    key = Fernet.generate_key().decode()
    _KEY_FILE.parent.mkdir(parents=True, exist_ok=True)
    _KEY_FILE.write_text(key)
    _chmod_600(_KEY_FILE)
    return key


def encrypt_secret(value: str) -> str:
    return _get_fernet().encrypt(value.encode()).decode()


def decrypt_secret(value: str) -> str:
    try:
        return _get_fernet().decrypt(value.encode()).decode()
    except InvalidToken as e:
        raise ValueError(
            "Failed to decrypt client secret — ZSCALER_SECRET_KEY may be wrong or the record is corrupted."
        ) from e


def add_tenant(
    name: str,
    zidentity_base_url: str,
    client_id: str,
    client_secret: str,
    oneapi_base_url: str = "https://api.zsapi.net",
    zpa_customer_id: Optional[str] = None,
    notes: Optional[str] = None,
) -> TenantConfig:
    """Add a new tenant configuration to the database."""
    with get_session() as session:
        tenant = TenantConfig(
            name=name,
            zidentity_base_url=zidentity_base_url.rstrip("/"),
            oneapi_base_url=oneapi_base_url.rstrip("/"),
            client_id=client_id,
            client_secret_enc=encrypt_secret(client_secret),
            zpa_customer_id=zpa_customer_id or None,
            notes=notes,
        )
        session.add(tenant)
        session.flush()
        session.refresh(tenant)
        return tenant


def get_tenant(name: str) -> Optional[TenantConfig]:
    """Retrieve an active tenant by name."""
    with get_session() as session:
        return session.query(TenantConfig).filter_by(name=name, is_active=True).first()


def list_tenants() -> List[TenantConfig]:
    """Return all active tenants."""
    with get_session() as session:
        return session.query(TenantConfig).filter_by(is_active=True).order_by(TenantConfig.name).all()


def update_tenant(
    name: str,
    zidentity_base_url: Optional[str] = None,
    client_id: Optional[str] = None,
    client_secret: Optional[str] = None,
    oneapi_base_url: Optional[str] = None,
    zpa_customer_id: Optional[str] = None,
    notes: Optional[str] = None,
) -> Optional[TenantConfig]:
    """Update fields on an existing tenant. Only provided fields are changed."""
    with get_session() as session:
        tenant = session.query(TenantConfig).filter_by(name=name, is_active=True).first()
        if not tenant:
            return None
        if zidentity_base_url is not None:
            tenant.zidentity_base_url = zidentity_base_url.rstrip("/")
        if oneapi_base_url is not None:
            tenant.oneapi_base_url = oneapi_base_url.rstrip("/")
        if client_id is not None:
            tenant.client_id = client_id
        if client_secret is not None:
            tenant.client_secret_enc = encrypt_secret(client_secret)
        if zpa_customer_id is not None:
            tenant.zpa_customer_id = zpa_customer_id
        if notes is not None:
            tenant.notes = notes
        session.flush()
        session.refresh(tenant)
        return tenant


def deactivate_tenant(name: str) -> bool:
    """Soft-delete a tenant (sets is_active=False)."""
    with get_session() as session:
        tenant = session.query(TenantConfig).filter_by(name=name, is_active=True).first()
        if not tenant:
            return False
        tenant.is_active = False
        return True
