import { create } from 'zustand'
import { persist } from 'zustand/middleware'

export type TaskType = 'idea' | 'task' | 'bug' | 'research' | 'plan'
export type TaskStatus = 'open' | 'todo' | 'blocked' | 'in-progress' | 'review' | 'validate' | 'archived' | 'canceled' | 'done'
export type Priority = 'low' | 'medium' | 'high' | 'urgent'
export type SprintStatus = 'planning' | 'active' | 'completed'
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i

export interface Sprint {
  id: string
  name: string
  goal?: string
  startDate: string
  endDate: string
  status: SprintStatus
  createdAt: string
}

export interface Comment {
  id: string
  text: string
  createdAt: string
  commentAuthorId: string
  replies?: Comment[]
}

export interface UserProfile {
  id: string
  name: string
  email?: string
  avatarUrl?: string
}

export interface TaskAttachment {
  id: string
  name: string
  type: string
  size: number
  dataUrl: string
  uploadedAt: string
}

export interface Task {
  id: string
  title: string
  description?: string
  type: TaskType
  status: TaskStatus
  priority: Priority
  projectId: string
  sprintId?: string
  createdAt: string
  updatedAt: string
  createdById?: string
  createdByName?: string
  createdByAvatarUrl?: string
  updatedById?: string
  updatedByName?: string
  updatedByAvatarUrl?: string
  assigneeId?: string
  assigneeName?: string
  assigneeEmail?: string
  assigneeAvatarUrl?: string
  dueDate?: string
  comments: Comment[]
  tags: string[]
  attachments?: TaskAttachment[]
}

export interface Project {
  id: string
  name: string
  description?: string
  color: string
  createdAt: string
}

interface TaskStore {
  projects: Project[]
  tasks: Task[]
  sprints: Sprint[]
  selectedProjectId: string | null
  selectedTaskId: string | null
  selectedSprintId: string | null
  currentUser: UserProfile
  isLoading: boolean
  lastSynced: number | null
  syncError: string | null

  // Sync actions
  syncFromServer: (options?: { includeFullTaskData?: boolean; scope?: "all" | "active-sprint"; taskId?: string }) => Promise<void>
  setCurrentUser: (user: Partial<UserProfile>) => void

  // Project actions
  addProject: (name: string, description?: string) => void
  updateProject: (id: string, updates: Partial<Project>) => void
  deleteProject: (id: string) => void
  selectProject: (id: string | null) => void

  // Task actions
  addTask: (task: Omit<Task, 'id' | 'createdAt' | 'updatedAt' | 'comments'>) => void
  updateTask: (id: string, updates: Partial<Task>) => Promise<boolean>
  deleteTask: (id: string) => void
  selectTask: (id: string | null) => void

  // Sprint actions
  addSprint: (sprint: Omit<Sprint, 'id' | 'createdAt' | 'status'>) => void
  updateSprint: (id: string, updates: Partial<Sprint>) => void
  deleteSprint: (id: string) => void
  selectSprint: (id: string | null) => void
  getTasksBySprint: (sprintId: string) => Task[]

  // Comment actions
  addComment: (taskId: string, text: string, commentAuthorId: string) => void
  deleteComment: (taskId: string, commentId: string) => void

  // Filters
  getTasksByProject: (projectId: string) => Task[]
  getTasksByStatus: (status: TaskStatus) => Task[]
  getTaskById: (id: string) => Task | undefined
}

const defaultCurrentUser: UserProfile = {
  id: '',
  name: 'Unknown User',
}

const normalizeUserProfile = (value: unknown, fallback: UserProfile = defaultCurrentUser): UserProfile => {
  if (!value || typeof value !== 'object') return fallback
  const candidate = value as Partial<UserProfile>
  const id = typeof candidate.id === 'string' && candidate.id.trim().length > 0 ? candidate.id : fallback.id
  const name = typeof candidate.name === 'string' && candidate.name.trim().length > 0 ? candidate.name.trim() : fallback.name
  const email = typeof candidate.email === 'string' && candidate.email.trim().length > 0 ? candidate.email.trim() : undefined
  const avatarUrl = typeof candidate.avatarUrl === 'string' && candidate.avatarUrl.trim().length > 0 ? candidate.avatarUrl : undefined
  return { id, name, email, avatarUrl }
}

const profileToCommentAuthor = (profile: UserProfile) => ({
  id: profile.id,
  name: profile.name,
  avatarUrl: profile.avatarUrl,
})

const normalizeCommentAuthorId = (value: unknown): string | null =>
  typeof value === 'string' && value.trim().length > 0 ? value.trim() : null

const normalizeComments = (value: unknown): Comment[] => {
  if (!Array.isArray(value)) return []

  const comments: Comment[] = []
  
  for (const entry of value) {
    if (!entry || typeof entry !== 'object') continue
    const candidate = entry as Partial<Comment>
    if (typeof candidate.id !== 'string' || typeof candidate.text !== 'string' || typeof candidate.createdAt !== 'string') continue
    const commentAuthorId = normalizeCommentAuthorId(candidate.commentAuthorId)
    if (!commentAuthorId) continue

    comments.push({
      id: candidate.id,
      text: candidate.text,
      createdAt: candidate.createdAt,
      commentAuthorId,
      replies: normalizeComments(candidate.replies),
    })
  }
  
  return comments
}

const normalizeAttachments = (value: unknown): TaskAttachment[] => {
  if (!Array.isArray(value)) return []

  return value
    .map((entry) => {
      if (!entry || typeof entry !== 'object') return null
      const candidate = entry as Partial<TaskAttachment>
      if (typeof candidate.id !== 'string' || typeof candidate.name !== 'string' || typeof candidate.dataUrl !== 'string') {
        return null
      }

      return {
        id: candidate.id,
        name: candidate.name,
        type: typeof candidate.type === 'string' ? candidate.type : 'application/octet-stream',
        size: typeof candidate.size === 'number' ? candidate.size : 0,
        dataUrl: candidate.dataUrl,
        uploadedAt: typeof candidate.uploadedAt === 'string' ? candidate.uploadedAt : new Date().toISOString(),
      }
    })
    .filter((attachment): attachment is TaskAttachment => attachment !== null)
}

const normalizeTask = (task: Task): Task => ({
  ...task,
  comments: normalizeComments(task.comments),
  tags: Array.isArray(task.tags) ? task.tags.filter((tag): tag is string => typeof tag === 'string' && tag.trim().length > 0) : [],
  attachments: normalizeAttachments(task.attachments),
  createdById: typeof task.createdById === 'string' && task.createdById.trim().length > 0 ? task.createdById : undefined,
  createdByName: typeof task.createdByName === 'string' && task.createdByName.trim().length > 0 ? task.createdByName : undefined,
  createdByAvatarUrl: typeof task.createdByAvatarUrl === 'string' && task.createdByAvatarUrl.trim().length > 0 ? task.createdByAvatarUrl : undefined,
  updatedById: typeof task.updatedById === 'string' && task.updatedById.trim().length > 0 ? task.updatedById : undefined,
  updatedByName: typeof task.updatedByName === 'string' && task.updatedByName.trim().length > 0 ? task.updatedByName : undefined,
  updatedByAvatarUrl: typeof task.updatedByAvatarUrl === 'string' && task.updatedByAvatarUrl.trim().length > 0 ? task.updatedByAvatarUrl : undefined,
  assigneeId: typeof task.assigneeId === 'string' && task.assigneeId.trim().length > 0 ? task.assigneeId : undefined,
  assigneeName: typeof task.assigneeName === 'string' && task.assigneeName.trim().length > 0 ? task.assigneeName : undefined,
  assigneeEmail: typeof task.assigneeEmail === 'string' && task.assigneeEmail.trim().length > 0 ? task.assigneeEmail : undefined,
  assigneeAvatarUrl: typeof task.assigneeAvatarUrl === 'string' && task.assigneeAvatarUrl.trim().length > 0 ? task.assigneeAvatarUrl : undefined,
})

const removeCommentFromThread = (comments: Comment[], targetId: string): Comment[] =>
  comments
    .filter((comment) => comment.id !== targetId)
    .map((comment) => ({
      ...comment,
      replies: removeCommentFromThread(normalizeComments(comment.replies), targetId),
    }))

const generateTaskId = (): string => {
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
    return crypto.randomUUID()
  }
  throw new Error('Unable to generate task UUID in this browser')
}

const getErrorMessage = (payload: unknown, fallback: string): string => {
  if (!payload || typeof payload !== 'object') return fallback
  const candidate = payload as { error?: unknown }
  return typeof candidate.error === 'string' && candidate.error.trim().length > 0 ? candidate.error : fallback
}

async function requestApi(path: string, init: RequestInit): Promise<unknown> {
  const response = await fetch(path, init)
  if (response.ok) {
    return response.json().catch(() => ({}))
  }

  const payload = await response.json().catch(() => null)
  const message = getErrorMessage(payload, `${path} failed with status ${response.status}`)
  throw new Error(message)
}

// Helper to sync a single task to server (lightweight)
async function syncTaskToServer(task: Task) {
  console.log('>>> syncTaskToServer: saving task', task.id, task.title)
  const isValidUuid = (value: string | undefined) => typeof value === 'string' && UUID_PATTERN.test(value)
  if (!isValidUuid(task.id)) {
    console.error('>>> syncTaskToServer: invalid task.id (expected UUID)', task.id)
    return false
  }
  if (!isValidUuid(task.projectId)) {
    console.error('>>> syncTaskToServer: invalid task.projectId (expected UUID)', task.projectId)
    return false
  }
  if (task.sprintId && !isValidUuid(task.sprintId)) {
    console.error('>>> syncTaskToServer: invalid task.sprintId (expected UUID)', task.sprintId)
    return false
  }
  if (task.assigneeId && !isValidUuid(task.assigneeId)) {
    console.error('>>> syncTaskToServer: invalid task.assigneeId (expected UUID)', task.assigneeId)
    return false
  }
  try {
    const res = await fetch('/api/tasks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ task }),
    })
    if (res.ok) {
      console.log('>>> syncTaskToServer: saved successfully')
      return true
    } else {
      const rawBody = await res.text().catch(() => '')
      let errorPayload: unknown = null
      if (rawBody) {
        try {
          errorPayload = JSON.parse(rawBody)
        } catch {
          errorPayload = { rawBody }
        }
      }
      console.error('>>> syncTaskToServer: failed with status', res.status, errorPayload)
      return false
    }
  } catch (error) {
    console.error('>>> syncTaskToServer: Failed to sync:', error)
    return false
  }
}

// Helper to delete a task from server
async function deleteTaskFromServer(taskId: string) {
  console.log('>>> deleteTaskFromServer: deleting task', taskId)
  try {
    const res = await fetch('/api/tasks', {
      method: 'DELETE',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id: taskId }),
    })
    if (res.ok) {
      console.log('>>> deleteTaskFromServer: deleted successfully')
      return true
    } else {
      console.error('>>> deleteTaskFromServer: failed with status', res.status)
      return false
    }
  } catch (error) {
    console.error('>>> deleteTaskFromServer: Failed to delete:', error)
    return false
  }
}

export const useTaskStore = create<TaskStore>()(
  persist(
    (set, get) => ({
      projects: [],
      tasks: [],
      sprints: [],
      selectedProjectId: null,
      selectedTaskId: null,
      selectedSprintId: null,
      currentUser: defaultCurrentUser,
      isLoading: false,
      lastSynced: null,
      syncError: null,

      syncFromServer: async (options) => {
        console.log('>>> syncFromServer START')
        set({ isLoading: true, syncError: null })
        try {
          const params = new URLSearchParams()
          if (options?.includeFullTaskData) params.set('include', 'detail')
          if (options?.scope && options.scope !== 'all') params.set('scope', options.scope)
          if (options?.taskId) params.set('taskId', options.taskId)
          const query = params.toString()
          const res = await fetch(`/api/tasks${query ? `?${query}` : ''}`, { cache: 'no-store' })
          console.log('>>> syncFromServer: API response status:', res.status)
          if (!res.ok) {
            const errorPayload = await res.json().catch(() => ({}))
            console.error('>>> syncFromServer: API error payload:', errorPayload)
            const message = typeof errorPayload?.error === 'string'
              ? errorPayload.error
              : `syncFromServer failed with status ${res.status}`
            throw new Error(message)
          }

          const data = await res.json()
          const serverProjects: Project[] = data.projects || []
          const serverCurrentUser = normalizeUserProfile(data.currentUser, get().currentUser)
          const currentSelectedProjectId = get().selectedProjectId
          const nextSelectedProjectId =
            currentSelectedProjectId && serverProjects.some((project) => project.id === currentSelectedProjectId)
              ? currentSelectedProjectId
              : (serverProjects[0]?.id ?? null)

          console.log('>>> syncFromServer: fetched data:', {
            projectsCount: serverProjects.length,
            tasksCount: data.tasks?.length,
            sprintsCount: data.sprints?.length,
            firstTaskTitle: data.tasks?.[0]?.title,
            lastUpdated: data.lastUpdated,
          })
          console.log('>>> syncFromServer: current store tasks count BEFORE set:', get().tasks.length)
          set({
            projects: serverProjects,
            tasks: (data.tasks || []).map((task: Task) => normalizeTask(task)),
            sprints: data.sprints || [],
            currentUser: serverCurrentUser,
            selectedProjectId: nextSelectedProjectId,
            syncError: null,
            lastSynced: Date.now(),
          })
          console.log('>>> syncFromServer: store tasks count AFTER set:', get().tasks.length)
        } catch (error) {
          console.error('>>> syncFromServer: Failed to sync from server:', error)
          const message = error instanceof Error ? error.message : 'Unknown sync error'
          set({
            projects: [],
            tasks: [],
            sprints: [],
            selectedProjectId: null,
            selectedTaskId: null,
            selectedSprintId: null,
            currentUser: defaultCurrentUser,
            syncError: message,
          })
        } finally {
          set({ isLoading: false })
          console.log('>>> syncFromServer END')
        }
      },

      setCurrentUser: (user) => {
        set((state) => ({ currentUser: normalizeUserProfile(user, state.currentUser) }))
      },

      addProject: (name, description) => {
        const colors = ['#8b5cf6', '#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#ec4899', '#06b6d4']
        const color = colors[Math.floor(Math.random() * colors.length)]

        void (async () => {
          try {
            await requestApi('/api/projects', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ name, description, color }),
            })
            await get().syncFromServer()
          } catch (error) {
            const message = error instanceof Error ? error.message : 'Failed to create project'
            console.error('>>> addProject failed:', error)
            set({ syncError: message })
          }
        })()
      },

      updateProject: (id, updates) => {
        const payload: Record<string, unknown> = { id }
        if (updates.name !== undefined) payload.name = updates.name
        if (updates.description !== undefined) payload.description = updates.description
        if (updates.color !== undefined) payload.color = updates.color

        void (async () => {
          try {
            await requestApi('/api/projects', {
              method: 'PATCH',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(payload),
            })
            await get().syncFromServer()
          } catch (error) {
            const message = error instanceof Error ? error.message : 'Failed to update project'
            console.error('>>> updateProject failed:', error)
            set({ syncError: message })
          }
        })()
      },

      deleteProject: (id) => {
        void (async () => {
          try {
            await requestApi('/api/projects', {
              method: 'DELETE',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ id }),
            })
            await get().syncFromServer()
          } catch (error) {
            const message = error instanceof Error ? error.message : 'Failed to delete project'
            console.error('>>> deleteProject failed:', error)
            set({ syncError: message })
          }
        })()
      },

      selectProject: (id) => set({ selectedProjectId: id, selectedTaskId: null, selectedSprintId: null }),

      addTask: (task) => {
        const actor = profileToCommentAuthor(get().currentUser)
        const newTask: Task = {
          ...task,
          id: generateTaskId(),
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          createdById: actor.id,
          createdByName: actor.name,
          createdByAvatarUrl: actor.avatarUrl,
          updatedById: actor.id,
          updatedByName: actor.name,
          updatedByAvatarUrl: actor.avatarUrl,
          assigneeId: task.assigneeId,
          assigneeName: task.assigneeName,
          assigneeEmail: task.assigneeEmail,
          assigneeAvatarUrl: task.assigneeAvatarUrl,
          comments: normalizeComments([]),
          attachments: normalizeAttachments(task.attachments),
        }
        set((state) => {
          const newTasks = [...state.tasks, newTask]
          void (async () => {
            const success = await syncTaskToServer(newTask)
            if (!success) {
              await get().syncFromServer()
            }
          })()
          return { tasks: newTasks }
        })
      },

      updateTask: async (id, updates) => {
        console.log('updateTask called:', id, updates)
        let syncSuccess = false
        let updatedTask: Task | undefined

        set((state) => {
          const actor = profileToCommentAuthor(state.currentUser)
          const newTasks = state.tasks.map((t) =>
            t.id === id
              ? normalizeTask({
                  ...t,
                  ...updates,
                  comments: updates.comments !== undefined ? normalizeComments(updates.comments) : t.comments,
                  attachments: updates.attachments !== undefined ? normalizeAttachments(updates.attachments) : t.attachments,
                  updatedAt: new Date().toISOString(),
                  updatedById: actor.id,
                  updatedByName: actor.name,
                  updatedByAvatarUrl: actor.avatarUrl,
                } as Task)
              : t
          )
          updatedTask = newTasks.find(t => t.id === id)
          console.log('updateTask: updated task:', updatedTask)
          return { tasks: newTasks }
        })

        // Sync individual task to server (lightweight)
        if (updatedTask) {
          syncSuccess = await syncTaskToServer(updatedTask)
          if (!syncSuccess) {
            await get().syncFromServer()
          }
        }

        return syncSuccess
      },

      deleteTask: (id) => {
        set((state) => {
          const newTasks = state.tasks.filter((t) => t.id !== id)
          void (async () => {
            const success = await deleteTaskFromServer(id)
            if (!success) {
              await get().syncFromServer()
            }
          })()
          return {
            tasks: newTasks,
            selectedTaskId: state.selectedTaskId === id ? null : state.selectedTaskId,
          }
        })
      },

      selectTask: (id) => set({ selectedTaskId: id }),

      // Sprint actions
      addSprint: (sprint) => {
        void (async () => {
          try {
            const payload = {
              name: sprint.name,
              goal: sprint.goal,
              startDate: sprint.startDate,
              endDate: sprint.endDate,
            }
            await requestApi('/api/sprints', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(payload),
            })
            await get().syncFromServer()
          } catch (error) {
            const message = error instanceof Error ? error.message : 'Failed to create sprint'
            console.error('>>> addSprint failed:', error)
            set({ syncError: message })
          }
        })()
      },

      updateSprint: (id, updates) => {
        void (async () => {
          try {
            const payload: Record<string, unknown> = { id }
            if (updates.name !== undefined) payload.name = updates.name
            if (updates.goal !== undefined) payload.goal = updates.goal
            if (updates.startDate !== undefined) payload.startDate = updates.startDate
            if (updates.endDate !== undefined) payload.endDate = updates.endDate

            await requestApi('/api/sprints', {
              method: 'PATCH',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(payload),
            })
            await get().syncFromServer()
          } catch (error) {
            const message = error instanceof Error ? error.message : 'Failed to update sprint'
            console.error('>>> updateSprint failed:', error)
            set({ syncError: message })
          }
        })()
      },

      deleteSprint: (id) => {
        void (async () => {
          try {
            await requestApi('/api/sprints', {
              method: 'DELETE',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ id }),
            })
            await get().syncFromServer()
          } catch (error) {
            const message = error instanceof Error ? error.message : 'Failed to delete sprint'
            console.error('>>> deleteSprint failed:', error)
            set({ syncError: message })
          }
        })()
      },

      selectSprint: (id) => set({ selectedSprintId: id }),

      getTasksBySprint: (sprintId) => {
        return get().tasks.filter((t) => t.sprintId === sprintId)
      },

      addComment: (taskId, text, commentAuthorId) => {
        const normalizedCommentAuthorId = normalizeCommentAuthorId(commentAuthorId)
        if (!normalizedCommentAuthorId) {
          const message = 'Failed to add comment: invalid comment author id'
          console.error(message, { taskId, commentAuthorId })
          set({ syncError: message })
          return
        }
        const newComment: Comment = {
          id: Date.now().toString(),
          text,
          createdAt: new Date().toISOString(),
          commentAuthorId: normalizedCommentAuthorId,
          replies: [],
        }
        set((state) => {
          const updater = profileToCommentAuthor(state.currentUser)
          const newTasks = state.tasks.map((t) =>
            t.id === taskId
              ? {
                  ...t,
                  comments: [...normalizeComments(t.comments), newComment],
                  updatedAt: new Date().toISOString(),
                  updatedById: updater.id,
                  updatedByName: updater.name,
                  updatedByAvatarUrl: updater.avatarUrl,
                }
              : t
          )
          const updatedTask = newTasks.find(t => t.id === taskId)
          if (updatedTask) {
            void (async () => {
              const success = await syncTaskToServer(updatedTask)
              if (!success) {
                await get().syncFromServer()
              }
            })()
          }
          return { tasks: newTasks }
        })
      },

      deleteComment: (taskId, commentId) => {
        set((state) => {
          const updater = profileToCommentAuthor(state.currentUser)
          const newTasks = state.tasks.map((t) =>
            t.id === taskId
              ? {
                  ...t,
                  comments: removeCommentFromThread(normalizeComments(t.comments), commentId),
                  updatedAt: new Date().toISOString(),
                  updatedById: updater.id,
                  updatedByName: updater.name,
                  updatedByAvatarUrl: updater.avatarUrl,
                }
              : t
          )
          const updatedTask = newTasks.find(t => t.id === taskId)
          if (updatedTask) {
            void (async () => {
              const success = await syncTaskToServer(updatedTask)
              if (!success) {
                await get().syncFromServer()
              }
            })()
          }
          return { tasks: newTasks }
        })
      },

      getTasksByProject: (projectId) => {
        return get().tasks.filter((t) => t.projectId === projectId)
      },

      getTasksByStatus: (status) => {
        return get().tasks.filter((t) => t.status === status)
      },

      getTaskById: (id) => {
        return get().tasks.find((t) => t.id === id)
      },
    }),
    {
      name: 'task-store',
      version: 1,
      migrate: (persistedState) => {
        if (!persistedState || typeof persistedState !== 'object') {
          return {}
        }

        const state = persistedState as {
          selectedProjectId?: unknown
          selectedTaskId?: unknown
          selectedSprintId?: unknown
        }

        return {
          selectedProjectId: typeof state.selectedProjectId === 'string' ? state.selectedProjectId : null,
          selectedTaskId: typeof state.selectedTaskId === 'string' ? state.selectedTaskId : null,
          selectedSprintId: typeof state.selectedSprintId === 'string' ? state.selectedSprintId : null,
        }
      },
      partialize: (state) => ({
        // Persist UI selection state only. All business data comes from Supabase.
        selectedProjectId: state.selectedProjectId,
        selectedTaskId: state.selectedTaskId,
        selectedSprintId: state.selectedSprintId,
      }),
      onRehydrateStorage: () => {
        console.log('>>> PERSIST: onRehydrateStorage called')
        return (state, error) => {
          if (error) {
            console.log('>>> PERSIST: Rehydration error:', error)
          } else {
            console.log('>>> PERSIST: Rehydrated state:', {
              selectedProjectId: state?.selectedProjectId,
              selectedTaskId: state?.selectedTaskId,
              selectedSprintId: state?.selectedSprintId,
            })
          }
        }
      },
    }
  )
)
