import { ref, readonly, watch, computed, type Ref, type ComputedRef } from 'vue';
import { useI18n } from 'vue-i18n';
import { useSettingsStore } from '../stores/settings.store';
import { storeToRefs } from 'pinia';
import type { WebSocketMessage, MessagePayload } from '../types/websocket.types';

// --- Interfaces (Copied from DockerManager.vue) ---
interface PortInfo {
  IP?: string;
  PrivatePort: number;
  PublicPort?: number;
  Type: 'tcp' | 'udp' | string;
}

export interface DockerContainer { // Exporting for potential use elsewhere
  id: string;
  Names: string[];
  Image: string;
  ImageID: string;
  Command: string;
  Created: number;
  State: 'created' | 'restarting' | 'running' | 'removing' | 'paused' | 'exited' | 'dead' | string;
  Status: string;
  Ports: PortInfo[];
  Labels: Record<string, string>;
  stats?: DockerStats | null;
}

export interface DockerStats { // Exporting for potential use elsewhere
    ID: string;
    Name: string;
    CPUPerc: string;
    MemUsage: string;
    MemPerc: string;
    NetIO: string;
    BlockIO: string;
    PIDs: string;
}

// --- WebSocket Dependencies Interface ---
// Similar to other composables, defining dependencies for WS communication
export interface DockerManagerDependencies {
    sendMessage: (message: WebSocketMessage) => void;
    onMessage: (type: string, handler: (payload: any, fullMessage?: WebSocketMessage) => void) => () => void;
    isConnected: ComputedRef<boolean>;
    // We might need isSshReady or similar if Docker commands depend on SSH being fully ready
    // For now, isConnected might suffice, assuming WS connection implies SSH readiness for Docker
}

/**
 * Creates a Docker manager instance for a specific session.
 * @param sessionId The unique identifier for the session.
 * @param wsDeps WebSocket dependencies object.
 * @param i18n The i18n instance (t function).
 * @returns Docker manager instance.
 */
export function createDockerManager(sessionId: string, wsDeps: DockerManagerDependencies, i18n: { t: (key: string, params?: any) => string }) {
    const { sendMessage, onMessage, isConnected } = wsDeps;
    const { t } = i18n; // Use the passed i18n instance

    // --- State ---
    const containers = ref<DockerContainer[]>([]);
    const isLoading = ref(false);
    const error = ref<string | null>(null);
    const isDockerAvailable = ref(true); // Assume available until checked
    const expandedContainerIds = ref<Set<string>>(new Set());
    const initialLoadDone = ref(false);
    let refreshInterval: ReturnType<typeof setInterval> | null = null;
    let wsUnsubscribeHooks: (() => void)[] = [];

    // --- Settings Store ---
    // Settings need to be accessed here as well for default expansion
    const settingsStore = useSettingsStore();
    const { dockerDefaultExpandBoolean } = storeToRefs(settingsStore);

    // --- Methods ---

    // Clear existing WebSocket listeners
    const clearWsListeners = () => {
        if (wsUnsubscribeHooks.length > 0) {
            console.log(`[DockerManager ${sessionId}] Clearing ${wsUnsubscribeHooks.length} WebSocket listeners.`);
            wsUnsubscribeHooks.forEach(unsub => unsub());
            wsUnsubscribeHooks = [];
        }
    };

    // Request Docker status via WebSocket
    const requestDockerStatus = () => {
        if (!isConnected.value) {
            console.log(`[DockerManager ${sessionId}] WebSocket not connected, skipping Docker status request.`);
            // Reset state if disconnected? Or rely on watch(isConnected)?
            // Let's reset here for immediate feedback if called manually while disconnected.
            containers.value = [];
            isLoading.value = false;
            error.value = t('dockerManager.error.sshDisconnected'); // Use a generic disconnected message
            isDockerAvailable.value = false;
            expandedContainerIds.value.clear();
            initialLoadDone.value = false;
            if (refreshInterval) clearInterval(refreshInterval);
            refreshInterval = null;
            return;
        }

        console.log(`[DockerManager ${sessionId}] Requesting Docker status.`);
        isLoading.value = true;
        error.value = null; // Clear previous error
        sendMessage({ type: 'docker:get_status', sessionId }); // Ensure sessionId is included if needed by backend routing
    };

    // Setup WebSocket listeners
    const setupWsListeners = () => {
        clearWsListeners(); // Clear previous listeners first
        if (!isConnected.value) {
             console.warn(`[DockerManager ${sessionId}] Cannot setup listeners, WebSocket not connected.`);
             return;
        }
        console.log(`[DockerManager ${sessionId}] Setting up WebSocket listeners.`);

        const unsubStatus = onMessage('docker:status:update', (payload, message) => {
            if (message?.sessionId && message.sessionId !== sessionId) return; // Ignore messages for other sessions
            console.log(`[DockerManager ${sessionId}] Received docker:status:update`, payload);
            isLoading.value = false;

            if (payload && typeof payload.available === 'boolean') {
                isDockerAvailable.value = payload.available;
                if (payload.available && Array.isArray(payload.containers)) {
                    containers.value = payload.containers as DockerContainer[];
                    error.value = null;

                    // Clean up expansion state
                    const currentIds = new Set(containers.value.map(c => c.id));
                    const idsToRemove = new Set<string>();
                    expandedContainerIds.value.forEach(id => {
                        if (!currentIds.has(id)) idsToRemove.add(id);
                    });
                    idsToRemove.forEach(id => expandedContainerIds.value.delete(id));

                    // Handle default expand on initial load
                    if (!initialLoadDone.value && dockerDefaultExpandBoolean.value) {
                        console.log(`[DockerManager ${sessionId}] Applying default expand setting.`);
                        containers.value.forEach(container => {
                            if (!expandedContainerIds.value.has(container.id)) {
                                expandedContainerIds.value.add(container.id);
                            }
                        });
                        initialLoadDone.value = true;
                    }
                } else {
                    containers.value = [];
                    error.value = null;
                    expandedContainerIds.value.clear();
                    if (refreshInterval && !payload.available) {
                        clearInterval(refreshInterval);
                        refreshInterval = null;
                        console.log(`[DockerManager ${sessionId}] Stopped refresh interval due to remote Docker unavailability.`);
                    }
                }
            } else {
                isDockerAvailable.value = false;
                containers.value = [];
                error.value = t('dockerManager.error.invalidResponse');
                expandedContainerIds.value.clear();
                if (refreshInterval) clearInterval(refreshInterval);
                refreshInterval = null;
            }
        });

        const unsubStatusError = onMessage('docker:status:error', (payload, message) => {
            if (message?.sessionId && message.sessionId !== sessionId) return;
            console.error(`[DockerManager ${sessionId}] Received docker:status:error`, payload);
            isLoading.value = false;
            error.value = payload?.message || t('dockerManager.error.fetchFailed');
            isDockerAvailable.value = false;
            containers.value = [];
            expandedContainerIds.value.clear();
            if (refreshInterval) clearInterval(refreshInterval);
            refreshInterval = null;
        });

        const unsubCommandError = onMessage('docker:command:error', (payload, message) => {
            if (message?.sessionId && message.sessionId !== sessionId) return;
            console.error(`[DockerManager ${sessionId}] Received docker:command:error`, payload);
            // How to notify UI? Maybe set an error ref? Or rely on status update?
            // For now, just log. UI component could show a generic error or use a notification system.
            // Consider adding a transient commandError ref if needed.
             alert(`${t('dockerManager.error.commandFailed', { command: payload?.command || '?' })}: ${payload?.message || 'Unknown error'}`);
        });

        const unsubRequestUpdate = onMessage('request_docker_status_update', (payload, message) => {
            if (message?.sessionId && message.sessionId !== sessionId) return;
            console.log(`[DockerManager ${sessionId}] Received request_docker_status_update from backend.`);
            requestDockerStatus(); // Trigger a status refresh immediately
        });

        wsUnsubscribeHooks.push(unsubStatus, unsubStatusError, unsubCommandError, unsubRequestUpdate);
    };

    // Send command for a specific container via WebSocket
    const sendDockerCommand = (containerId: string, command: 'start' | 'stop' | 'restart' | 'remove') => {
        if (!isConnected.value) {
            console.warn(`[DockerManager ${sessionId}] Cannot send command, WebSocket not connected.`);
            alert(t('dockerManager.error.sshNotConnected')); // Use generic disconnected message
            return;
        }
        if (!isDockerAvailable.value) {
           console.warn(`[DockerManager ${sessionId}] Cannot send command, remote Docker is not available.`);
           alert(t('dockerManager.notAvailable'));
           return;
        }

        console.log(`[DockerManager ${sessionId}] Sending command '${command}' for container ${containerId}`);
        sendMessage({
            type: 'docker:command',
            sessionId, // Include sessionId if needed by backend routing
            payload: { containerId, command }
        });
        // Optionally trigger a status refresh sooner after a command
        // setTimeout(requestDockerStatus, 500);
    };

    // Toggle expansion state for a container
    const toggleExpand = (containerId: string) => {
        if (expandedContainerIds.value.has(containerId)) {
            expandedContainerIds.value.delete(containerId);
            console.log(`[DockerManager ${sessionId}] Collapsed container ${containerId}.`);
        } else {
            expandedContainerIds.value.add(containerId);
            console.log(`[DockerManager ${sessionId}] Expanded container ${containerId}.`);
        }
    };

    // --- Lifecycle Management ---

    // Reset state function
    const resetStateAndInterval = () => {
        console.log(`[DockerManager ${sessionId}] Resetting state and clearing interval.`);
        containers.value = [];
        isLoading.value = false;
        error.value = null;
        isDockerAvailable.value = true; // Assume available until checked
        expandedContainerIds.value.clear();
        initialLoadDone.value = false;

        if (refreshInterval) {
            clearInterval(refreshInterval);
            refreshInterval = null;
            console.log(`[DockerManager ${sessionId}] Cleared main refresh interval.`);
        }
        clearWsListeners();
    };

    // Watch for connection changes to manage listeners and interval
    watch(isConnected, (newIsConnected, oldIsConnected) => {
        console.log(`[DockerManager ${sessionId}] Connection status changed: ${oldIsConnected} -> ${newIsConnected}`);
        if (newIsConnected) {
            // Connection established
            setupWsListeners();
            requestDockerStatus(); // Fetch initial status

            // Start refresh interval (consider if backend pushes updates reliably)
            if (!refreshInterval) {
                // Keep a safety interval
                refreshInterval = setInterval(requestDockerStatus, 15000); // Check every 15s
                console.log(`[DockerManager ${sessionId}] Main refresh interval started (15s).`);
            }
        } else {
            // Connection lost
            resetStateAndInterval();
            // Set error state to indicate disconnection
            error.value = t('dockerManager.error.sshDisconnected');
            isDockerAvailable.value = false; // Assume unavailable when disconnected
        }
    }, { immediate: false }); // Don't run immediately, let initial connect trigger it

    // Cleanup function to be called when the session ends
    const cleanup = () => {
        console.log(`[DockerManager ${sessionId}] Cleaning up.`);
        resetStateAndInterval(); // Clears listeners and interval
    };

    // --- Initial Setup ---
    // If already connected when this manager is created, set up listeners and fetch data.
    // This handles cases where the manager is created after the WS connection is live.
    if (isConnected.value) {
        console.log(`[DockerManager ${sessionId}] Initial setup: Already connected.`);
        setupWsListeners();
        requestDockerStatus();
        if (!refreshInterval) {
             refreshInterval = setInterval(requestDockerStatus, 15000);
             console.log(`[DockerManager ${sessionId}] Initial setup: Main refresh interval started (15s).`);
        }
    } else {
         console.log(`[DockerManager ${sessionId}] Initial setup: Not connected yet.`);
         // Set initial state for disconnected status
         error.value = t('dockerManager.error.sshDisconnected');
         isDockerAvailable.value = false;
    }


    // --- Exposed Interface ---
    return {
        // Readonly State
        containers: readonly(containers),
        isLoading: readonly(isLoading),
        error: readonly(error),
        isDockerAvailable: readonly(isDockerAvailable),
        expandedContainerIds: readonly(expandedContainerIds), // UI needs this read-only

        // Methods
        requestDockerStatus, // Might be useful for manual refresh button in UI
        sendDockerCommand,
        toggleExpand, // UI needs this to handle clicks

        // Lifecycle
        cleanup,
    };
}

// Export the type of the returned manager instance
export type DockerManagerInstance = ReturnType<typeof createDockerManager>;