import { useCallback, useEffect, useState, type Dispatch, type SetStateAction } from 'react'
import { invoke } from '@tauri-apps/api/core'
import { isTauri, mockInvoke } from '../mock-tauri'
import { APP_STORAGE_KEYS, LEGACY_APP_STORAGE_KEYS, getAppStorageItem } from '../constants/appStorage'
import {
  buildGettingStartedVaultPath,
  formatGettingStartedCloneError,
  labelFromPath,
} from '../utils/gettingStartedVault'
import { formatFolderPickerActionError, pickFolder } from '../utils/vault-dialog'

type OnboardingState =
  | { status: 'loading' }
  | { status: 'welcome'; defaultPath: string }
  | { status: 'vault-missing'; vaultPath: string; defaultPath: string }
  | { status: 'ready'; vaultPath: string }

type CreatingAction = 'template' | 'empty' | null
type ReadyVaultSource = 'template' | 'empty' | 'existing'
type OnVaultReady = (vaultPath: string, source: ReadyVaultSource) => void
type RegisterVault = (
  vaultPath: string,
  label: string,
  options?: { verifyAvailability?: boolean },
) => Promise<void>
type SetError = Dispatch<SetStateAction<string | null>>
type SetCreatingAction = Dispatch<SetStateAction<CreatingAction>>

interface TemplateVaultCreationOptions {
  setState: Dispatch<SetStateAction<OnboardingState>>
  setCreatingAction: SetCreatingAction
  setError: SetError
  setLastTemplatePath: Dispatch<SetStateAction<string | null>>
  registerVault?: RegisterVault
  onVaultReady?: OnVaultReady
}

interface OnboardingOptions {
  onVaultReady?: OnVaultReady
  registerVault?: RegisterVault
}

interface ReadyVaultHandlerOptions {
  onVaultReady?: OnVaultReady
  registerVault?: RegisterVault
  setError: SetError
  setState: Dispatch<SetStateAction<OnboardingState>>
}

interface CreateEmptyVaultHandlerOptions extends ReadyVaultHandlerOptions {
  setCreatingAction: SetCreatingAction
}

function tauriCall<T>(command: string, args: Record<string, unknown>): Promise<T> {
  return isTauri() ? invoke<T>(command, args) : mockInvoke<T>(command, args)
}

interface PersistedVaultList {
  vaults?: Array<{ label: string; path: string }>
  active_vault?: string | null
  hidden_defaults?: string[]
}

function wasDismissed(): boolean {
  try {
    return getAppStorageItem('welcomeDismissed') === '1'
  } catch {
    return false
  }
}

function markDismissed(): void {
  try {
    localStorage.setItem(APP_STORAGE_KEYS.welcomeDismissed, '1')
    localStorage.removeItem(LEGACY_APP_STORAGE_KEYS.welcomeDismissed)
  } catch {
    // localStorage may be unavailable in some contexts
  }
}

async function clearMissingActiveVault(missingPath: string): Promise<boolean> {
  try {
    const list = await tauriCall<PersistedVaultList>('load_vault_list', {})
    if (!list || list.active_vault !== missingPath) return false
    await tauriCall('save_vault_list', {
      list: {
        vaults: list.vaults ?? [],
        active_vault: null,
        hidden_defaults: list.hidden_defaults ?? [],
      },
    })
    return true
  } catch (error) {
    void error
    // Best effort only — onboarding should still proceed
    return false
  }
}

function markVaultReady(
  setState: Dispatch<SetStateAction<OnboardingState>>,
  vaultPath: string,
) {
  markDismissed()
  setState({ status: 'ready', vaultPath })
}

function formatOnboardingRegistrationError({
  action,
  err,
}: {
  action: string
  err: unknown
}): string {
  const message =
    typeof err === 'string'
      ? err
      : err instanceof Error
        ? err.message
        : `${err}`

  return message ? `${action}: ${message}` : action
}

async function registerVaultSelection(
  registerVault: RegisterVault | undefined,
  vaultPath: string,
  options?: { verifyAvailability?: boolean },
): Promise<void> {
  if (!registerVault) {
    return
  }

  const label = labelFromPath(vaultPath)
  if (options) {
    await registerVault(vaultPath, label, options)
    return
  }

  await registerVault(vaultPath, label)
}

async function pickFolderWithOnboardingError({
  action,
  setError,
  title,
}: {
  action: string
  setError: SetError
  title: string
}): Promise<string | null> {
  setError(null)

  try {
    return await pickFolder(title)
  } catch (err) {
    setError(formatFolderPickerActionError(action, err))
    return null
  }
}

function useTemplateVaultCreation(
  options: TemplateVaultCreationOptions,
) {
  return useCallback(async (targetPath: string) => {
    options.setCreatingAction('template')
    options.setError(null)
    options.setLastTemplatePath(targetPath)

    try {
      const vaultPath = await tauriCall<string>('create_getting_started_vault', { targetPath })
      try {
        await registerVaultSelection(options.registerVault, vaultPath, { verifyAvailability: false })
      } catch (err) {
        options.setError(formatOnboardingRegistrationError({
          action: 'Could not register the Getting Started vault',
          err,
        }))
        return
      }
      markVaultReady(options.setState, vaultPath)
      options.onVaultReady?.(vaultPath, 'template')
    } catch (err) {
      options.setError(formatGettingStartedCloneError(err))
    } finally {
      options.setCreatingAction(null)
    }
  }, [options])
}

function useCreateVaultHandler(
  createTemplateVault: (targetPath: string) => Promise<void>,
  setError: SetError,
) {
  return useCallback(async () => {
    const parentPath = await pickFolderWithOnboardingError({
      action: 'Could not choose a parent folder',
      setError,
      title: 'Choose a parent folder for the Getting Started vault',
    })
    if (!parentPath) return

    await createTemplateVault(buildGettingStartedVaultPath(parentPath))
  }, [createTemplateVault, setError])
}

function useCreateEmptyVaultHandler(
  options: CreateEmptyVaultHandlerOptions,
) {
  return useCallback(async () => {
    const path = await pickFolderWithOnboardingError({
      action: 'Could not choose where to create your vault',
      setError: options.setError,
      title: 'Choose where to create your vault',
    })
    if (!path) return

    try {
      options.setCreatingAction('empty')
      const vaultPath = await tauriCall<string>('create_empty_vault', { targetPath: path })
      try {
        await registerVaultSelection(options.registerVault, vaultPath, { verifyAvailability: false })
      } catch (err) {
        options.setError(formatOnboardingRegistrationError({
          action: 'Could not register the new vault',
          err,
        }))
        return
      }
      markVaultReady(options.setState, vaultPath)
      options.onVaultReady?.(vaultPath, 'empty')
    } catch (err) {
      options.setError(typeof err === 'string' ? err : `Failed to create vault: ${err}`)
    } finally {
      options.setCreatingAction(null)
    }
  }, [options])
}

function useOpenFolderHandler(
  options: ReadyVaultHandlerOptions,
) {
  return useCallback(async () => {
    const path = await pickFolderWithOnboardingError({
      action: 'Failed to open folder',
      setError: options.setError,
      title: 'Open vault folder',
    })
    if (!path) return

    try {
      await registerVaultSelection(options.registerVault, path)
    } catch (err) {
      options.setError(formatOnboardingRegistrationError({
        action: 'Could not open vault',
        err,
      }))
      return
    }

    markVaultReady(options.setState, path)
    options.onVaultReady?.(path, 'existing')
  }, [options])
}

export function useOnboarding(
  initialVaultPath: string,
  options: OnboardingOptions = {},
  initialVaultResolved = true,
) {
  const [state, setState] = useState<OnboardingState>({ status: 'loading' })
  const [creatingAction, setCreatingAction] = useState<CreatingAction>(null)
  const [error, setError] = useState<string | null>(null)
  const [lastTemplatePath, setLastTemplatePath] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false

    if (!initialVaultResolved) {
      return () => { cancelled = true }
    }

    async function check() {
      try {
        const defaultPath = await tauriCall<string>('get_default_vault_path', {})
        const exists = await tauriCall<boolean>('check_vault_exists', { path: initialVaultPath })

        if (cancelled) return

        if (exists) {
          setState({ status: 'ready', vaultPath: initialVaultPath })
          return
        }

        const missingWasPersistedActiveVault = await clearMissingActiveVault(initialVaultPath)
        if (cancelled) return

        if (wasDismissed() && missingWasPersistedActiveVault) {
          // Only show vault-missing when a previously selected vault path truly disappeared.
          setState({ status: 'vault-missing', vaultPath: initialVaultPath, defaultPath })
        } else {
          setState({ status: 'welcome', defaultPath })
        }
      } catch (error) {
        void error
        // If commands fail (e.g. mock mode), just proceed
        if (!cancelled) setState({ status: 'ready', vaultPath: initialVaultPath })
      }
    }

    check()
    return () => { cancelled = true }
  }, [initialVaultPath, initialVaultResolved])

  const createTemplateVault = useTemplateVaultCreation({
    setState,
    setCreatingAction,
    setError,
    setLastTemplatePath,
    registerVault: options.registerVault,
    onVaultReady: options.onVaultReady,
  })

  const handleCreateVault = useCreateVaultHandler(createTemplateVault, setError)

  const retryCreateVault = useCallback(async () => {
    if (!lastTemplatePath) return
    await createTemplateVault(lastTemplatePath)
  }, [createTemplateVault, lastTemplatePath])

  const handleCreateEmptyVault = useCreateEmptyVaultHandler({
    onVaultReady: options.onVaultReady,
    registerVault: options.registerVault,
    setCreatingAction,
    setError,
    setState,
  })

  const handleOpenFolder = useOpenFolderHandler({
    onVaultReady: options.onVaultReady,
    registerVault: options.registerVault,
    setError,
    setState,
  })

  const handleDismiss = useCallback(() => {
    markDismissed()
    setState({ status: 'ready', vaultPath: initialVaultPath })
  }, [initialVaultPath])

  const resolvedState = initialVaultResolved ? state : { status: 'loading' as const }

  return {
    state: resolvedState,
    creating: creatingAction !== null,
    creatingAction,
    error,
    canRetryTemplate: !!error && !!lastTemplatePath && creatingAction === null,
    handleCreateVault,
    retryCreateVault,
    handleCreateEmptyVault,
    handleOpenFolder,
    handleDismiss,
  }
}
