from __future__ import annotations

from typing import Any

from liverylab_core import livery_identity, official_external_workflow, official_to_advanced_import, project_features, project_types


ORIGIN_CREATED = "created"
ORIGIN_PACKAGE_IMPORT = "package_import"
ORIGIN_GAME_IMPORT = "game_import"
ORIGIN_OFFICIAL_EXTERNAL = "official_external"
ORIGIN_UNKNOWN = "unknown"

PROJECT_STATE_EDITABLE = "editable"
PROJECT_STATE_NONE = "none"

READ_ONLY_MESSAGE = "Installed package skins are read-only."


def skin_origin(entry: dict[str, Any] | None) -> str:
    data = entry if isinstance(entry, dict) else {}
    raw = str(data.get("origin") or data.get("skin_origin") or "").strip().lower()
    aliases = {
        "self_created": ORIGIN_CREATED,
        "local_created": ORIGIN_CREATED,
        "liverylab_created": ORIGIN_CREATED,
        "created": ORIGIN_CREATED,
        "downloaded": ORIGIN_PACKAGE_IMPORT,
        "installed": ORIGIN_PACKAGE_IMPORT,
        "package": ORIGIN_PACKAGE_IMPORT,
        "package_import": ORIGIN_PACKAGE_IMPORT,
        "imported_package": ORIGIN_PACKAGE_IMPORT,
        "game": ORIGIN_GAME_IMPORT,
        "game_import": ORIGIN_GAME_IMPORT,
        "external": ORIGIN_GAME_IMPORT,
        "official": ORIGIN_OFFICIAL_EXTERNAL,
        "official_external": ORIGIN_OFFICIAL_EXTERNAL,
        "official_external_livery": ORIGIN_OFFICIAL_EXTERNAL,
    }
    if raw in aliases:
        return aliases[raw]
    managed_by = str(data.get("managed_by") or "").strip().lower()
    if managed_by == "game_import":
        return ORIGIN_GAME_IMPORT
    if managed_by == "liverylab":
        return ORIGIN_CREATED
    return ORIGIN_UNKNOWN


def project_state(entry: dict[str, Any] | None, origin: str | None = None) -> str:
    data = entry if isinstance(entry, dict) else {}
    raw = str(data.get("project_state") or data.get("projectState") or "").strip().lower()
    if raw in {PROJECT_STATE_EDITABLE, PROJECT_STATE_NONE}:
        return raw
    resolved_origin = origin or skin_origin(data)
    return PROJECT_STATE_EDITABLE if resolved_origin == ORIGIN_CREATED else PROJECT_STATE_NONE


def policy_for_skin(entry: dict[str, Any] | None) -> dict[str, Any]:
    data = entry if isinstance(entry, dict) else {}
    origin = skin_origin(data)
    state = project_state(data, origin)
    project_type = project_types.project_type_for_entry(data)
    official_external = project_type == project_types.PROJECT_TYPE_OFFICIAL_EXTERNAL_LIVERY or origin == ORIGIN_OFFICIAL_EXTERNAL
    official_external_role = official_external_workflow.role_for_entry(data) if official_external else ""
    official_details = data.get("official_external") if isinstance(data.get("official_external"), dict) else {}
    registration_state = str(
        data.get("registration_state")
        or data.get("registrationState")
        or official_details.get("registrationState")
        or ""
    ).strip().lower()
    unregistered_external = bool(
        official_external
        and (
            data.get("registered") is False
            or registration_state == "unregistered"
        )
    )
    official_external_project = official_external and official_external_role == official_external_workflow.ROLE_PROJECT
    editable = origin == ORIGIN_CREATED and state == PROJECT_STATE_EDITABLE and (not official_external or official_external_project)
    package_import = origin == ORIGIN_PACKAGE_IMPORT
    game_import = origin == ORIGIN_GAME_IMPORT
    read_only = not editable
    feature_matrix = project_features.permission_matrix(
        data,
        editable=editable,
        read_only=read_only,
        official_external=official_external,
        official_external_role=official_external_role,
    )
    permissions = feature_matrix.get("permissions") if isinstance(feature_matrix.get("permissions"), dict) else {}
    derived_advanced_dependencies = official_to_advanced_import.allows_advanced_dependencies(data)
    if derived_advanced_dependencies:
        permissions = {
            **permissions,
            "canEditPreviewImage": True,
            "canEditTyres": True,
            "canEditDriver": True,
            "canEditNumberplate": True,
            "canCreateEditableProject": False,
        }
    if unregistered_external:
        permissions = {
            **permissions,
            "canEditMetadata": False,
            "canEditProjectMetadata": False,
            "canEditPreviewImage": False,
            "canEditTyres": False,
            "canEditDriver": False,
            "canEditNumberplate": False,
            "canEditMaterialFinish": False,
            "canExportPackage": False,
            "canUseEditablePreviewTools": False,
            "canActivateInGame": False,
            "canDeactivateInGame": False,
            "canRemove": False,
            "canTrash": False,
            "canBackupTransfer": False,
            "canDuplicateProject": False,
            "canCreateEditableProject": False,
        }
    identity_locked = livery_identity.identity_locked(data)
    permission_reasons = feature_matrix.get("permissionReasons") if isinstance(feature_matrix.get("permissionReasons"), dict) else {}
    feature_rows = feature_matrix.get("featureRows") if isinstance(feature_matrix.get("featureRows"), list) else []
    read_only_reason = (
        "Baked Official artwork is locked. Advanced tyre, driver and numberplate add-ons remain editable."
        if official_to_advanced_import.artwork_locked(data) else
        "This ExternalLivery is detected in Saved Games but is not registered in LiveryLab yet."
        if unregistered_external else
        (
            "Official external projects are editable locally and can be deployed through the official Saved Games workflow."
            if official_external_project else (
                "Installed read-only Official packages remain read-only."
                if package_import else
                "External installs are kept as read-only library copies."
            )
        )
        if official_external else (
            READ_ONLY_MESSAGE if package_import else (
                "Imported game skins are read-only until converted into an editable project." if game_import else ""
            )
        )
    )
    return {
        "origin": origin,
        "projectState": state,
        "projectType": project_type,
        "officialExternalRole": official_external_role,
        "registrationState": registration_state or ("unregistered" if unregistered_external else "registered"),
        "registered": not unregistered_external,
        "deploymentTarget": data.get("deployment_target") or data.get("deploymentTarget") or (
            official_external_workflow.DEPLOYMENT_TARGET_SAVED_GAMES_EXTERNAL_LIVERIES if official_external else ""
        ),
        "editable": editable,
        "readOnly": read_only,
        "readOnlyReason": read_only_reason,
        "identityLocked": identity_locked,
        "canEditMetadata": bool(permissions.get("canEditMetadata")) and not identity_locked,
        "canEditProjectMetadata": bool(permissions.get("canEditProjectMetadata")),
        "canEditPreviewImage": bool(permissions.get("canEditPreviewImage")),
        "canEditTyres": bool(permissions.get("canEditTyres")),
        "canEditDriver": bool(permissions.get("canEditDriver")),
        "canEditNumberplate": bool(permissions.get("canEditNumberplate")),
        "canEditMaterialFinish": bool(permissions.get("canEditMaterialFinish")),
        "canExportPackage": bool(permissions.get("canExportPackage")),
        "canUseEditablePreviewTools": bool(permissions.get("canUseEditablePreviewTools")),
        "canUseReadOnlyPreview": bool(permissions.get("canUseReadOnlyPreview")),
        "canActivateInGame": bool(permissions.get("canActivateInGame")),
        "canDeactivateInGame": bool(permissions.get("canDeactivateInGame")),
        "canRemove": bool(permissions.get("canRemove")),
        "canTrash": bool(permissions.get("canTrash")),
        "canBackupTransfer": bool(permissions.get("canBackupTransfer")),
        "canDuplicateProject": bool(permissions.get("canDuplicateProject")),
        "canCreateEditableProject": bool(permissions.get("canCreateEditableProject")),
        "canRegisterExternal": unregistered_external,
        "permissionReasons": permission_reasons,
        "featureRows": feature_rows,
    }


def assert_policy(entry: dict[str, Any] | None, permission: str) -> None:
    policy = policy_for_skin(entry)
    if policy.get(permission):
        return
    reasons = policy.get("permissionReasons") if isinstance(policy.get("permissionReasons"), dict) else {}
    reason = str(reasons.get(permission) or policy.get("readOnlyReason") or "This action is not available for this skin.").strip()
    raise PermissionError(reason)
