import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { expenseService } from '@/services/expenseService'
import type {
    Expense,
    ExpenseCreate,
    ExpenseUpdate,
    SettlementActivity,
    SettlementActivityCreate,
    Settlement,
    SettlementCreate,
    ExpenseSplit,
    SplitType,
    ExpenseOverallStatus
} from '@/types/unified-expense'
import { useSocket } from '@/composables/useSocket'
import { useConflictResolution } from '@/composables/useConflictResolution'
import { useAuthStore } from './auth'

export const useExpensesStore = defineStore('expenses', () => {
    // ----- Enhanced State with Unified Types -----
    const expenses = ref<Expense[]>([])
    const settlements = ref<Settlement[]>([])
    const isLoading = ref(false)
    const error = ref<string | null>(null)

    // Real-time collaboration state
    const activeExpenseEditSessions = ref<Record<number, {
        user_id: number;
        user_name: string;
        editing_field?: string
    }>>({})

    // Enhanced debt tracking
    const pendingSettlements = ref<Record<string, {
        amount: number;
        from_user_id: number;
        to_user_id: number;
        due_date: Date
    }>>({})

    // Real-time and conflict resolution
    const { on, off, isConnected, startEditing, stopEditing } = useSocket()
    const {
        trackOptimisticUpdate,
        confirmOptimisticUpdate,
        failOptimisticUpdate,
        resolveConflict
    } = useConflictResolution()

    // Auth store for current user context
    const authStore = useAuthStore()

    // ----- Enhanced Getters with Unified Types -----
    const recurringExpenses = computed(() =>
        expenses.value.filter(e => e.is_recurring)
    )

    const expensesByGroup = computed(() => {
        const map: Record<number, Expense[]> = {}
        for (const exp of expenses.value) {
            if (exp.group_id != null) {
                if (!map[exp.group_id]) map[exp.group_id] = []
                map[exp.group_id].push(exp)
            }
        }
        return map
    })

    // Advanced balance calculations with enhanced debt flow tracking
    const detailedBalanceByUser = computed(() => {
        const balance: Record<number, {
            total_owed: number,
            total_owing: number,
            net_balance: number,
            splits_by_expense: Record<number, ExpenseSplit>,
            settlement_history: SettlementActivity[]
        }> = {}

        // Initialize users from all splits
        for (const exp of expenses.value) {
            for (const split of exp.splits) {
                if (!balance[split.user_id]) {
                    balance[split.user_id] = {
                        total_owed: 0,
                        total_owing: 0,
                        net_balance: 0,
                        splits_by_expense: {},
                        settlement_history: []
                    }
                }
            }
        }

        // Calculate balances from expense splits using unified types
        for (const exp of expenses.value) {
            for (const split of exp.splits) {
                const owed = parseFloat(split.owed_amount)

                // Calculate paid amount from settlement activities
                const paidAmount = split.settlement_activities.reduce(
                    (sum, activity) => sum + parseFloat(activity.amount_paid),
                    0
                )

                const netAmount = owed - paidAmount

                balance[split.user_id].total_owed += owed
                balance[split.user_id].total_owing += paidAmount
                balance[split.user_id].net_balance += netAmount
                balance[split.user_id].splits_by_expense[exp.id] = split

                // Add settlement activities to history
                balance[split.user_id].settlement_history.push(...split.settlement_activities)
            }
        }

        return balance
    })

    // Current user's financial summary
    const currentUserBalance = computed(() => {
        const userId = authStore.user?.id
        if (!userId) return null

        return detailedBalanceByUser.value[userId as number] || {
            total_owed: 0,
            total_owing: 0,
            net_balance: 0,
            splits_by_expense: {},
            settlement_history: []
        }
    })

    // Enhanced money flow analysis
    const debtFlowAnalysis = computed(() => {
        const flows: Array<{
            from: number,
            to: number,
            amount: number,
            expense_count: number
        }> = []
        const userTotals: Record<number, number> = {}

        for (const exp of expenses.value) {
            // Find who paid for this expense
            const paidBy = exp.paid_by_user_id

            for (const split of exp.splits) {
                if (split.user_id === paidBy) continue

                const owed = parseFloat(split.owed_amount)
                const paidAmount = split.settlement_activities.reduce(
                    (sum, activity) => sum + parseFloat(activity.amount_paid),
                    0
                )
                const amount = owed - paidAmount

                if (amount <= 0) continue // Already settled

                const existingFlow = flows.find(f => f.from === split.user_id && f.to === paidBy)

                if (existingFlow) {
                    existingFlow.amount += amount
                    existingFlow.expense_count += 1
                } else {
                    flows.push({
                        from: split.user_id,
                        to: paidBy,
                        amount,
                        expense_count: 1
                    })
                }

                userTotals[split.user_id] = (userTotals[split.user_id] || 0) - amount
                userTotals[paidBy] = (userTotals[paidBy] || 0) + amount
            }
        }

        return {
            flows: flows.filter(f => f.amount > 0.01),
            user_totals: userTotals,
            biggest_debtor: Object.entries(userTotals)
                .reduce((a, b) => userTotals[parseInt(a[0])] < userTotals[parseInt(b[0])] ? a : b)?.[0],
            biggest_creditor: Object.entries(userTotals)
                .reduce((a, b) => userTotals[parseInt(a[0])] > userTotals[parseInt(b[0])] ? a : b)?.[0]
        }
    })

    // ----- Real-time Event Handlers -----
    function setupWebSocketListeners() {
        on('expense:created', handleExpenseCreated)
        on('expense:updated', handleExpenseUpdated)
        on('expense:deleted', handleExpenseDeleted)
        on('expense:split_settled', handleSplitSettled)
        on('expense:editing_started', handleEditingStarted)
        on('expense:editing_stopped', handleEditingStopped)
        on('settlement:created', handleSettlementCreated)
        on('settlement:updated', handleSettlementUpdated)
    }

    function cleanupWebSocketListeners() {
        off('expense:created', handleExpenseCreated)
        off('expense:updated', handleExpenseUpdated)
        off('expense:deleted', handleExpenseDeleted)
        off('expense:split_settled', handleSplitSettled)
        off('expense:editing_started', handleEditingStarted)
        off('expense:editing_stopped', handleEditingStopped)
        off('settlement:created', handleSettlementCreated)
        off('settlement:updated', handleSettlementUpdated)
    }

    function handleExpenseCreated(payload: { expense: Expense }) {
        // Check if we already have this expense (avoid duplicates)
        const existingIndex = expenses.value.findIndex(e => e.id === payload.expense.id)
        if (existingIndex === -1) {
            expenses.value.unshift(payload.expense)
        }
    }

    function handleExpenseUpdated(payload: { expense: Expense }) {
        const index = findIndex(payload.expense.id)
        if (index >= 0) {
            expenses.value[index] = payload.expense
        }
    }

    function handleExpenseDeleted(payload: { expense_id: number }) {
        const index = findIndex(payload.expense_id)
        if (index >= 0) {
            expenses.value.splice(index, 1)
        }
    }

    function handleSplitSettled(payload: {
        expense_id: number;
        split_id: number;
        settlement: SettlementActivity
    }) {
        const expense = expenses.value.find(e => e.id === payload.expense_id)
        if (expense) {
            const split = expense.splits.find(s => s.id === payload.split_id)
            if (split) {
                split.settlement_activities.push(payload.settlement)

                // Update split status based on total settlements
                const totalPaid = split.settlement_activities.reduce(
                    (sum, activity) => sum + parseFloat(activity.amount_paid),
                    0
                )
                const totalOwed = parseFloat(split.owed_amount)

                if (totalPaid >= totalOwed) {
                    split.status = 'paid' as any
                } else if (totalPaid > 0) {
                    split.status = 'partially_paid' as any
                } else {
                    split.status = 'unpaid' as any
                }
            }
        }
    }

    function handleEditingStarted(payload: {
        expense_id: number;
        user_id: number;
        user_name: string;
        field?: string
    }) {
        activeExpenseEditSessions.value[payload.expense_id] = {
            user_id: payload.user_id,
            user_name: payload.user_name,
            editing_field: payload.field
        }
    }

    function handleEditingStopped(payload: { expense_id: number }) {
        delete activeExpenseEditSessions.value[payload.expense_id]
    }

    function handleSettlementCreated(payload: { settlement: Settlement }) {
        settlements.value.push(payload.settlement)
    }

    function handleSettlementUpdated(payload: { settlement: Settlement }) {
        const index = settlements.value.findIndex(s => s.id === payload.settlement.id)
        if (index >= 0) {
            settlements.value[index] = payload.settlement
        }
    }

    // ----- Helper Functions -----
    function findIndex(id: number) {
        return expenses.value.findIndex(e => e.id === id)
    }

    function setError(message: string) {
        error.value = message
        console.error('[ExpensesStore]', message)
    }

    // ----- Enhanced Actions with Conflict Resolution -----
    async function fetchExpenses(params?: {
        group_id?: number
        list_id?: number
        isRecurring?: boolean
        skip?: number
        limit?: number
    }) {
        isLoading.value = true
        error.value = null

        try {
            const data = await expenseService.getExpenses(params)
            expenses.value = data.sort((a, b) =>
                new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
            )
            setupWebSocketListeners()
        } catch (err: any) {
            setError(err.message || 'Failed to fetch expenses')
            throw err
        } finally {
            isLoading.value = false
        }
    }

    async function createExpense(expenseData: ExpenseCreate) {
        const tempId = -Date.now() // Negative ID for optimistic updates

        const optimisticExpense = {
            id: tempId,
            description: expenseData.description,
            total_amount: expenseData.total_amount.toString(),
            currency: expenseData.currency || 'USD',
            expense_date: expenseData.expense_date || new Date().toISOString(),
            split_type: expenseData.split_type,
            list_id: expenseData.list_id ?? null,
            group_id: expenseData.group_id ?? null,
            item_id: expenseData.item_id ?? null,
            paid_by_user_id: expenseData.paid_by_user_id,
            created_by_user_id: authStore.user?.id as number,
            overall_settlement_status: 'unpaid' as ExpenseOverallStatus,
            is_recurring: expenseData.is_recurring || false,
            next_occurrence: null,
            last_occurrence: null,
            parent_expense_id: null,
            created_at: new Date().toISOString(),
            updated_at: new Date().toISOString(),
            version: 1,
            splits: (expenseData.splits_in ?? []).map((split, index) => ({
                id: -index - 1,
                expense_id: tempId,
                user_id: split.user_id,
                owed_amount: split.owed_amount?.toString() || '0',
                share_percentage: split.share_percentage?.toString(),
                share_units: split.share_units,
                status: 'unpaid' as any,
                settlement_activities: [],
                created_at: new Date().toISOString(),
                updated_at: new Date().toISOString(),
                version: 1
            })),
        } as any as Expense

        // Track optimistic update
        const updateId = trackOptimisticUpdate('expense', tempId, 'create', optimisticExpense)

        // Optimistic update
        expenses.value.unshift(optimisticExpense)

        try {
            const newExpense = await expenseService.createExpense(expenseData)

            // Replace optimistic expense with real one
            const index = expenses.value.findIndex(e => e.id === tempId)
            if (index >= 0) {
                expenses.value[index] = newExpense
            }

            // Confirm optimistic update
            confirmOptimisticUpdate(updateId, newExpense)

            return newExpense
        } catch (err: any) {
            // Remove optimistic expense on error
            const index = expenses.value.findIndex(e => e.id === tempId)
            if (index >= 0) {
                expenses.value.splice(index, 1)
            }

            // Mark optimistic update as failed
            failOptimisticUpdate(updateId, null, 0)

            setError(err.message || 'Failed to create expense')
            throw err
        }
    }

    async function updateExpense(id: number, updates: ExpenseUpdate) {
        const index = findIndex(id)
        if (index < 0) return

        const original = { ...expenses.value[index] }
        const updateId = trackOptimisticUpdate('expense', id, 'update', updates)

        // Optimistic update
        expenses.value[index] = { ...original, ...updates }

        try {
            const updated = await expenseService.updateExpense(id, updates)
            expenses.value[index] = updated

            confirmOptimisticUpdate(updateId, updated)
            return updated
        } catch (err: any) {
            // Rollback on error
            expenses.value[index] = original

            // Handle version conflicts
            if (err.status === 409) {
                const conflictId = failOptimisticUpdate(updateId, err.serverData, err.serverVersion)
                if (conflictId) {
                    // Let the user resolve the conflict
                    console.log('Version conflict detected, awaiting user resolution')
                }
            } else {
                failOptimisticUpdate(updateId, null, 0)
            }

            setError(err.message || 'Failed to update expense')
            throw err
        }
    }

    async function deleteExpense(id: number) {
        const index = findIndex(id)
        if (index < 0) return

        const original = expenses.value[index]
        const updateId = trackOptimisticUpdate('expense', id, 'delete', original)

        // Optimistic removal
        expenses.value.splice(index, 1)

        try {
            await expenseService.deleteExpense(id)
            confirmOptimisticUpdate(updateId)
        } catch (err: any) {
            // Restore on error
            expenses.value.splice(index, 0, original)
            failOptimisticUpdate(updateId, original, original.version)
            setError(err.message || 'Failed to delete expense')
            throw err
        }
    }

    // Enhanced settlement workflow with conflict resolution
    async function settleExpenseSplit(
        expenseId: number,
        splitId: number,
        amount: string
    ) {
        const expense = expenses.value.find(e => e.id === expenseId)
        if (!expense) return

        const split = expense.splits.find(s => s.id === splitId)
        if (!split) return

        const originalActivities = [...split.settlement_activities]
        const optimisticActivity: SettlementActivity = {
            id: -Date.now(),
            expense_split_id: splitId,
            paid_by_user_id: authStore.user?.id as number,
            amount_paid: amount,
            paid_at: new Date().toISOString(),
            created_by_user_id: authStore.user?.id as number,
            created_at: new Date().toISOString(),
            updated_at: new Date().toISOString(),
            version: 1
        }

        // Optimistic update
        split.settlement_activities.push(optimisticActivity)

        try {
            const settlement = await expenseService.settleExpenseSplit(expenseId, splitId, amount)

            // Replace optimistic activity with real one
            const activityIndex = split.settlement_activities.findIndex(
                a => a.id === optimisticActivity.id
            )
            if (activityIndex >= 0) {
                split.settlement_activities[activityIndex] = settlement
            }

            return settlement
        } catch (err: any) {
            // Rollback on error
            split.settlement_activities = originalActivities
            setError(err.message || 'Failed to settle expense split')
            throw err
        }
    }

    // Collaborative editing functions
    function startEditingExpense(expenseId: number, field?: string) {
        const userId = authStore.user?.id
        const userName = authStore.user?.name

        if (userId && userName && isConnected.value) {
            activeExpenseEditSessions.value[expenseId] = {
                user_id: userId as number,
                user_name: userName,
                editing_field: field
            }

            startEditing('expense', expenseId, field)
        }
    }

    function stopEditingExpense(expenseId: number) {
        delete activeExpenseEditSessions.value[expenseId]

        if (isConnected.value) {
            stopEditing('expense', expenseId)
        }
    }

    // Cleanup
    function cleanup() {
        cleanupWebSocketListeners()
    }

    return {
        // State
        expenses,
        settlements,
        isLoading,
        error,
        pendingSettlements,
        activeExpenseEditSessions,

        // Getters
        recurringExpenses,
        expensesByGroup,
        detailedBalanceByUser,
        currentUserBalance,
        debtFlowAnalysis,

        // Actions
        fetchExpenses,
        createExpense,
        updateExpense,
        deleteExpense,
        settleExpenseSplit,
        startEditingExpense,
        stopEditingExpense,
        setupWebSocketListeners,
        cleanup
    }
}) 