'use client'

import { useCallback, useEffect, useMemo, useState } from 'react'
import { cmsApi } from '../../lib/api.js'

/**
 * Settings → AI state.
 *
 * Two persistence surfaces, mirroring the SEO tab's multi-backend pattern:
 *   - Provider connections (create / update / test / disconnect / sync models)
 *     act IMMEDIATELY against `/ai/providers/*`. These touch encrypted secrets,
 *     so they shouldn't sit in a dirty buffer waiting for a Save click.
 *   - Defaults, feature toggles, and the monthly budget are buffered and saved
 *     together via `PUT /ai/settings` through the shared SettingsSaveBar.
 *
 * Provider API keys are never returned by the server; we only ever receive a
 * masked `apiKeyLast4` + a source flag.
 */

export type AiProviderKey = 'openai' | 'anthropic' | 'xai' | 'custom'
export type AiConnectionStatus =
  | 'connected'
  | 'invalid_key'
  | 'rate_limited'
  | 'provider_down'
  | 'needs_setup'
  | 'unknown'
export type AiModelStatus = 'available' | 'unavailable' | 'deprecated' | 'unknown'

export interface AiModelView {
  id: string
  provider: AiProviderKey
  providerModelId: string
  displayName: string
  description?: string
  status: AiModelStatus
  capabilities: Record<string, boolean>
  contextWindow?: number
  maxOutputTokens?: number
  inputModalities: string[]
  outputModalities: string[]
  pricing?: {
    inputPerMillion?: number
    outputPerMillion?: number
    image?: number
    search?: number
  }
  lastSyncedAt: string
  source: string
}

export interface AiProviderView {
  id: string
  provider: AiProviderKey
  displayName: string
  enabled: boolean
  status: AiConnectionStatus
  apiKeyLast4?: string
  baseUrl?: string
  organizationId?: string
  projectId?: string
  lastTestedAt?: string
  lastModelSyncAt?: string
  apiKeySource: 'environment' | 'database' | 'config'
  keyManagedExternally: boolean
  hasKey: boolean
  createdAt: string
  updatedAt: string
  updatedBy: string
}

export interface AiFeatureView {
  key: string
  label: string
  description: string
  enabled: boolean
  category: string
  requiredCapabilities: string[]
  /** Per-feature model override (`provider:modelId`). Empty = use the default. */
  modelId?: string
  /** Per-feature provider connection override. Usually derived from the model. */
  providerId?: string
}

export interface AiUsageView {
  totalTokens: number
  promptTokens: number
  completionTokens: number
  estimatedCost?: number
  monthlyTokenLimit?: number
  periodStart: string
  periodEnd: string
  runCount: number
}

/** Editable brand voice fields (the buffered Save-bar portion). */
export interface BrandVoiceForm {
  enabled: boolean
  name: string
  tone: string
  audience: string
  guidelines: string
  sampleText: string
}

const EMPTY_BRAND_VOICE: BrandVoiceForm = {
  enabled: false,
  name: '',
  tone: '',
  audience: '',
  guidelines: '',
  sampleText: '',
}

/** The buffered (Save-bar) portion of AI settings. */
export interface AiSettingsForm {
  defaultProviderId: string
  defaultModelId: string
  features: AiFeatureView[]
  monthlyTokenLimit: string
  brandVoice: BrandVoiceForm
}

interface AiSettingsResponse {
  settings: {
    defaultProviderId?: string
    defaultModelId?: string
    features: AiFeatureView[]
    budget: { monthlyTokenLimit?: number }
    brandVoice?: {
      enabled?: boolean
      name?: string
      tone?: string
      audience?: string
      guidelines?: string
      sampleText?: string
    }
  }
  providers: AiProviderView[]
  defaultModel: AiModelView | null
}

type SaveState = 'idle' | 'saving' | 'success' | 'error'

function formFromResponse(res: AiSettingsResponse): AiSettingsForm {
  return {
    defaultProviderId: res.settings.defaultProviderId ?? '',
    defaultModelId: res.settings.defaultModelId ?? '',
    features: res.settings.features.map((f) => ({
      ...f,
      modelId: f.modelId ?? '',
      providerId: f.providerId ?? '',
    })),
    monthlyTokenLimit:
      typeof res.settings.budget.monthlyTokenLimit === 'number'
        ? String(res.settings.budget.monthlyTokenLimit)
        : '',
    brandVoice: {
      enabled: res.settings.brandVoice?.enabled ?? false,
      name: res.settings.brandVoice?.name ?? '',
      tone: res.settings.brandVoice?.tone ?? '',
      audience: res.settings.brandVoice?.audience ?? '',
      guidelines: res.settings.brandVoice?.guidelines ?? '',
      sampleText: res.settings.brandVoice?.sampleText ?? '',
    },
  }
}

const EMPTY_FORM: AiSettingsForm = {
  defaultProviderId: '',
  defaultModelId: '',
  features: [],
  monthlyTokenLimit: '',
  brandVoice: { ...EMPTY_BRAND_VOICE },
}

/** A brand voice with no content and disabled clears the stored profile (send null). */
function brandVoiceIsEmpty(bv: BrandVoiceForm): boolean {
  return (
    !bv.enabled &&
    !bv.name.trim() &&
    !bv.tone.trim() &&
    !bv.audience.trim() &&
    !bv.guidelines.trim() &&
    !bv.sampleText.trim()
  )
}

export interface ProviderInput {
  provider: AiProviderKey
  displayName?: string
  apiKey?: string
  baseUrl?: string
  organizationId?: string
  projectId?: string
  enabled?: boolean
  test?: boolean
}

export interface UseAiSettings {
  loading: boolean
  loadError: string | null
  providers: AiProviderView[]
  models: AiModelView[]
  usage: AiUsageView | null
  defaultModel: AiModelView | null
  form: AiSettingsForm
  setDefaultProvider: (id: string) => void
  setDefaultModel: (id: string) => void
  setMonthlyTokenLimit: (value: string) => void
  toggleFeature: (key: string, enabled: boolean) => void
  /** Set a per-feature model override. Empty string reverts to the default model. */
  setFeatureModel: (key: string, modelId: string) => void
  /** Patch one or more brand voice fields. */
  setBrandVoice: (patch: Partial<BrandVoiceForm>) => void
  dirty: boolean
  budgetError: string | null
  saveState: SaveState
  saveError: string | null
  save: () => Promise<void>
  reset: () => void
  refetch: () => void
  /** Provider connection actions (persist immediately). */
  createProvider: (input: ProviderInput) => Promise<{ ok: boolean; error?: string }>
  updateProvider: (
    id: string,
    patch: Omit<Partial<ProviderInput>, 'apiKey'> & { apiKey?: string | null },
  ) => Promise<{ ok: boolean; error?: string }>
  deleteProvider: (id: string) => Promise<{ ok: boolean; error?: string }>
  testProvider: (
    id: string,
  ) => Promise<{ ok: boolean; status?: AiConnectionStatus; message?: string; error?: string }>
  syncModels: (
    id: string,
  ) => Promise<{ ok: boolean; fromFallback?: boolean; warning?: string | null; error?: string }>
  busyProviderId: string | null
}

export function useAiSettings(canEdit: boolean): UseAiSettings {
  const [loading, setLoading] = useState(true)
  const [loadError, setLoadError] = useState<string | null>(null)
  const [providers, setProviders] = useState<AiProviderView[]>([])
  const [models, setModels] = useState<AiModelView[]>([])
  const [usage, setUsage] = useState<AiUsageView | null>(null)
  const [defaultModel, setDefaultModel] = useState<AiModelView | null>(null)
  const [baseline, setBaseline] = useState<AiSettingsForm>(EMPTY_FORM)
  const [form, setForm] = useState<AiSettingsForm>(EMPTY_FORM)
  const [saveState, setSaveState] = useState<SaveState>('idle')
  const [saveError, setSaveError] = useState<string | null>(null)
  const [busyProviderId, setBusyProviderId] = useState<string | null>(null)
  const [reloadKey, setReloadKey] = useState(0)

  const loadModels = useCallback(async () => {
    const res = await cmsApi<{ models: AiModelView[] }>('/ai/models')
    if (!res.error && res.data) setModels(res.data.models ?? [])
  }, [])

  useEffect(() => {
    let cancelled = false
    setLoading(true)
    setLoadError(null)
    Promise.all([
      cmsApi<AiSettingsResponse>('/ai/settings'),
      cmsApi<{ models: AiModelView[] }>('/ai/models'),
      cmsApi<AiUsageView>('/ai/usage/summary'),
    ])
      .then(([settingsRes, modelsRes, usageRes]) => {
        if (cancelled) return
        if (settingsRes.error) {
          setLoadError(settingsRes.error)
          return
        }
        if (settingsRes.data) {
          const loaded = formFromResponse(settingsRes.data)
          setBaseline(loaded)
          setForm(loaded)
          setProviders(settingsRes.data.providers ?? [])
          setDefaultModel(settingsRes.data.defaultModel ?? null)
        }
        if (!modelsRes.error && modelsRes.data) setModels(modelsRes.data.models ?? [])
        if (!usageRes.error && usageRes.data) setUsage(usageRes.data)
      })
      .catch(() => {
        if (!cancelled) setLoadError('Failed to load AI settings.')
      })
      .finally(() => {
        if (!cancelled) setLoading(false)
      })
    return () => {
      cancelled = true
    }
  }, [reloadKey])

  const setDefaultProvider = useCallback((id: string) => {
    setForm((prev) => ({ ...prev, defaultProviderId: id, defaultModelId: '' }))
    setSaveState('idle')
  }, [])

  const setDefaultModelId = useCallback((id: string) => {
    setForm((prev) => ({ ...prev, defaultModelId: id }))
    setSaveState('idle')
  }, [])

  const setMonthlyTokenLimit = useCallback((value: string) => {
    setForm((prev) => ({ ...prev, monthlyTokenLimit: value.replace(/[^\d]/g, '') }))
    setSaveState('idle')
  }, [])

  const toggleFeature = useCallback((key: string, enabled: boolean) => {
    setForm((prev) => ({
      ...prev,
      features: prev.features.map((f) => (f.key === key ? { ...f, enabled } : f)),
    }))
    setSaveState('idle')
  }, [])

  const setFeatureModel = useCallback((key: string, modelId: string) => {
    setForm((prev) => ({
      ...prev,
      features: prev.features.map((f) =>
        f.key === key ? { ...f, modelId, providerId: modelId ? f.providerId : '' } : f,
      ),
    }))
    setSaveState('idle')
  }, [])

  const setBrandVoice = useCallback((patch: Partial<BrandVoiceForm>) => {
    setForm((prev) => ({ ...prev, brandVoice: { ...prev.brandVoice, ...patch } }))
    setSaveState('idle')
  }, [])

  const budgetError = useMemo(() => {
    if (form.monthlyTokenLimit === '') return null
    const n = Number(form.monthlyTokenLimit)
    if (!Number.isFinite(n) || n < 0) return 'Monthly token limit must be a non-negative number.'
    return null
  }, [form.monthlyTokenLimit])

  const dirty = useMemo(() => {
    if (form.defaultProviderId !== baseline.defaultProviderId) return true
    if (form.defaultModelId !== baseline.defaultModelId) return true
    if (form.monthlyTokenLimit !== baseline.monthlyTokenLimit) return true
    if (form.features.length !== baseline.features.length) return true
    const bvKeys: (keyof BrandVoiceForm)[] = [
      'enabled',
      'name',
      'tone',
      'audience',
      'guidelines',
      'sampleText',
    ]
    if (bvKeys.some((k) => form.brandVoice[k] !== baseline.brandVoice[k])) return true
    return form.features.some((f, i) => {
      const base = baseline.features[i]
      return f.enabled !== base?.enabled || (f.modelId ?? '') !== (base?.modelId ?? '')
    })
  }, [form, baseline])

  const save = useCallback(async () => {
    if (!canEdit || budgetError) return
    setSaveState('saving')
    setSaveError(null)
    try {
      const res = await cmsApi<AiSettingsResponse>('/ai/settings', {
        method: 'PUT',
        body: JSON.stringify({
          defaultProviderId: form.defaultProviderId || null,
          defaultModelId: form.defaultModelId || null,
          features: form.features.map((f) => ({
            key: f.key,
            enabled: f.enabled,
            modelId: f.modelId ? f.modelId : null,
          })),
          budget: {
            monthlyTokenLimit:
              form.monthlyTokenLimit === '' ? null : Number(form.monthlyTokenLimit),
          },
          brandVoice: brandVoiceIsEmpty(form.brandVoice)
            ? null
            : {
                enabled: form.brandVoice.enabled,
                name: form.brandVoice.name,
                tone: form.brandVoice.tone,
                audience: form.brandVoice.audience,
                guidelines: form.brandVoice.guidelines,
                sampleText: form.brandVoice.sampleText,
              },
        }),
      })
      if (res.error) throw new Error(res.error)
      setBaseline(form)
      setSaveState('success')
      // Refresh usage so the budget meter reflects the new limit immediately.
      const usageRes = await cmsApi<AiUsageView>('/ai/usage/summary')
      if (!usageRes.error && usageRes.data) setUsage(usageRes.data)
    } catch (err) {
      setSaveState('error')
      setSaveError(err instanceof Error ? err.message : 'Failed to save AI settings.')
    }
  }, [canEdit, budgetError, form])

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

  const refetch = useCallback(() => setReloadKey((k) => k + 1), [])

  const reloadProviders = useCallback(async () => {
    const res = await cmsApi<AiSettingsResponse>('/ai/settings')
    if (!res.error && res.data) {
      setProviders(res.data.providers ?? [])
      setDefaultModel(res.data.defaultModel ?? null)
    }
  }, [])

  const createProvider = useCallback(
    async (input: ProviderInput) => {
      if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
      setBusyProviderId('new')
      try {
        const res = await cmsApi<AiProviderView>('/ai/providers', {
          method: 'POST',
          body: JSON.stringify(input),
        })
        if (res.error) return { ok: false, error: res.error }
        await reloadProviders()
        return { ok: true }
      } finally {
        setBusyProviderId(null)
      }
    },
    [canEdit, reloadProviders],
  )

  const updateProvider = useCallback(
    async (
      id: string,
      patch: Omit<Partial<ProviderInput>, 'apiKey'> & { apiKey?: string | null },
    ) => {
      if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
      setBusyProviderId(id)
      try {
        const res = await cmsApi<AiProviderView>(`/ai/providers/${id}`, {
          method: 'PUT',
          body: JSON.stringify(patch),
        })
        if (res.error) return { ok: false, error: res.error }
        await reloadProviders()
        return { ok: true }
      } finally {
        setBusyProviderId(null)
      }
    },
    [canEdit, reloadProviders],
  )

  const deleteProvider = useCallback(
    async (id: string) => {
      if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
      setBusyProviderId(id)
      try {
        const res = await cmsApi(`/ai/providers/${id}`, { method: 'DELETE' })
        if (res.error) return { ok: false, error: res.error }
        await reloadProviders()
        await loadModels()
        return { ok: true }
      } finally {
        setBusyProviderId(null)
      }
    },
    [canEdit, reloadProviders, loadModels],
  )

  const testProvider = useCallback(
    async (id: string) => {
      if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
      setBusyProviderId(id)
      try {
        const res = await cmsApi<{
          provider: AiProviderView
          result: { ok: boolean; status: AiConnectionStatus; message: string }
        }>(`/ai/providers/${id}/test`, { method: 'POST' })
        if (res.error) return { ok: false, error: res.error }
        await reloadProviders()
        return {
          ok: res.data?.result.ok ?? false,
          status: res.data?.result.status,
          message: res.data?.result.message,
        }
      } finally {
        setBusyProviderId(null)
      }
    },
    [canEdit, reloadProviders],
  )

  const syncModels = useCallback(
    async (id: string) => {
      if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
      setBusyProviderId(id)
      try {
        const res = await cmsApi<{
          models: AiModelView[]
          fromFallback: boolean
          warning: string | null
        }>(`/ai/providers/${id}/sync-models`, { method: 'POST' })
        if (res.error) return { ok: false, error: res.error }
        await loadModels()
        await reloadProviders()
        return {
          ok: true,
          fromFallback: res.data?.fromFallback,
          warning: res.data?.warning ?? null,
        }
      } finally {
        setBusyProviderId(null)
      }
    },
    [canEdit, loadModels, reloadProviders],
  )

  return {
    loading,
    loadError,
    providers,
    models,
    usage,
    defaultModel,
    form,
    setDefaultProvider,
    setDefaultModel: setDefaultModelId,
    setMonthlyTokenLimit,
    toggleFeature,
    setFeatureModel,
    setBrandVoice,
    dirty,
    budgetError,
    saveState,
    saveError,
    save,
    reset,
    refetch,
    createProvider,
    updateProvider,
    deleteProvider,
    testProvider,
    syncModels,
    busyProviderId,
  }
}
