import { useEffect, useState } from 'react'
import { Plus, Sparkles } from 'lucide-react'
import { useToast } from '../context/ToastContext'
import { CategoryModal } from '../components/CategoryModal'
import { CategoryAiPromptModal } from '../components/CategoryAiPromptModal'
import { CategoryTree } from '../components/CategoryTree'
import { Breadcrumbs } from '../components/Breadcrumbs'
import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal'
import { VariationsModal } from '../components/VariationsModal'
import { TechnicalFormModal } from '../components/TechnicalFormModal'
import { TechnicalFormAiPromptModal } from '../components/TechnicalFormAiPromptModal'
import type { Category, CategoryFormData } from '../types/category'
import type { Variation } from '../types/variation'
import type { TechnicalFormFieldDraft } from '../types/technicalForm'
import {
  listCategoryVariations,
  saveCategoryVariations,
} from '../services/categoryVariationsService'
import {
  getCategoryTechnicalForm,
  mapApiFieldToDraft,
  saveCategoryTechnicalForm,
  suggestCategoryTechnicalFormByAi,
} from '../services/categoryTechnicalFormService'
import { createId } from '../utils/id'
import { ApiError } from '../lib/api'
import {
  createProductCategory,
  deleteProductCategory,
  generateProductCategoriesByAi,
  listProductCategories,
  mapProductCategoryToUi,
  toCategoryFormPayload,
  updateProductCategory,
} from '../services/productCategoryService'
import { useT } from '../i18n/useT'
import pageStyles from '../components/PageContent.module.css'
import aiStyles from '../styles/ai.module.css'
import styles from './CategoriesPage.module.css'

export function CategoriesPage() {
  const t = useT()
  const { showToast } = useToast()
  const [categories, setCategories] = useState<Category[]>([])
  const [expandedIds, setExpandedIds] = useState<Set<string>>(() => new Set())
  const [modalOpen, setModalOpen] = useState(false)
  const [defaultParentId, setDefaultParentId] = useState('')
  const [modalTitle, setModalTitle] = useState(() => t('categories.add'))
  const [editingCategory, setEditingCategory] = useState<Category | null>(null)
  const [isLoading, setIsLoading] = useState(true)
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [error, setError] = useState('')
  const [deleteTarget, setDeleteTarget] = useState<Category | null>(null)
  const [variationsTarget, setVariationsTarget] = useState<{
    categoryId: string
    categoryName: string
  } | null>(null)
  const [categoryVariations, setCategoryVariations] = useState<Record<string, Variation[]>>({})
  const [variationsLoading, setVariationsLoading] = useState(false)
  const [variationsSaving, setVariationsSaving] = useState(false)
  const [technicalTarget, setTechnicalTarget] = useState<{
    categoryId: string
    categoryName: string
  } | null>(null)
  const [categoryTechnicalFields, setCategoryTechnicalFields] = useState<
    Record<string, TechnicalFormFieldDraft[]>
  >({})
  const [technicalLoading, setTechnicalLoading] = useState(false)
  const [technicalSaving, setTechnicalSaving] = useState(false)
  const [technicalGenerating, setTechnicalGenerating] = useState(false)
  const [technicalError, setTechnicalError] = useState('')
  const [aiModalOpen, setAiModalOpen] = useState(false)
  const [aiGenerating, setAiGenerating] = useState(false)
  const [techAiPromptOpen, setTechAiPromptOpen] = useState(false)

  useEffect(() => {
    const controller = new AbortController()
    void loadCategories(controller.signal)
    return () => controller.abort()
  }, [])

  async function loadCategories(signal?: AbortSignal) {
    setIsLoading(true)
    setError('')
    try {
      const items = await listProductCategories(signal)
      setCategories(items.map(mapProductCategoryToUi))
    } catch (err) {
      if (err instanceof DOMException && err.name === 'AbortError') return
      if (err instanceof ApiError) {
        setError(err.message)
      } else {
        setError('Unable to load categories.')
      }
    } finally {
      setIsLoading(false)
    }
  }

  function openCreateModal(parentId = '', title?: string) {
    setEditingCategory(null)
    setDefaultParentId(parentId)
    setModalTitle(title ?? t('categories.add'))
    setModalOpen(true)
    if (parentId) {
      setExpandedIds((prev) => new Set(prev).add(parentId))
    }
  }

  function openEditModal(category: Category) {
    setEditingCategory(category)
    setDefaultParentId(category.parentId ?? '')
    setModalTitle(t('categories.edit'))
    setModalOpen(true)
  }

  function toggleExpanded(id: string) {
    setExpandedIds((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }

  async function handleSubmit(data: CategoryFormData) {
    setIsSubmitting(true)
    setError('')

    try {
      const payload = toCategoryFormPayload(data)

      if (editingCategory) {
        const result = await updateProductCategory(editingCategory.id, {
          ...payload,
          parentId: data.parentId || null,
        })
        setCategories((prev) =>
          prev.map((category) =>
            category.id === editingCategory.id
              ? mapProductCategoryToUi(result.category)
              : category,
          ),
        )
      } else {
        const result = await createProductCategory(payload)
        setCategories((prev) => [...prev, mapProductCategoryToUi(result.category)])
        if (data.parentId) {
          setExpandedIds((prev) => new Set(prev).add(data.parentId))
        }
      }

      setModalOpen(false)
      setEditingCategory(null)
    } catch (err) {
      if (err instanceof ApiError) {
        setError(err.message)
      } else {
        setError('Unable to save category.')
      }
    } finally {
      setIsSubmitting(false)
    }
  }

  async function confirmDelete() {
    if (!deleteTarget) return

    setIsSubmitting(true)
    setError('')

    try {
      const result = await deleteProductCategory(deleteTarget.id)
      const removed = new Set(result.deletedIds)
      setCategories((prev) => prev.filter((category) => !removed.has(category.id)))
      setExpandedIds((prev) => {
        const next = new Set(prev)
        removed.forEach((id) => next.delete(id))
        return next
      })
      setCategoryVariations((prev) => {
        const next = { ...prev }
        removed.forEach((id) => delete next[id])
        return next
      })
      setCategoryTechnicalFields((prev) => {
        const next = { ...prev }
        removed.forEach((id) => delete next[id])
        return next
      })
      setDeleteTarget(null)
    } catch (err) {
      if (err instanceof ApiError) {
        setError(err.message)
      } else {
        setError('Unable to delete category.')
      }
    } finally {
      setIsSubmitting(false)
    }
  }

  function getVariationCount(categoryId: string): number {
    const cached = categoryVariations[categoryId]
    if (cached) return cached.length
    return categories.find((category) => category.id === categoryId)?.variationCount ?? 0
  }

  function variationCountsMap(): Record<string, number> {
    return Object.fromEntries(
      categories.map((category) => [category.id, getVariationCount(category.id)]),
    )
  }

  function setVariationCount(categoryId: string, count: number) {
    setCategories((prev) =>
      prev.map((category) =>
        category.id === categoryId ? { ...category, variationCount: count } : category,
      ),
    )
  }

  async function openVariations(categoryId: string) {
    const category = categories.find((c) => c.id === categoryId)
    if (!category) return

    setVariationsTarget({
      categoryId,
      categoryName: category.nameFa
        ? `${category.nameFa} · ${category.nameEn}`
        : category.nameEn,
    })
    setVariationsLoading(true)
    setError('')

    try {
      const items = await listCategoryVariations(categoryId)
      setCategoryVariations((prev) => ({ ...prev, [categoryId]: items }))
      setVariationCount(categoryId, items.length)
    } catch (err) {
      if (err instanceof ApiError) {
        setError(err.message)
      } else {
        setError('Unable to load category variations.')
      }
    } finally {
      setVariationsLoading(false)
    }
  }

  function getVariationsForCategory(categoryId: string): Variation[] {
    return categoryVariations[categoryId] ?? []
  }

  function technicalFieldCountsMap(): Record<string, number> {
    return Object.fromEntries(
      categories.map((category) => [
        category.id,
        categoryTechnicalFields[category.id]?.length ?? 0,
      ]),
    )
  }

  async function openTechnicalForm(categoryId: string) {
    const category = categories.find((c) => c.id === categoryId)
    if (!category) return

    setTechnicalTarget({
      categoryId,
      categoryName: category.nameFa
        ? `${category.nameFa} · ${category.nameEn}`
        : category.nameEn,
    })
    setTechnicalLoading(true)
    setTechnicalError('')

    try {
      const form = await getCategoryTechnicalForm(categoryId)
      setCategoryTechnicalFields((prev) => ({
        ...prev,
        [categoryId]: form?.fields.map(mapApiFieldToDraft) ?? [],
      }))
    } catch (err) {
      if (err instanceof ApiError) {
        setTechnicalError(err.message)
      } else {
        setTechnicalError('Unable to load technical form.')
      }
    } finally {
      setTechnicalLoading(false)
    }
  }

  function getTechnicalFieldsForCategory(categoryId: string): TechnicalFormFieldDraft[] {
    return categoryTechnicalFields[categoryId] ?? []
  }

  function handleGenerateTechnicalFormClick() {
    setTechAiPromptOpen(true)
  }

  async function handleGenerateTechnicalForm(hint?: string) {
    if (!technicalTarget) return

    setTechAiPromptOpen(false)
    setTechnicalGenerating(true)
    setTechnicalError('')

    try {
      const suggested = await suggestCategoryTechnicalFormByAi(technicalTarget.categoryId, hint)
      const drafts: TechnicalFormFieldDraft[] = suggested.map((field) => ({
        id: createId(),
        label: field.label,
        type: field.type,
        isRequired: field.isRequired,
        options:
          field.type === 'select' || field.type === 'multi_select'
            ? field.options?.length
              ? field.options
              : ['']
            : [],
      }))

      setCategoryTechnicalFields((prev) => ({
        ...prev,
        [technicalTarget.categoryId]: drafts,
      }))
      showToast('Technical form draft generated. Review and save when ready.', 'success')
    } catch (err) {
      if (err instanceof ApiError) {
        setTechnicalError(err.message)
      } else {
        setTechnicalError('Unable to generate technical form with AI.')
      }
    } finally {
      setTechnicalGenerating(false)
    }
  }

  async function handleSaveTechnicalForm() {
    if (!technicalTarget) return

    setTechnicalSaving(true)
    setTechnicalError('')

    try {
      const fields = getTechnicalFieldsForCategory(technicalTarget.categoryId)
      const saved = await saveCategoryTechnicalForm(technicalTarget.categoryId, fields)
      setCategoryTechnicalFields((prev) => ({
        ...prev,
        [technicalTarget.categoryId]: saved?.fields.map(mapApiFieldToDraft) ?? [],
      }))
      setTechnicalTarget(null)
    } catch (err) {
      if (err instanceof ApiError) {
        setTechnicalError(err.message)
      } else {
        setTechnicalError('Unable to save technical form.')
      }
    } finally {
      setTechnicalSaving(false)
    }
  }

  async function handleGenerateCategoriesByAi(prompt: string) {
    setAiGenerating(true)
    setError('')

    try {
      const result = await generateProductCategoriesByAi(prompt)
      const items = await listProductCategories()
      const mapped = items.map(mapProductCategoryToUi)
      setCategories(mapped)
      setExpandedIds(
        new Set(mapped.filter((category) => !category.parentId).map((category) => category.id)),
      )
      showToast(result.message, 'success')
      setAiModalOpen(false)
    } catch (err) {
      if (err instanceof ApiError) {
        setError(err.message)
      } else {
        setError('Unable to generate categories with AI.')
      }
      throw err
    } finally {
      setAiGenerating(false)
    }
  }

  async function handleSaveVariations() {
    if (!variationsTarget) return

    setVariationsSaving(true)
    setError('')

    try {
      const saved = await saveCategoryVariations(
        variationsTarget.categoryId,
        getVariationsForCategory(variationsTarget.categoryId),
      )
      setCategoryVariations((prev) => ({
        ...prev,
        [variationsTarget.categoryId]: saved,
      }))
      setVariationCount(variationsTarget.categoryId, saved.length)
      setVariationsTarget(null)
    } catch (err) {
      if (err instanceof ApiError) {
        setError(err.message)
      } else {
        setError('Unable to save category variations.')
      }
    } finally {
      setVariationsSaving(false)
    }
  }

  return (
    <main className={pageStyles.content}>
      <Breadcrumbs
        items={[
          { label: 'Dashboard', href: '/' },
          { label: 'Products', href: '/products' },
          { label: 'Categories' },
        ]}
      />
      <div className={pageStyles.pageHeader}>
        <div>
          <h2 className={pageStyles.pageTitle}>{t('title.categories')}</h2>
          <p className={pageStyles.pageSubtitle}>{t('categories.page.subtitle')}</p>
        </div>
      </div>

      {error && (
        <div className={styles.error} role="alert">
          {error}
        </div>
      )}

      <div className={styles.list}>
        {isLoading ? (
          <p className={styles.empty}>{t('categories.loading')}</p>
        ) : categories.length === 0 ? (
          <p className={styles.empty}>{t('categories.empty')}</p>
        ) : (
          <CategoryTree
            categories={categories}
            expandedIds={expandedIds}
            variationCounts={variationCountsMap()}
            technicalFieldCounts={technicalFieldCountsMap()}
            onToggle={toggleExpanded}
            onAddSub={(id) => openCreateModal(id, t('categories.addSub'))}
            onVariations={openVariations}
            onTechnicalForm={openTechnicalForm}
            onOptions={(id) => {
              const category = categories.find((item) => item.id === id)
              if (category) openEditModal(category)
            }}
            onRemove={(id) => {
              const category = categories.find((item) => item.id === id)
              if (category) setDeleteTarget(category)
            }}
          />
        )}
      </div>

      <CategoryModal
        open={modalOpen}
        onClose={() => {
          if (!isSubmitting) {
            setModalOpen(false)
            setEditingCategory(null)
          }
        }}
        onSubmit={handleSubmit}
        categories={categories}
        defaultParentId={defaultParentId}
        editingCategory={editingCategory}
        title={modalTitle}
        isSubmitting={isSubmitting}
      />

      <ConfirmDeleteModal
        open={!!deleteTarget}
        title={t('categories.deleteTitle')}
        message={
          deleteTarget
            ? t('categories.deleteMessage', {
                name: deleteTarget.nameFa || deleteTarget.nameEn,
              })
            : ''
        }
        onConfirm={confirmDelete}
        onCancel={() => !isSubmitting && setDeleteTarget(null)}
      />

      {variationsTarget && (
        <VariationsModal
          open={!!variationsTarget}
          categoryName={variationsTarget.categoryName}
          variations={
            variationsLoading
              ? []
              : getVariationsForCategory(variationsTarget.categoryId)
          }
          isSaving={variationsSaving || variationsLoading}
          onClose={() => !variationsSaving && setVariationsTarget(null)}
          onChange={(variations) => {
            setCategoryVariations((prev) => ({
              ...prev,
              [variationsTarget.categoryId]: variations,
            }))
            setVariationCount(variationsTarget.categoryId, variations.length)
          }}
          onSave={handleSaveVariations}
        />
      )}
      {technicalTarget && (
        <TechnicalFormModal
          open={!!technicalTarget}
          categoryName={technicalTarget.categoryName}
          fields={
            technicalLoading ? [] : getTechnicalFieldsForCategory(technicalTarget.categoryId)
          }
          isLoading={technicalLoading}
          isSaving={technicalSaving}
          isGenerating={technicalGenerating}
          error={technicalError}
          onClose={() => !technicalSaving && !technicalGenerating && setTechnicalTarget(null)}
          onChange={(fields) => {
            setCategoryTechnicalFields((prev) => ({
              ...prev,
              [technicalTarget.categoryId]: fields,
            }))
          }}
          onSave={() => void handleSaveTechnicalForm()}
          onGenerate={() => void handleGenerateTechnicalForm()}
          onGenerateClick={handleGenerateTechnicalFormClick}
        />
      )}

      {technicalTarget && (
        <TechnicalFormAiPromptModal
          open={techAiPromptOpen}
          categoryName={technicalTarget.categoryName}
          onClose={() => setTechAiPromptOpen(false)}
          onRun={(prompt) => handleGenerateTechnicalForm(prompt)}
          isRunning={technicalGenerating}
        />
      )}

      <CategoryAiPromptModal
        open={aiModalOpen}
        onClose={() => !aiGenerating && setAiModalOpen(false)}
        onRun={handleGenerateCategoriesByAi}
        isRunning={aiGenerating}
      />

      <div className={styles.fabDock}>
        <button
          type="button"
          className={aiStyles.aiFabStrip}
          onClick={() => setAiModalOpen(true)}
        >
          <Sparkles size={18} />
          <span>{t('categories.addByAi')}</span>
        </button>
        <button
          type="button"
          className={styles.addFab}
          onClick={() => openCreateModal()}
          aria-label={t('categories.add')}
        >
          <Plus size={24} />
        </button>
      </div>
    </main>
  )
}
