package com.secureline.server;

import android.app.KeyguardManager;
import android.content.Context;
import android.os.UserManager;
import android.util.Slog;

import java.util.Objects;
import java.util.function.Supplier;

/** Central SecureLineOS USB policy. Policy only; enforcement is external. */
public final class SecureLineUsbProtection {
    private static final String TAG = "SecureLineUsbProtection";

    public enum UsbEvent {
        DISCONNECTED,
        CHARGING_ONLY,
        NEGOTIATING,
        UNKNOWN,
        DATA_CONNECTION,
        HOST_DEVICE,
        DATA_ACCESSORY,
        AUDIO_ACCESSORY
    }

    public enum UsbAction { ALLOW, BLOCK, BLOCK_AND_WIPE }

    public static final class UsbDecision {
        public final UsbAction action;
        public final boolean allow;
        public final boolean wipe;
        public final String reason;

        private UsbDecision(UsbAction action, String reason) {
            this.action = action;
            this.allow = action == UsbAction.ALLOW;
            this.wipe = action == UsbAction.BLOCK_AND_WIPE;
            this.reason = reason;
        }

        public static UsbDecision allow(String reason) {
            return new UsbDecision(UsbAction.ALLOW, reason);
        }
        public static UsbDecision block(String reason) {
            return new UsbDecision(UsbAction.BLOCK, reason);
        }
        public static UsbDecision blockAndWipe(String reason) {
            return new UsbDecision(UsbAction.BLOCK_AND_WIPE, reason);
        }
    }

    private final KeyguardManager mKeyguardManager;
    private final UserManager mUserManager;
    private final Supplier<Boolean> mProtectionEnabledSupplier;

    public SecureLineUsbProtection(Context context, Supplier<Boolean> protectionEnabledSupplier) {
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
        mUserManager = context.getSystemService(UserManager.class);
        mProtectionEnabledSupplier = Objects.requireNonNull(protectionEnabledSupplier);
    }

    /** Returns the authoritative runtime protection state. Fail-closed on errors. */
    public boolean isProtectionEnabled() {
        return isProtectionEnabledInternal();
    }

    /** Returns the authoritative lock/BFU state used by the USB policy. */
    public boolean isDeviceLocked() {
        return isLockedInternal();
    }

    /** Internal Android configuration request: may block, never wipes by itself. */
    public UsbDecision evaluate(UsbEvent event) {
        return evaluateInternal(event, false);
    }

    /** Positively confirmed physical USB event. */
    public UsbDecision evaluatePhysical(UsbEvent event) {
        return evaluateInternal(event, true);
    }

    private UsbDecision evaluateInternal(UsbEvent event, boolean physicalEvidence) {
        try {
            if (event == null) event = UsbEvent.UNKNOWN;
            if (!isProtectionEnabledInternal()) return UsbDecision.allow("protection_disabled");

            if (event == UsbEvent.DISCONNECTED || event == UsbEvent.CHARGING_ONLY) {
                return UsbDecision.allow(event.name().toLowerCase());
            }

            final boolean locked = isLockedInternal();
            if (!locked) {
                // Owner is actively using an unlocked device: allow all USB modes.
                return UsbDecision.allow("device_unlocked");
            }

            switch (event) {
                case DATA_CONNECTION:
                    return physicalEvidence
                            ? UsbDecision.blockAndWipe("usb_locked_data_connection")
                            : UsbDecision.block("usb_data_configuration_blocked");
                case HOST_DEVICE:
                    return physicalEvidence
                            ? UsbDecision.blockAndWipe("usb_locked_host_device")
                            : UsbDecision.block("usb_host_configuration_blocked");
                case DATA_ACCESSORY:
                    return physicalEvidence
                            ? UsbDecision.blockAndWipe("usb_locked_data_accessory")
                            : UsbDecision.block("usb_accessory_configuration_blocked");
                case AUDIO_ACCESSORY:
                    return physicalEvidence
                            ? UsbDecision.blockAndWipe("usb_locked_audio_accessory")
                            : UsbDecision.block("usb_audio_configuration_blocked");
                case NEGOTIATING:
                    return UsbDecision.block("usb_negotiating");
                case UNKNOWN:
                default:
                    return UsbDecision.block("usb_unknown");
            }
        } catch (Throwable t) {
            Slog.e(TAG, "USB policy evaluation failed", t);
            return UsbDecision.block("policy_failure");
        }
    }

    private boolean isLockedInternal() {
        try {
            if (mUserManager == null || !mUserManager.isUserUnlocked()) return true;
            return mKeyguardManager == null || mKeyguardManager.isDeviceLocked();
        } catch (Throwable t) {
            Slog.e(TAG, "Lock state failed", t);
            return true;
        }
    }

    private boolean isProtectionEnabledInternal() {
        try {
            final Boolean enabled = mProtectionEnabledSupplier.get();
            return enabled == null || enabled;
        } catch (Throwable t) {
            Slog.e(TAG, "Protection state failed", t);
            return true;
        }
    }
}
