'use client'

import { useCallback, useEffect, useState } from 'react'
import { toast } from 'sonner'

import { cmsApi } from '../../lib/api.js'

export type UpdateChangeType =
  | 'feature'
  | 'fix'
  | 'improvement'
  | 'breaking'
  | 'security'
  | 'deprecation'

export interface UpdateChangelogItem {
  version: string
  date: string
  summary: string
  type?: UpdateChangeType
}

export interface UpdatePluginCompatibility {
  name: string
  compatible: boolean
  minVersion?: string
}

export interface UpdateInfo {
  current: string
  latest: string
  updateAvailable: boolean
  severity?: 'patch' | 'minor' | 'major' | 'security'
  releaseDate?: string
  changelog?: UpdateChangelogItem[]
  updateCommand?: string
  breakingChanges?: string[]
  hasMigrations?: boolean
  pluginCompatibility?: UpdatePluginCompatibility[]
  source?: 'update-server' | 'npm'
  /** Whether the current session may manage updates (admin/owner). */
  canManage?: boolean
  hasGithubToken?: boolean
  githubRepo?: string
}

interface GithubForm {
  githubRepo: string
  githubToken: string
}

const REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/

/** Admin/Owner may manage updates (matches the server's `requireAdminScope`). */
export function canManageUpdates(role: string | null | undefined): boolean {
  const r = (role ?? '').toUpperCase()
  return r === 'ADMIN' || r === 'OWNER'
}

/**
 * State + actions for the Settings → Updates tab. Owns the update-status check,
 * the GitHub-integration config form (with dirty tracking for the shared save
 * bar), and the "Create Update PR" action. All writes are gated on `canManage`,
 * which the server confirms in the check response.
 */
export function useUpdates({ role }: { role?: string }) {
  const [info, setInfo] = useState<UpdateInfo | null>(null)
  const [checking, setChecking] = useState(false)
  const [hasChecked, setHasChecked] = useState(false)
  const [error, setError] = useState('')
  const [applying, setApplying] = useState(false)
  const [prResult, setPrResult] = useState<{ prUrl: string; prNumber: number } | null>(null)

  const [form, setForm] = useState<GithubForm>({ githubRepo: '', githubToken: '' })
  const [baseline, setBaseline] = useState<GithubForm>({ githubRepo: '', githubToken: '' })
  const [saveState, setSaveState] = useState<'idle' | 'saving' | 'success' | 'error'>('idle')

  // Optimistic until the check response confirms the server-side permission.
  const canManage = info?.canManage ?? canManageUpdates(role)

  const check = useCallback(async () => {
    setChecking(true)
    setError('')
    setPrResult(null)
    const res = await cmsApi<UpdateInfo>('/updates/check')
    if (res.error) {
      setError(res.error)
    } else if (res.data) {
      setInfo(res.data)
      const repo = res.data.githubRepo ?? ''
      setForm({ githubRepo: repo, githubToken: '' })
      setBaseline({ githubRepo: repo, githubToken: '' })
    }
    setChecking(false)
    setHasChecked(true)
  }, [])

  useEffect(() => {
    check()
  }, [check])

  const repoError =
    form.githubRepo.trim() && !REPO_PATTERN.test(form.githubRepo.trim())
      ? 'Use the owner/repo format (e.g. actuate-media/my-site).'
      : ''
  const hasErrors = Boolean(repoError)
  const dirty = form.githubRepo !== baseline.githubRepo || form.githubToken.trim() !== ''

  const setField = useCallback((key: keyof GithubForm, value: string) => {
    setForm((f) => ({ ...f, [key]: value }))
    setSaveState('idle')
  }, [])

  const save = useCallback(async () => {
    if (hasErrors || !dirty) return
    setSaveState('saving')
    const body: Record<string, string> = {}
    if (form.githubToken.trim()) body.githubToken = form.githubToken.trim()
    if (form.githubRepo.trim()) body.githubRepo = form.githubRepo.trim()

    const res = await cmsApi('/updates/config', { method: 'PUT', body: JSON.stringify(body) })
    if (res.error) {
      setSaveState('error')
      toast.error(res.error)
      return
    }
    setSaveState('success')
    toast.success('GitHub configuration saved and encrypted.')
    const savedRepo = form.githubRepo.trim()
    setBaseline({ githubRepo: savedRepo, githubToken: '' })
    setForm({ githubRepo: savedRepo, githubToken: '' })
    setInfo((prev) =>
      prev
        ? {
            ...prev,
            githubRepo: savedRepo,
            hasGithubToken: prev.hasGithubToken || Boolean(body.githubToken),
          }
        : prev,
    )
  }, [dirty, form.githubRepo, form.githubToken, hasErrors])

  const reset = useCallback(() => {
    setForm({ ...baseline })
    setSaveState('idle')
  }, [baseline])

  const apply = useCallback(async () => {
    if (!info?.latest || !canManage) return
    setApplying(true)
    const res = await cmsApi<{ prUrl: string; prNumber: number }>('/updates/apply', {
      method: 'POST',
      body: JSON.stringify({ targetVersion: info.latest }),
    })
    if (res.error) {
      toast.error(res.error)
    } else if (res.data) {
      setPrResult(res.data)
      toast.success('Update PR created.')
    }
    setApplying(false)
  }, [info, canManage])

  return {
    info,
    checking,
    hasChecked,
    error,
    applying,
    prResult,
    canManage,
    form,
    setField,
    dirty,
    repoError,
    hasErrors,
    saveState,
    save,
    reset,
    check,
    apply,
  }
}
