"""Persist license and trial usage on disk."""

from __future__ import annotations

import json
import threading
from datetime import datetime, timezone
from pathlib import Path
import platform
from typing import Any, Dict, Optional

from .. import config
from . import everflow_client
from .machine_id import get_device_name, get_machine_id
from .plans import PLAN_CONFIG_VERSION, PURCHASE_URL

_lock = threading.RLock()

LICENSE_PATH = config.DATA_DIR / "license.json"
TRIAL_USAGE_PATH = config.DATA_DIR / "trial_usage.json"


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def _read_json(path: Path, default: dict) -> dict:
    if not path.exists():
        return dict(default)
    try:
        with path.open(encoding="utf-8") as f:
            data = json.load(f)
        return data if isinstance(data, dict) else dict(default)
    except (OSError, json.JSONDecodeError):
        return dict(default)


def _write_json(path: Path, data: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    tmp = path.with_suffix(path.suffix + ".tmp")
    with tmp.open("w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    tmp.replace(path)


def get_mode() -> str:
    with _lock:
        lic = _read_json(LICENSE_PATH, {})
    if lic.get("mode") == "licensed" and lic.get("status") == "active":
        return "licensed"
    return "trial"


def get_license_record() -> Optional[dict]:
    with _lock:
        lic = _read_json(LICENSE_PATH, {})
    if lic.get("mode") != "licensed":
        return None
    return lic


def _save_licensed_record(
    license_key: str,
    api_response: dict,
    *,
    source: str = "everflow",
) -> dict:
    entitlements = api_response.get("entitlements") or {}
    record = {
        "mode": "licensed",
        "status": api_response.get("status") or "active",
        "license_key": license_key.strip(),
        "license_key_hint": api_response.get("license_key_hint")
        or (f"{license_key[:4]}…{license_key[-4:]}" if len(license_key) > 8 else license_key),
        "instance_id": api_response.get("instance_id"),
        "machine_id": get_machine_id(),
        "activated_at": _now_iso(),
        "last_validated_at": api_response.get("validated_at") or _now_iso(),
        "source": source,
        "entitlements": {
            "core": bool(entitlements.get("core", True)),
            "max_devices": entitlements.get("max_devices", 2),
            "max_major_version": entitlements.get("max_major_version", 1),
        },
        "expires_at": api_response.get("expires_at"),
        "customer_email": api_response.get("customer_email"),
    }
    with _lock:
        _write_json(LICENSE_PATH, record)
    return record


def activate_license(license_key: str, *, source: str = "everflow") -> dict:
    key = (license_key or "").strip()
    if not key:
        raise ValueError("license_key is required")

    api_response = everflow_client.activate(
        key,
        machine_id=get_machine_id(),
        device_name=get_device_name(),
        app_version=config.app_version(),
        arch=platform.machine(),
    )
    if not api_response.get("ok"):
        raise ValueError("Activation failed")
    return _save_licensed_record(key, api_response, source=source)


def validate_saved_license() -> Optional[dict]:
    with _lock:
        lic = _read_json(LICENSE_PATH, {})
    if lic.get("mode") != "licensed" or not lic.get("license_key"):
        return None

    try:
        api_response = everflow_client.validate(
            lic["license_key"],
            machine_id=lic.get("machine_id") or get_machine_id(),
            instance_id=lic.get("instance_id"),
        )
    except everflow_client.LicenseApiError:
        return None

    lic["status"] = api_response.get("status") or lic.get("status")
    lic["last_validated_at"] = api_response.get("validated_at") or _now_iso()
    lic["instance_id"] = api_response.get("instance_id") or lic.get("instance_id")
    if api_response.get("license_key_hint"):
        lic["license_key_hint"] = api_response.get("license_key_hint")
    if api_response.get("customer_email"):
        lic["customer_email"] = api_response.get("customer_email")
    entitlements = api_response.get("entitlements") or {}
    lic["entitlements"] = {
        "core": bool(entitlements.get("core", True)),
        "max_devices": entitlements.get("max_devices", 2),
        "max_major_version": entitlements.get("max_major_version", 1),
    }
    with _lock:
        _write_json(LICENSE_PATH, lic)
    return lic


def deactivate_license() -> None:
    with _lock:
        lic = _read_json(LICENSE_PATH, {})
    license_key = lic.get("license_key")
    if license_key:
        try:
            everflow_client.deactivate(
                license_key,
                machine_id=lic.get("machine_id") or get_machine_id(),
                instance_id=lic.get("instance_id"),
            )
        except everflow_client.LicenseApiError:
            pass
    with _lock:
        if LICENSE_PATH.exists():
            LICENSE_PATH.unlink()


def _default_usage() -> dict:
    return {
        "plan_config_version": PLAN_CONFIG_VERSION,
        "quality_rescan_count_by_project": {},
    }


def load_trial_usage() -> dict:
    with _lock:
        usage = _read_json(TRIAL_USAGE_PATH, _default_usage())
    if usage.get("plan_config_version") != PLAN_CONFIG_VERSION:
        usage = _default_usage()
    if "quality_rescan_count_by_project" not in usage:
        usage["quality_rescan_count_by_project"] = {}
    return usage


def save_trial_usage(usage: dict) -> None:
    usage["plan_config_version"] = PLAN_CONFIG_VERSION
    with _lock:
        _write_json(TRIAL_USAGE_PATH, usage)


def quality_rescan_count(project_id: int) -> int:
    usage = load_trial_usage()
    return int(usage["quality_rescan_count_by_project"].get(str(project_id), 0))


def record_quality_rescan(project_id: int) -> int:
    usage = load_trial_usage()
    key = str(project_id)
    count = int(usage["quality_rescan_count_by_project"].get(key, 0)) + 1
    usage["quality_rescan_count_by_project"][key] = count
    save_trial_usage(usage)
    return count


def build_status(*, project_count: int, photos_in_project: int, active_project_id: Optional[int]) -> dict:
    from .plans import limits_for_mode

    mode = get_mode()
    limits = limits_for_mode(mode)
    rescan_count = quality_rescan_count(active_project_id) if active_project_id else 0

    locked_features = []
    if not limits.batch_rename_enabled:
        locked_features.append("batch_rename")
    if not limits.defect_detection_enabled:
        locked_features.append("defect_detection")
    if not limits.exposure_detection_enabled:
        locked_features.append("exposure_detection")
    if not limits.editing_enabled:
        locked_features.append("editing")
    if not limits.export_enabled:
        locked_features.append("export")

    license_record = get_license_record()
    if license_record:
        public_license = dict(license_record)
        public_license.pop("license_key", None)
    else:
        public_license = None

    return {
        "mode": mode,
        "plan_config_version": PLAN_CONFIG_VERSION,
        "machine_id": get_machine_id(),
        "device_name": get_device_name(),
        "limits": limits.to_dict(),
        "usage": {
            "project_count": project_count,
            "photos_in_project": photos_in_project,
            "quality_rescan_count": rescan_count,
            "active_project_id": active_project_id,
        },
        "locked_features": locked_features,
        "purchase_url": PURCHASE_URL,
        "license": public_license,
    }
