import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { Chore, ChoreCreate, ChoreUpdate, ChoreType, ChoreAssignment } from '@/types/chore'
import { choreService } from '@/services/choreService'
import { apiClient, API_ENDPOINTS } from '@/services/api'
import type { TimeEntry } from '@/types/time_entry'
import { useSocket } from '@/composables/useSocket'
import { useFairness } from '@/composables/useFairness'
import { useOptimisticUpdates } from '@/composables/useOptimisticUpdates'
import { useNotificationStore } from '@/stores/notifications'
import { useAuthStore } from '@/stores/auth'

export const useChoreStore = defineStore('chores', () => {
    // ---- State ----
    const personal = ref<Chore[]>([])
    const groupById = ref<Record<number, Chore[]>>({})
    const isLoading = ref(false)
    const error = ref<string | null>(null)
    const timeEntriesByAssignment = ref<Record<number, TimeEntry[]>>({})

    // Real-time state
    const { isConnected, on, off, emit } = useSocket()
    const { getNextAssignee } = useFairness()
    const notificationStore = useNotificationStore()
    const authStore = useAuthStore()

    // Point tracking for gamification
    const pointsByUser = ref<Record<number, number>>({})
    const streaksByUser = ref<Record<number, number>>({})

    // Optimistic updates state
    const { data: _choreUpdates, mutate: _mutate, rollback: _rollback } = useOptimisticUpdates<Chore>([])

    // ---- Enhanced Getters ----
    const allChores = computed<Chore[]>(() => [
        ...personal.value,
        ...Object.values(groupById.value).flat(),
    ])

    const choresByPriority = computed(() => {
        const now = new Date()
        return allChores.value
            .filter(chore => !chore.assignments.some(a => a.is_complete))
            .sort((a, b) => {
                // Priority scoring: overdue > due today > upcoming
                const aScore = getChoreScore(a, now)
                const bScore = getChoreScore(b, now)
                return bScore - aScore
            })
    })

    const overdueChores = computed(() => {
        const now = new Date()
        return allChores.value.filter(chore => {
            const dueDate = chore.next_due_date ? new Date(chore.next_due_date) : null
            return dueDate && dueDate < now && !chore.assignments.some(a => a.is_complete)
        })
    })

    const upcomingChores = computed(() => {
        const now = new Date()
        const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
        return allChores.value.filter(chore => {
            const dueDate = chore.next_due_date ? new Date(chore.next_due_date) : null
            return dueDate && dueDate >= now && dueDate <= nextWeek && !chore.assignments.some(a => a.is_complete)
        })
    })

    const availableChores = computed(() => {
        return allChores.value.filter(chore =>
            chore.assignments.length === 0 ||
            !chore.assignments.some(a => a.is_complete || a.assigned_to_user_id)
        )
    })

    const activeTimerEntry = computed<TimeEntry | null>(() => {
        for (const assignmentId in timeEntriesByAssignment.value) {
            const entry = timeEntriesByAssignment.value[assignmentId].find((te) => !te.end_time);
            if (entry) return entry;
        }
        return null;
    });

    // ---- Helper Functions ----
    function getChoreScore(chore: Chore, now: Date): number {
        const dueDate = chore.next_due_date ? new Date(chore.next_due_date) : null
        if (!dueDate) return 0

        const timeDiff = dueDate.getTime() - now.getTime()
        const daysDiff = timeDiff / (1000 * 60 * 60 * 24)

        if (daysDiff < 0) return 100 + Math.abs(daysDiff) // Overdue
        if (daysDiff < 1) return 50 // Due today
        if (daysDiff < 7) return 25 // Due this week
        return 10 // Future
    }

    function setError(message: string) {
        error.value = message
        isLoading.value = false
    }

    // ---- Real-time Event Handlers ----
    function setupWebSocketListeners() {
        on('chore:created', handleChoreCreated)
        on('chore:updated', handleChoreUpdated)
        on('chore:deleted', handleChoreDeleted)
        on('chore:assigned', handleChoreAssigned)
        on('chore:assignment_updated', handleChoreAssigned) // Reuse the same handler
        on('chore:completed', handleChoreCompleted)
        on('timer:started', handleTimerStarted)
        on('timer:stopped', handleTimerStopped)
    }

    function cleanupWebSocketListeners() {
        off('chore:created', handleChoreCreated)
        off('chore:updated', handleChoreUpdated)
        off('chore:deleted', handleChoreDeleted)
        off('chore:assigned', handleChoreAssigned)
        off('chore:assignment_updated', handleChoreAssigned)
        off('chore:completed', handleChoreCompleted)
        off('timer:started', handleTimerStarted)
        off('timer:stopped', handleTimerStopped)
    }

    function handleChoreCreated(payload: { chore: Chore }) {
        const { chore } = payload
        if (chore.type === 'personal') {
            personal.value.push(chore)
        } else if (chore.group_id != null) {
            if (!groupById.value[chore.group_id]) groupById.value[chore.group_id] = []
            groupById.value[chore.group_id].push(chore)
        }
    }

    function handleChoreUpdated(payload: { chore: Chore }) {
        const { chore } = payload
        updateChoreInStore(chore)
    }

    function handleChoreDeleted(payload: { choreId: number, groupId?: number }) {
        const { choreId, groupId } = payload
        if (groupId) {
            groupById.value[groupId] = (groupById.value[groupId] || []).filter(c => c.id !== choreId)
        } else {
            personal.value = personal.value.filter(c => c.id !== choreId)
        }
    }

    function handleChoreAssigned(payload: { assignment: ChoreAssignment }) {
        const { assignment } = payload
        const chore = findChoreById(assignment.chore_id)
        if (chore) {
            const existingIndex = chore.assignments.findIndex(a => a.id === assignment.id)
            if (existingIndex >= 0) {
                chore.assignments[existingIndex] = assignment
            } else {
                chore.assignments.push(assignment)
            }

            // Show notification if assigned to current user
            if (assignment.assigned_to_user_id === authStore.user?.id) {
                notificationStore.addNotification({
                    message: `You have been assigned "${chore.name}"`,
                    type: 'info'
                })
            }
        }
    }

    function handleChoreCompleted(payload: { assignment: ChoreAssignment, points: number }) {
        const { assignment, points } = payload
        handleChoreAssigned({ assignment })

        // Update points and streaks
        if (assignment.assigned_to_user_id) {
            pointsByUser.value[assignment.assigned_to_user_id] =
                (pointsByUser.value[assignment.assigned_to_user_id] || 0) + points
            streaksByUser.value[assignment.assigned_to_user_id] =
                (streaksByUser.value[assignment.assigned_to_user_id] || 0) + 1
        }

        // Show notification for completed chore
        const chore = findChoreById(assignment.chore_id)
        if (chore && assignment.assigned_to_user_id !== authStore.user?.id) {
            // Only show notification if someone else completed it
            notificationStore.addNotification({
                message: `"${chore.name}" was completed!`,
                type: 'success'
            })
        }
    }

    function handleTimerStarted(payload: { timeEntry: TimeEntry }) {
        const { timeEntry } = payload
        if (!timeEntriesByAssignment.value[timeEntry.chore_assignment_id]) {
            timeEntriesByAssignment.value[timeEntry.chore_assignment_id] = []
        }
        timeEntriesByAssignment.value[timeEntry.chore_assignment_id].push(timeEntry)
    }

    function handleTimerStopped(payload: { timeEntry: TimeEntry }) {
        const { timeEntry } = payload
        const entries = timeEntriesByAssignment.value[timeEntry.chore_assignment_id] || []
        const index = entries.findIndex(t => t.id === timeEntry.id)
        if (index > -1) {
            entries[index] = timeEntry
        }
    }

    function findChoreById(choreId: number): Chore | undefined {
        return allChores.value.find(c => c.id === choreId)
    }

    function updateChoreInStore(chore: Chore) {
        if (chore.type === 'personal') {
            const index = personal.value.findIndex(c => c.id === chore.id)
            if (index >= 0) personal.value[index] = chore
        } else if (chore.group_id != null) {
            const groupChores = groupById.value[chore.group_id] || []
            const index = groupChores.findIndex(c => c.id === chore.id)
            if (index >= 0) groupChores[index] = chore
        }
    }

    // ---- Enhanced Actions ----
    async function fetchPersonal() {
        isLoading.value = true
        error.value = null
        try {
            personal.value = await choreService.getPersonalChores()
        } catch (e: any) {
            setError(e?.message || 'Failed to load personal chores')
        } finally {
            isLoading.value = false
        }
    }

    async function fetchGroup(groupId: number) {
        isLoading.value = true
        error.value = null
        try {
            const chores = await choreService.getChores(groupId)
            groupById.value[groupId] = chores
        } catch (e: any) {
            setError(e?.message || 'Failed to load group chores')
        } finally {
            isLoading.value = false
        }
    }

    async function create(chore: ChoreCreate) {
        try {
            // Optimistic update - add temporarily
            const tempId = -Date.now()
            const tempChore = { ...chore, id: tempId } as Chore

            if (chore.type === 'personal') {
                personal.value.push(tempChore)
            } else if (chore.group_id != null) {
                if (!groupById.value[chore.group_id]) groupById.value[chore.group_id] = []
                groupById.value[chore.group_id].push(tempChore)
            }

            const created = await choreService.createChore(chore)

            // Replace temp with real
            if (created.type === 'personal') {
                const index = personal.value.findIndex(c => c.id === tempId)
                if (index >= 0) personal.value[index] = created
            } else if (created.group_id != null) {
                const groupChores = groupById.value[created.group_id] || []
                const index = groupChores.findIndex(c => c.id === tempId)
                if (index >= 0) groupChores[index] = created
            }

            // Emit real-time event
            emit('chore:created', { chore: created })
            return created
        } catch (e: any) {
            // Rollback optimistic update
            if (chore.type === 'personal') {
                personal.value = personal.value.filter(c => c.id > 0)
            } else if (chore.group_id != null) {
                groupById.value[chore.group_id] = (groupById.value[chore.group_id] || [])
                    .filter(c => c.id > 0)
            }
            setError(e?.message || 'Failed to create chore')
            throw e
        }
    }

    async function update(choreId: number, updates: ChoreUpdate, original: Chore) {
        try {
            // Optimistic update
            const updated = { ...original, ...updates }
            updateChoreInStore(updated)

            const result = await choreService.updateChore(choreId, updates, original)
            updateChoreInStore(result)

            // Emit real-time event
            emit('chore:updated', { chore: result })
            return result
        } catch (e: any) {
            // Rollback
            updateChoreInStore(original)
            setError(e?.message || 'Failed to update chore')
            throw e
        }
    }

    async function remove(choreId: number, choreType: ChoreType, groupId?: number) {
        try {
            // Optimistic removal
            const _originalChore = findChoreById(choreId)
            if (choreType === 'personal') {
                personal.value = personal.value.filter((c) => c.id !== choreId)
            } else if (groupId != null) {
                groupById.value[groupId] = (groupById.value[groupId] || []).filter((c) => c.id !== choreId)
            }

            await choreService.deleteChore(choreId, choreType, groupId)

            // Emit real-time event
            emit('chore:deleted', { choreId, groupId })
        } catch (e: any) {
            // Rollback - would need to restore original chore
            if (groupId) await fetchGroup(groupId)
            else await fetchPersonal()
            setError(e?.message || 'Failed to delete chore')
            throw e
        }
    }

    async function smartAssignChore(choreId: number, _groupId: number) {
        try {
            // For now, get members from group store or API
            // This would be expanded with proper member fetching
            const members = [{ id: 1, name: 'User 1' }, { id: 2, name: 'User 2' }] // Placeholder
            const nextAssignee = getNextAssignee(members)
            if (!nextAssignee) throw new Error('No available assignee')

            const assignment = await choreService.createAssignment({
                chore_id: choreId,
                assigned_to_user_id: Number(nextAssignee.id),
                due_date: new Date().toISOString(),
            })

            // Emit real-time event
            emit('chore:assigned', { assignment })
            return assignment
        } catch (e: any) {
            setError(e?.message || 'Failed to assign chore')
            throw e
        }
    }

    async function fetchTimeEntries(assignmentId: number) {
        try {
            const response = await apiClient.get(`${API_ENDPOINTS.CHORES.ASSIGNMENT_BY_ID(assignmentId)}/time-entries`)
            timeEntriesByAssignment.value[assignmentId] = response.data
        } catch (e: any) {
            setError(e?.message || `Failed to fetch time entries for assignment ${assignmentId}`)
        }
    }

    async function startTimer(assignmentId: number) {
        try {
            const response = await apiClient.post(`${API_ENDPOINTS.CHORES.ASSIGNMENT_BY_ID(assignmentId)}/time-entries`)
            if (!timeEntriesByAssignment.value[assignmentId]) {
                timeEntriesByAssignment.value[assignmentId] = []
            }
            timeEntriesByAssignment.value[assignmentId].push(response.data)

            // Emit real-time event
            emit('timer:started', { timeEntry: response.data })
            return response.data
        } catch (e: any) {
            setError(e?.message || `Failed to start timer for assignment ${assignmentId}`)
            throw e
        }
    }

    async function stopTimer(assignmentId: number, timeEntryId: number) {
        try {
            const response = await apiClient.put(`${API_ENDPOINTS.CHORES.TIME_ENTRY(timeEntryId)}`)
            const entries = timeEntriesByAssignment.value[assignmentId] || []
            const index = entries.findIndex((t) => t.id === timeEntryId)
            if (index > -1) {
                entries[index] = response.data
            }

            // Emit real-time event
            emit('timer:stopped', { timeEntry: response.data })
            return response.data
        } catch (e: any) {
            setError(e?.message || `Failed to stop timer for entry ${timeEntryId}`)
            throw e
        }
    }

    async function toggleCompletion(chore: Chore, assignment: ChoreAssignment | null) {
        try {
            let currentAssignment = assignment;
            if (!currentAssignment) {
                // create assignment default user
                currentAssignment = await choreService.createAssignment({
                    chore_id: chore.id,
                    assigned_to_user_id: chore.created_by_id,
                    due_date: chore.next_due_date,
                });
            }
            const newStatus = !currentAssignment.is_complete;
            let updatedAssignment: ChoreAssignment;

            if (newStatus) {
                updatedAssignment = await choreService.completeAssignment(currentAssignment.id);

                // Calculate and emit points
                const points = calculateCompletionPoints(chore, updatedAssignment)
                emit('chore:completed', { assignment: updatedAssignment, points })
            } else {
                updatedAssignment = await choreService.updateAssignment(currentAssignment.id, { is_complete: false });
            }

            // update local state: find chore and update assignment status.
            function applyToList(list: Chore[]) {
                const cIndex = list.findIndex((c) => c.id === chore.id);
                if (cIndex === -1) return;
                const c = list[cIndex];
                const aIndex = c.assignments.findIndex((a) => a.id === updatedAssignment.id);
                if (aIndex !== -1) {
                    c.assignments[aIndex] = updatedAssignment;
                }
            }
            if (chore.type === 'personal') {
                applyToList(personal.value);
            } else if (chore.group_id != null) {
                applyToList(groupById.value[chore.group_id] || []);
            }
            return updatedAssignment;
        } catch (e: any) {
            setError(e?.message || 'Failed to toggle completion');
            throw e;
        }
    }

    function calculateCompletionPoints(chore: Chore, assignment: ChoreAssignment): number {
        let points = 1 // Base points

        // Bonus for completing on time
        const dueDate = new Date(assignment.due_date)
        const completedDate = new Date(assignment.completed_at!)
        if (completedDate <= dueDate) {
            points += 1
        }

        // Bonus for streak
        if (assignment.assigned_to_user_id) {
            const streak = streaksByUser.value[assignment.assigned_to_user_id] || 0
            if (streak >= 5) points += 2
            else if (streak >= 3) points += 1
        }

        return points
    }

    // Initialize WebSocket listeners
    setupWebSocketListeners()

    return {
        // State
        personal,
        groupById,
        allChores,
        isLoading,
        error,
        timeEntriesByAssignment,
        pointsByUser,
        streaksByUser,

        // Enhanced getters
        choresByPriority,
        overdueChores,
        upcomingChores,
        availableChores,
        activeTimerEntry,

        // Actions
        fetchPersonal,
        fetchGroup,
        create,
        update,
        remove,
        smartAssignChore,
        setError,
        fetchTimeEntries,
        startTimer,
        stopTimer,
        toggleCompletion,

        // Real-time
        isConnected,
        setupWebSocketListeners,
        cleanupWebSocketListeners,
    }
}) 