import { createEffect, createEvent, createStore, sample } from 'effector'
import { apiClient } from '@/shared/api/client'
import { createErrorStore } from '@/shared/lib/create-error-store'
import { persistToken } from '@/shared/lib/persist-token'

export type AuthUser = {
  id: string
  email: string
  displayName?: string | null
  avatar?: string | null
  avatarStyle?: string | null
  isAdmin?: boolean
}

export type AuthMethod = {
  type: 'password' | 'vk' | 'yandex'
  active: boolean
}

export const tokenSet = createEvent<string | null>()
export const logout = createEvent()

// ----- Token persistence -----

const persistTokenFx = createEffect<string | null, void>({
  handler: (token) => persistToken(token),
})

export const $token = createStore<string | null>(null)
  .on(tokenSet, (_, t) => t)
  .reset(logout)

sample({
  clock: $token,
  target: persistTokenFx,
})

// ----- User -----

export const $user = createStore<AuthUser | null>(null).reset(logout)

export const meFx = createEffect(async (token: string) => {
  const { data } = await apiClient.get<{ user: AuthUser | null }>('me', {
    headers: { Authorization: `Bearer ${token}` },
  })
  return data.user
})

sample({
  clock: tokenSet,
  filter: (t): t is string => Boolean(t),
  target: meFx,
})

sample({
  clock: meFx.doneData,
  target: $user,
})

// ----- Profile update -----

export type UpdateProfileParams = {
  displayName: string | null
  avatar?: string | null
  avatarStyle?: string | null
}

export const updateProfileFx = createEffect(async (params: UpdateProfileParams) => {
  const { data } = await apiClient.patch<{ user: AuthUser }>('me/profile', params)
  return data.user
})

// ----- Auth effects -----

export const loginFx = createEffect(async (params: { email: string; password: string }) => {
  const { data } = await apiClient.post<{ token: string; user: AuthUser }>('auth/login', params)
  tokenSet(data.token)
  return data.user
})

export const registerFx = createEffect(async (params: { email: string; password: string; displayName?: string }) => {
  const { data } = await apiClient.post<{ token: string; user: AuthUser }>('auth/register', params)
  tokenSet(data.token)
  return data.user
})

export const fetchAuthMethodsFx = createEffect(async () => {
  const { data } = await apiClient.get<{ methods: AuthMethod[] }>('me/auth-methods')
  return data.methods
})

export const setPasswordFx = createEffect(async (password: string) => {
  await apiClient.post('me/password', { password })
})

export const unlinkOAuthFx = createEffect(async (provider: 'vk' | 'yandex') => {
  await apiClient.delete(`me/oauth/${provider}`)
})

export const changePasswordFx = createEffect(async (params: { oldPassword: string; newPassword: string }) => {
  await apiClient.post('me/change-password', params)
})

export const requestEmailChangeFx = createEffect(async (email: string) => {
  const { data } = await apiClient.patch<{ verificationUrl: string }>('me/email', { email })
  return data.verificationUrl
})

// ----- Error stores -----

export const $updateProfileError = createErrorStore(updateProfileFx).$error

// ----- Re-exports -----

export { readStoredToken } from '@/shared/lib/persist-token'

// ----- Sync user from profile changes -----

sample({
  clock: [updateProfileFx.doneData],
  target: $user,
})
