import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { apiClient, API_ENDPOINTS } from '@/services/api';
import type { List } from '@/types/list';
import type { Item } from '@/types/item';
import type { Expense, SettlementActivityCreate } from '@/types/expense';
import { useAuthStore } from './auth';
import { useSocket } from '@/composables/useSocket';
import { useOptimisticUpdates } from '@/composables/useOptimisticUpdates';
import { API_BASE_URL } from '@/config/api-config';

export interface ListWithDetails extends List {
    items: Item[];
    expenses: Expense[];
}

export const useListsStore = defineStore('lists', () => {
    // --- STATE ---
    const allLists = ref<List[]>([]);
    const currentList = ref<ListWithDetails | null>(null);
    const isLoading = ref(false);
    const error = ref<string | null>(null);
    const isSettlingSplit = ref(false);

    // Real-time state
    const { isConnected, on, off, emit } = useSocket();
    const { data: _itemUpdates, mutate: _mutate, rollback: _rollback } = useOptimisticUpdates<Item>([]);

    // Collaboration state
    const activeUsers = ref<Record<string, { id: number, name: string, lastSeen: Date }>>({});
    const itemsBeingEdited = ref<Record<number, { userId: number, userName: string }>>({});

    // Properties to support legacy polling logic in ListDetailPage, will be removed later.
    const lastListUpdate = ref<string | null>(null);
    const lastItemCount = ref<number | null>(null);

    // WebSocket state for list-specific connections
    let listWebSocket: WebSocket | null = null;
    let currentListId: number | null = null;

    // --- Enhanced Getters ---
    const items = computed(() => currentList.value?.items || []);
    const expenses = computed(() => currentList.value?.expenses || []);

    const itemsByCategory = computed(() => {
        const categories: Record<string, Item[]> = {};
        items.value.forEach(item => {
            // Use category_id for now since category object may not be populated
            const categoryName = item.category_id ? `Category ${item.category_id}` : 'Uncategorized';
            if (!categories[categoryName]) categories[categoryName] = [];
            categories[categoryName].push(item);
        });
        return categories;
    });

    const completedItems = computed(() => items.value.filter(item => item.is_complete));
    const pendingItems = computed(() => items.value.filter(item => !item.is_complete));
    const claimedItems = computed(() => items.value.filter(item => item.claimed_by_user_id));

    const totalEstimatedCost = computed(() => {
        return items.value.reduce((total, item) => {
            return total + (item.price ? parseFloat(String(item.price)) : 0);
        }, 0);
    });

    const listSummary = computed(() => ({
        totalItems: items.value.length,
        completedItems: completedItems.value.length,
        claimedItems: claimedItems.value.length,
        estimatedCost: totalEstimatedCost.value,
        activeCollaborators: Object.keys(activeUsers.value).length
    }));

    // --- Helper Functions ---
    function getPaidAmountForSplit(splitId: number): number {
        let totalPaid = 0;
        if (currentList.value && currentList.value.expenses) {
            for (const expense of currentList.value.expenses) {
                const split = expense.splits.find((s) => s.id === splitId);
                if (split && split.settlement_activities) {
                    totalPaid = split.settlement_activities.reduce((sum, activity) => {
                        return sum + parseFloat(activity.amount_paid);
                    }, 0);
                    break;
                }
            }
        }
        return totalPaid;
    }

    // --- WebSocket Connection Management ---
    function connectWebSocket(listId: number, token: string) {
        // Disconnect any existing connection
        disconnectWebSocket();

        currentListId = listId;
        // Build WebSocket URL using the same host as the HTTP request. The backend exposes
        // the list-scoped endpoint at /api/v1/ws/list/{id}, so we mirror that here.
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        const host = window.location.host;
        const wsUrl = `${protocol}//${host}/api/v1/ws/list/${listId}?token=${encodeURIComponent(token)}`;

        console.log('Connecting to list WebSocket:', wsUrl);
        listWebSocket = new WebSocket(wsUrl);

        listWebSocket.addEventListener('open', () => {
            console.log('List WebSocket connected for list:', listId);
            // Inform backend that we joined this list room
            joinListRoom(listId);
        });

        listWebSocket.addEventListener('close', (event) => {
            console.log('List WebSocket disconnected:', event.code, event.reason);
            listWebSocket = null;

            // Auto-reconnect if unexpected disconnect and still on the same list
            if (currentListId === listId && event.code !== 1000) {
                setTimeout(() => {
                    if (currentListId === listId) {
                        const authStore = useAuthStore();
                        if (authStore.accessToken) {
                            connectWebSocket(listId, authStore.accessToken || '');
                        }
                    }
                }, 2000);
            }
        });

        listWebSocket.addEventListener('message', (event) => {
            try {
                const data = JSON.parse(event.data);
                console.log('Received WebSocket message:', data);

                // Handle the message based on its type
                if (data.event && data.payload) {
                    handleWebSocketMessage(data.event, data.payload);
                }
            } catch (err) {
                console.error('Failed to parse WebSocket message:', err);
            }
        });

        listWebSocket.addEventListener('error', (error) => {
            console.error('List WebSocket error:', error);
        });
    }

    function disconnectWebSocket() {
        if (listWebSocket) {
            console.log('Disconnecting list WebSocket');
            listWebSocket.close(1000); // Normal closure
            listWebSocket = null;
        }
        currentListId = null;
        activeUsers.value = {};
        itemsBeingEdited.value = {};
    }

    function handleWebSocketMessage(event: string, payload: any) {
        switch (event) {
            case 'item_added':
            case 'list:item_added':
                handleItemAdded(payload);
                break;
            case 'item_updated':
            case 'list:item_updated':
                handleItemUpdated(payload);
                break;
            case 'item_deleted':
            case 'list:item_deleted':
                handleItemDeleted(payload);
                break;
            case 'item_claimed':
            case 'list:item_claimed':
                handleItemClaimed(payload);
                break;
            case 'item_unclaimed':
            case 'list:item_unclaimed':
                handleItemUnclaimed(payload);
                break;
            case 'item_reordered':
            case 'list:item_reordered':
                handleItemReordered(payload);
                break;
            case 'list:user_joined':
                handleUserJoined(payload);
                break;
            case 'list:user_left':
                handleUserLeft(payload);
                break;
            case 'list:item_editing_started':
                handleItemEditingStarted(payload);
                break;
            case 'list:item_editing_stopped':
                handleItemEditingStopped(payload);
                break;
            default:
                console.log('Unhandled WebSocket event:', event, payload);
        }
    }

    function sendWebSocketMessage(event: string, payload: any) {
        if (listWebSocket && listWebSocket.readyState === WebSocket.OPEN) {
            listWebSocket.send(JSON.stringify({ event, payload }));
        } else {
            console.warn('WebSocket not connected, cannot send message:', event);
        }
    }

    // --- Real-time Event Handlers ---
    // Event handlers are now called directly from handleWebSocketMessage

    function handleItemAdded(payload: { item: Item }) {
        if (!currentList.value) return;
        const existingIndex = currentList.value.items.findIndex(i => i.id === payload.item.id);
        if (existingIndex === -1) {
            currentList.value.items.push(payload.item);
            lastItemCount.value = currentList.value.items.length;
        }
    }

    function handleItemUpdated(payload: { item: Item }) {
        if (!currentList.value) return;
        const index = currentList.value.items.findIndex(i => i.id === payload.item.id);
        if (index >= 0) {
            currentList.value.items[index] = payload.item;
        }
    }

    function handleItemDeleted(payload: { itemId: number }) {
        if (!currentList.value) return;
        currentList.value.items = currentList.value.items.filter(i => i.id !== payload.itemId);
        lastItemCount.value = currentList.value.items.length;
    }

    function handleItemClaimed(payload: { itemId: number, claimedBy: { id: number, name: string, email: string }, claimedAt: string, version: number }) {
        if (!currentList.value) return;
        const item = currentList.value.items.find((i: Item) => i.id === payload.itemId);
        if (item) {
            item.claimed_by_user_id = payload.claimedBy.id;
            item.claimed_by_user = payload.claimedBy;
            item.claimed_at = payload.claimedAt;
            item.version = payload.version;
        }
    }

    function handleItemUnclaimed(payload: { itemId: number, version: number }) {
        const item = items.value.find((i: Item) => i.id === payload.itemId);
        if (item) {
            item.claimed_by_user_id = null;
            item.claimed_by_user = null;
            item.claimed_at = null;
            item.version = payload.version;
        }
    }

    function handleItemReordered(payload: { listId: number, orderedIds: number[] }) {
        if (!currentList.value) return;
        const { orderedIds } = payload;

        // Optimistic update: reorder items in local state
        const reorderedItems = orderedIds.map(id =>
            currentList.value!.items.find(item => item.id === id)
        ).filter(Boolean) as Item[];

        // Add any items not in the ordered list (shouldn't happen but safety first)
        const missingItems = currentList.value.items.filter(item =>
            !orderedIds.includes(Number(item.id))
        );

        currentList.value.items = [...reorderedItems, ...missingItems];
    }

    function handleUserJoined(payload: { user: { id: number, name: string } }) {
        activeUsers.value[payload.user.id] = {
            ...payload.user,
            lastSeen: new Date()
        };
    }

    function handleUserLeft(payload: { userId: number }) {
        delete activeUsers.value[payload.userId];
        // Clean up any editing locks from this user
        Object.keys(itemsBeingEdited.value).forEach(itemId => {
            if (itemsBeingEdited.value[Number(itemId)].userId === payload.userId) {
                delete itemsBeingEdited.value[Number(itemId)];
            }
        });
    }

    function handleItemEditingStarted(payload: { itemId: number, user: { id: number, name: string } }) {
        itemsBeingEdited.value[payload.itemId] = {
            userId: payload.user.id,
            userName: payload.user.name
        };
    }

    function handleItemEditingStopped(payload: { itemId: number }) {
        delete itemsBeingEdited.value[payload.itemId];
    }

    // --- Enhanced Actions ---
    async function fetchAllLists() {
        isLoading.value = true;
        error.value = null;
        try {
            const response = await apiClient.get(API_ENDPOINTS.LISTS.BASE);
            allLists.value = response.data;
        } catch (err: any) {
            error.value = err.response?.data?.detail || 'Failed to fetch lists.';
        } finally {
            isLoading.value = false;
        }
    }

    async function fetchListDetails(listId: string) {
        isLoading.value = true;
        error.value = null;
        try {
            const [listResponse, itemsResponse, expensesResponse] = await Promise.all([
                apiClient.get(API_ENDPOINTS.LISTS.BY_ID(listId)),
                apiClient.get(API_ENDPOINTS.LISTS.ITEMS(listId)),
                apiClient.get(API_ENDPOINTS.LISTS.EXPENSES(listId))
            ]);

            currentList.value = {
                ...listResponse.data,
                items: itemsResponse.data,
                expenses: expensesResponse.data,
            };

            // Update polling properties
            lastListUpdate.value = listResponse.data.updated_at;
            lastItemCount.value = itemsResponse.data.length;

            // Join real-time list room
            joinListRoom(Number(listId));

        } catch (err: any) {
            error.value = err.response?.data?.detail || 'Failed to fetch list details.';
            console.error(err);
        } finally {
            isLoading.value = false;
        }
    }

    function joinListRoom(listId: number) {
        // Notify server that user joined this list
        sendWebSocketMessage('list:join', { listId });
    }

    function leaveListRoom() {
        if (currentList.value) {
            sendWebSocketMessage('list:leave', { listId: currentList.value.id });
        }
        activeUsers.value = {};
        itemsBeingEdited.value = {};
    }

    async function claimItem(itemId: number) {
        const item = currentList.value?.items.find((i: Item) => i.id === itemId);
        if (!item || !currentList.value) return;

        const originalClaimedById = item.claimed_by_user_id;
        const authStore = useAuthStore();

        // Optimistic update
        const userId = Number(authStore.user!.id);
        item.claimed_by_user_id = userId;
        item.claimed_by_user = { id: userId, name: authStore.user!.name, email: authStore.user!.email };
        item.claimed_at = new Date().toISOString();

        try {
            const response = await apiClient.post(`/items/${itemId}/claim`);
            item.version = response.data.version;

            // Emit real-time event so other browser tabs update immediately (backend will
            // also broadcast its own event).
            sendWebSocketMessage('item_claimed', {
                itemId,
                claimedBy: item.claimed_by_user,
                claimedAt: item.claimed_at,
                version: item.version
            });
        } catch (err: any) {
            // Rollback optimistic update
            item.claimed_by_user_id = originalClaimedById;
            item.claimed_by_user = null;
            item.claimed_at = null;
            error.value = err.response?.data?.detail || 'Failed to claim item.';
        }
    }

    async function unclaimItem(itemId: number) {
        const item = currentList.value?.items.find((i: Item) => i.id === itemId);
        if (!item) return;

        const originalClaimedById = item.claimed_by_user_id;
        const originalClaimedByUser = item.claimed_by_user;
        const originalClaimedAt = item.claimed_at;

        // Optimistic update
        item.claimed_by_user_id = null;
        item.claimed_by_user = null;
        item.claimed_at = null;

        try {
            const response = await apiClient.delete(`/items/${itemId}/claim`);
            item.version = response.data.version;

            // Emit real-time event so other browser tabs update immediately (backend will
            // also broadcast its own event).
            sendWebSocketMessage('item_unclaimed', { itemId, version: item.version });
        } catch (err: any) {
            // Rollback
            item.claimed_by_user_id = originalClaimedById;
            item.claimed_by_user = originalClaimedByUser;
            item.claimed_at = originalClaimedAt;
            error.value = err.response?.data?.detail || 'Failed to unclaim item.';
        }
    }

    async function addItem(listId: string | number, payload: { name: string; quantity?: string | null; category_id?: number | null }) {
        if (!currentList.value) return;
        error.value = null;

        try {
            // Optimistic: push placeholder item with temporary id (-Date.now())
            const tempId = -Date.now();
            const tempItem: Item = {
                id: tempId,
                name: payload.name,
                quantity: payload.quantity ?? null,
                is_complete: false,
                price: null,
                list_id: Number(listId),
                category_id: payload.category_id ?? null,
                created_at: new Date().toISOString(),
                updated_at: new Date().toISOString(),
                completed_at: null,
                version: 0,
                added_by_id: useAuthStore().user?.id ?? 0,
                completed_by_id: null,
            } as unknown as Item;

            currentList.value.items.push(tempItem);

            const response = await apiClient.post(API_ENDPOINTS.LISTS.ITEMS(String(listId)), payload);

            // Replace temp item with actual item
            const index = currentList.value.items.findIndex(i => i.id === tempId);
            if (index !== -1) {
                currentList.value.items[index] = response.data;
            } else {
                currentList.value.items.push(response.data);
            }

            // Update lastListUpdate etc.
            lastItemCount.value = currentList.value.items.length;
            lastListUpdate.value = response.data.updated_at;

            // Emit real-time event
            sendWebSocketMessage('item_added', { item: response.data });

            return response.data;
        } catch (err: any) {
            // Rollback optimistic
            if (currentList.value) {
                currentList.value.items = currentList.value.items.filter(i => i.id >= 0);
            }
            error.value = err.response?.data?.detail || 'Failed to add item.';
            throw err;
        }
    }

    async function updateItem(listId: string | number, itemId: number, updates: Partial<Item> & { version: number }) {
        if (!currentList.value) return;
        error.value = null;

        const item = currentList.value.items.find(i => i.id === itemId);
        if (!item) return;

        const original = { ...item };

        // Start editing lock
        startEditingItem(itemId);

        // Optimistic merge
        Object.assign(item, updates);

        try {
            const response = await apiClient.put(API_ENDPOINTS.LISTS.ITEM(String(listId), String(itemId)), updates);

            // ensure store item updated with server version
            const index = currentList.value.items.findIndex(i => i.id === itemId);
            if (index !== -1) {
                currentList.value.items[index] = response.data;
            }

            // Emit real-time event
            sendWebSocketMessage('item_updated', { item: response.data });

            return response.data;
        } catch (err: any) {
            // rollback
            const index = currentList.value.items.findIndex(i => i.id === itemId);
            if (index !== -1) {
                currentList.value.items[index] = original;
            }
            error.value = err.response?.data?.detail || 'Failed to update item.';
            throw err;
        } finally {
            // Stop editing lock
            stopEditingItem(itemId);
        }
    }

    async function deleteItem(listId: string | number, itemId: number, version: number) {
        if (!currentList.value) return;
        error.value = null;

        const index = currentList.value.items.findIndex(i => i.id === itemId);
        if (index === -1) return;

        const [removed] = currentList.value.items.splice(index, 1);

        try {
            await apiClient.delete(`${API_ENDPOINTS.LISTS.ITEM(String(listId), String(itemId))}?expected_version=${version}`);
            lastItemCount.value = currentList.value.items.length;

            // Emit real-time event
            sendWebSocketMessage('item_deleted', { itemId });

            return true;
        } catch (err: any) {
            // rollback
            currentList.value.items.splice(index, 0, removed);
            error.value = err.response?.data?.detail || 'Failed to delete item.';
            throw err;
        }
    }

    function startEditingItem(itemId: number) {
        const authStore = useAuthStore();
        const user = authStore.user;
        if (user) {
            sendWebSocketMessage('list:item_editing_started', {
                itemId,
                user: { id: user.id, name: user.name }
            });
        }
    }

    function stopEditingItem(itemId: number) {
        sendWebSocketMessage('list:item_editing_stopped', { itemId });
    }

    function isItemBeingEdited(itemId: number): boolean {
        return itemId in itemsBeingEdited.value;
    }

    function getItemEditor(itemId: number): string | null {
        const editor = itemsBeingEdited.value[itemId];
        return editor ? editor.userName : null;
    }

    async function settleExpenseSplit(payload: { expense_split_id: number; activity_data: SettlementActivityCreate }) {
        isSettlingSplit.value = true;
        error.value = null;
        try {
            const endpoint = `/financials/expense_splits/${payload.expense_split_id}/settle`;
            await apiClient.post(endpoint, payload.activity_data);
            if (currentList.value?.id) {
                await fetchListDetails(String(currentList.value.id));
            }
            return true;
        } catch (err: any) {
            error.value = err.response?.data?.detail || 'Failed to settle expense split.';
            return false;
        } finally {
            isSettlingSplit.value = false;
        }
    }

    async function reorderItems(listId: string | number, orderedItemIds: number[]) {
        if (!currentList.value) return;

        const originalItems = [...currentList.value.items];

        // Optimistic update: reorder items in local state
        const reorderedItems = orderedItemIds.map(id =>
            currentList.value!.items.find(item => item.id === id)
        ).filter(Boolean) as Item[];

        // Add any items not in the ordered list (shouldn't happen but safety first)
        const missingItems = currentList.value.items.filter(item =>
            !orderedItemIds.includes(Number(item.id))
        );

        currentList.value.items = [...reorderedItems, ...missingItems];

        try {
            // Call API to persist the reorder on the items endpoint
            await apiClient.put(`${API_ENDPOINTS.LISTS.ITEMS(String(listId))}/reorder`, {
                ordered_ids: orderedItemIds
            });

            // Emit WebSocket event for real-time updates to other users
            if (listWebSocket) {
                listWebSocket.send(JSON.stringify({
                    type: 'item_reordered',
                    data: { listId: Number(listId), orderedIds: orderedItemIds }
                }));
            }
        } catch (error) {
            // Rollback on failure
            currentList.value.items = originalItems;
            console.error('Failed to reorder items:', error);
            throw error;
        }
    }

    // WebSocket listeners are now handled directly in connectWebSocket

    return {
        // State
        allLists,
        currentList,
        items,
        expenses,
        isLoading,
        error,
        isSettlingSplit,
        lastListUpdate,
        lastItemCount,
        activeUsers,
        itemsBeingEdited,

        // Enhanced getters
        itemsByCategory,
        completedItems,
        pendingItems,
        claimedItems,
        totalEstimatedCost,
        listSummary,

        // Actions
        fetchAllLists,
        fetchListDetails,
        joinListRoom,
        leaveListRoom,
        claimItem,
        unclaimItem,
        addItem,
        updateItem,
        deleteItem,
        startEditingItem,
        stopEditingItem,
        isItemBeingEdited,
        getItemEditor,
        settleExpenseSplit,
        reorderItems,
        getPaidAmountForSplit,

        // Real-time
        isConnected,
        connectWebSocket,
        disconnectWebSocket,
    };
}); 