package com.tiedup.remake.state.components;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.LeashProxyEntity;
import com.tiedup.remake.state.ICaptor;
import com.tiedup.remake.state.IPlayerLeashAccess;
import com.tiedup.remake.state.hosts.IPlayerBindStateHost;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;

/**
 * Component responsible for captivity mechanics and leash proxy management.
 *
 * Single Responsibility: Captivity lifecycle and transport management
 * Complexity: VERY HIGH (mixin coupling, network sync, leash proxy coordination)
 * Risk: VERY HIGH (critical path, mixin dependency, 4 network sync points)
 *
 * Mixin Dependency: IPlayerLeashAccess for leash proxy system
 * Network Sync: 4 syncEnslavement() calls coordinated via host
 *
 * Captivity States:
 * - Not Captive: No captor, no leash
 * - Captive (by entity): Has captor, leashed to entity
 * - Pole Binding: No captor, leashed to pole (LeashFenceKnotEntity)
 */
public class PlayerCaptivity {

    private final IPlayerBindStateHost host;

    public PlayerCaptivity(IPlayerBindStateHost host) {
        this.host = host;
    }

    // ========== Captivity Initiation ==========

    /**
     * Initiates the capture process by a captor.
     * Uses the proxy-based leash system (player is NOT mounted).
     *
     * Thread Safety: Synchronized to prevent race condition where two kidnappers
     * could both pass the isCaptive() check and attempt to capture simultaneously.
     *
     * @param newCaptor The entity attempting to capture this player
     * @return true if capture succeeded
     */
    public synchronized boolean getCapturedBy(ICaptor newCaptor) {
        Player player = host.getPlayer();
        if (player == null || newCaptor == null) return false;

        // Must be enslavable (tied up) OR captor can capture (includes collar owner exception)
        if (
            !isEnslavable() && !newCaptor.canCapture(host.getKidnapped())
        ) return false;

        // Check if already captured (atomic check under synchronization)
        if (isCaptive()) return false;

        // Free all captives instead of transferring (until multi-captive is implemented)
        if (host.getCaptorManager().hasCaptives()) {
            TiedUpMod.LOGGER.debug(
                "[PlayerCaptivity] {} is being captured - freeing their {} captives",
                player.getName().getString(),
                host.getCaptorManager().getCaptiveCount()
            );
            host.getCaptorManager().freeAllCaptives(true);
        }

        // Use new proxy-based leash system
        if (player instanceof IPlayerLeashAccess access) {
            access.tiedup$attachLeash(newCaptor.getEntity());
            newCaptor.addCaptive(host.getKidnapped());
            host.setCaptor(newCaptor);

            TiedUpMod.LOGGER.debug(
                "[PlayerCaptivity] {} captured by {} (proxy leash)",
                player.getName().getString(),
                newCaptor.getEntity().getName().getString()
            );

            // Sync enslavement state to all clients
            host.syncEnslavement();
            return true;
        }

        TiedUpMod.LOGGER.error(
            "[PlayerCaptivity] Player {} does not implement IPlayerLeashAccess!",
            player.getName().getString()
        );
        return false;
    }

    // ========== Captivity Release ==========

    /**
     * Ends captivity with default behavior (drops leash item).
     */
    public void free() {
        free(true);
    }

    /**
     *
     * @param dropLead Whether to drop the leash item
     */
    public void free(boolean dropLead) {
        Player player = host.getPlayer();
        if (player == null) return;

        if (!(player instanceof IPlayerLeashAccess access)) {
            TiedUpMod.LOGGER.error(
                "[PlayerCaptivity] Player {} does not implement IPlayerLeashAccess!",
                player.getName().getString()
            );
            return;
        }

        ICaptor captor = host.getCaptor();

        // Handle pole binding (no captor) - just detach leash
        if (captor == null) {
            if (access.tiedup$isLeashed()) {
                TiedUpMod.LOGGER.debug(
                    "[PlayerCaptivity] Freeing {} from pole binding",
                    player.getName().getString()
                );
                if (dropLead) {
                    access.tiedup$dropLeash();
                }
                access.tiedup$detachLeash();
                host.syncEnslavement();
            }
            return;
        }

        TiedUpMod.LOGGER.debug(
            "[PlayerCaptivity] Freeing {} from captivity",
            player.getName().getString()
        );

        // 1. Remove from captor's tracking list
        captor.removeCaptive(host.getKidnapped(), false);

        // 2. Detach leash proxy
        if (dropLead) {
            access.tiedup$dropLeash();
        }
        access.tiedup$detachLeash();

        // 3. Reset state
        host.setCaptor(null);

        // 4. Sync freed state to all clients
        host.syncEnslavement();
    }

    // ========== Captivity Transfer ==========

    /**
     * Transfers captivity from current captor to a new captor.
     *
     * Thread Safety: Synchronized to prevent concurrent transfer attempts.
     *
     * @param newCaptor The new captor entity
     */
    public synchronized void transferCaptivityTo(ICaptor newCaptor) {
        Player player = host.getPlayer();
        ICaptor currentCaptor = host.getCaptor();

        if (
            player == null ||
            newCaptor == null ||
            currentCaptor == null ||
            !currentCaptor.allowCaptiveTransfer()
        ) return;

        currentCaptor.removeCaptive(host.getKidnapped(), false);

        // Re-attach leash to new captor
        if (player instanceof IPlayerLeashAccess access) {
            access.tiedup$detachLeash();
            access.tiedup$attachLeash(newCaptor.getEntity());
        }

        newCaptor.addCaptive(host.getKidnapped());
        host.setCaptor(newCaptor);

        // Sync new captor to all clients
        host.syncEnslavement();
    }

    // ========== State Queries ==========

    /**
     * Check if this player can be captured (leashed).
     * Must be tied up to be leashed.
     * Collar alone is NOT enough - collar owner exception is handled in canCapture().
     *
     * @return true if player is tied up and can be leashed
     */
    public boolean isEnslavable() {
        return host.isTiedUp();
    }

    /**
     * Check if player is currently captured by an entity.
     *
     * @return true if player has a captor and is leashed
     */
    public boolean isCaptive() {
        Player player = host.getPlayer();
        if (host.getCaptor() == null) return false;
        if (player instanceof IPlayerLeashAccess access) {
            return access.tiedup$isLeashed();
        }
        return false;
    }

    /**
     * Get the leash proxy entity (transport system).
     *
     * @return The leash proxy, or null if not leashed
     */
    @Nullable
    public LeashProxyEntity getTransport() {
        Player player = host.getPlayer();
        if (player instanceof IPlayerLeashAccess access) {
            return access.tiedup$getLeashProxy();
        }
        return null;
    }

    // ========== Captivity Monitoring ==========

    /**
     * Periodically monitors captivity validity.
     * Simplified: If any condition is invalid, free the captive immediately.
     *
     * Called from RestraintTaskTickHandler every player tick.
     */
    public void checkStillCaptive() {
        if (!isCaptive()) return;

        Player player = host.getPlayer();
        if (player == null) return;

        // Check if no longer tied/collared
        if (!host.isTiedUp() && !host.hasCollar()) {
            TiedUpMod.LOGGER.debug(
                "[PlayerCaptivity] Auto-freeing {} - no restraints",
                player.getName().getString()
            );
            free();
            return;
        }

        // Check leash proxy status
        if (player instanceof IPlayerLeashAccess access) {
            LeashProxyEntity proxy = access.tiedup$getLeashProxy();
            if (proxy == null || proxy.proxyIsRemoved()) {
                TiedUpMod.LOGGER.debug(
                    "[PlayerCaptivity] Auto-freeing {} - proxy invalid",
                    player.getName().getString()
                );
                // Notify captor BEFORE freeing (triggers retrieval behavior)
                ICaptor captor = host.getCaptor();
                if (captor != null) {
                    captor.onCaptiveReleased(host.getKidnapped());
                }
                free();
                return;
            }

            // Check if leash holder is still valid
            if (proxy.getLeashHolder() == null) {
                TiedUpMod.LOGGER.debug(
                    "[PlayerCaptivity] Auto-freeing {} - leash holder gone",
                    player.getName().getString()
                );
                // Notify captor BEFORE freeing (triggers retrieval behavior)
                ICaptor captor = host.getCaptor();
                if (captor != null) {
                    captor.onCaptiveReleased(host.getKidnapped());
                }
                free();
            }
        }
    }
}
