import { useEffect, useMemo, useState, type FormEvent } from 'react'
import {
  deleteNavBuilderItem,
  fetchNavBuilderData,
  reorderNavBuilderItems,
  saveNavBuilderItem,
} from '../api/session'
import type { NavBuilderData, NavBuilderItem } from '../api/types'
import { CiFlashMessages, type CiFlashMessage } from '../components/CiPartials'

type NavFormState = {
  id: string
  menu_parent_id: string
  label: string
  url: string
  icon_class: string
  target: string
  sort_order: string
  is_enabled: boolean
  roles: string[]
}


const navBuilderCollator = new Intl.Collator(undefined, {
  sensitivity: 'base',
  numeric: true,
})

function compareLabels(a: string | null | undefined, b: string | null | undefined): number {
  return navBuilderCollator.compare(a?.trim() || '', b?.trim() || '')
}

function navParentId(item: NavBuilderItem): number | null {
  return item.menu_parent_id ?? item.parent_id ?? null
}

function compareBuilderItems(a: NavBuilderItem, b: NavBuilderItem): number {
  const parentResult = compareLabels(a.parent_label ?? '', b.parent_label ?? '')
  const labelResult = compareLabels(a.label, b.label)
  return parentResult || labelResult || a.id - b.id
}

function sortBuilderItems(items: NavBuilderItem[]): NavBuilderItem[] {
  return [...items].sort(compareBuilderItems)
}

function sortParentOptions(options: NavBuilderData['parentOptions']): NavBuilderData['parentOptions'] {
  return [...options].sort((a, b) => compareLabels(a.label, b.label) || a.id - b.id)
}

function normalizeBuilderData(data: NavBuilderData): NavBuilderData {
  return {
    ...data,
    items: sortBuilderItems(data.items ?? []),
    parentOptions: sortParentOptions(data.parentOptions ?? []),
  }
}

function alphabeticalOrdersByParent(items: NavBuilderItem[]): Record<string, number> {
  const groups = new Map<string, NavBuilderItem[]>()
  for (const item of items) {
    const key = String(navParentId(item) ?? 'root')
    groups.set(key, [...(groups.get(key) ?? []), item])
  }

  const orders: Record<string, number> = {}
  for (const group of groups.values()) {
    const sorted = [...group].sort((a, b) => compareLabels(a.label, b.label) || a.id - b.id)
    sorted.forEach((item, index) => {
      orders[String(item.id)] = index + 1
    })
  }
  return orders
}

const blankForm: NavFormState = {
  id: '',
  menu_parent_id: '',
  label: '',
  url: '',
  icon_class: '',
  target: '',
  sort_order: '0',
  is_enabled: true,
  roles: [],
}

export function NavBuilderPage() {
  const [payload, setPayload] = useState<NavBuilderData>({
    items: [],
    roles: [],
    parentOptions: [],
  })
  const [form, setForm] = useState<NavFormState>(blankForm)
  const [loading, setLoading] = useState(true)
  const [saving, setSaving] = useState(false)
  const [messages, setMessages] = useState<CiFlashMessage[]>([])

  async function load() {
    setLoading(true)
    try {
      const res = await fetchNavBuilderData()
      setPayload(normalizeBuilderData(res.data?.data ?? { items: [], roles: [], parentOptions: [] }))
      setMessages([])
    } catch (error) {
      setMessages([{ type: 'warning', message: error instanceof Error ? error.message : 'Failed to load menu items.' }])
    } finally {
      setLoading(false)
    }
  }

  useEffect(() => {
    void load()
  }, [])

  const sortedItems = useMemo(() => sortBuilderItems(payload.items), [payload.items])

  const filteredParentOptions = useMemo(
    () => sortParentOptions(payload.parentOptions.filter((option) => String(option.id) !== form.id)),
    [form.id, payload.parentOptions],
  )

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setSaving(true)
    setMessages([])
    try {
      await saveNavBuilderItem({
        id: form.id ? Number(form.id) : null,
        menu_parent_id: form.menu_parent_id ? Number(form.menu_parent_id) : null,
        label: form.label.trim(),
        url: form.url.trim() || null,
        icon_class: form.icon_class.trim() || null,
        target: form.target.trim() || null,
        sort_order: 0,
        is_enabled: form.is_enabled,
        roles: form.roles.map(Number).filter((id) => id > 0),
      })
      setForm(blankForm)
      setMessages([{ type: 'success', message: 'Menu saved.' }])
      await load()
    } catch (error) {
      setMessages([{ type: 'warning', message: error instanceof Error ? error.message : 'Unable to save menu item.' }])
    } finally {
      setSaving(false)
    }
  }

  async function onDelete(item: NavBuilderItem) {
    if (!window.confirm(`Delete "${item.label}"?`)) return
    setMessages([])
    try {
      await deleteNavBuilderItem(item.id)
      setMessages([{ type: 'success', message: 'Menu item deleted.' }])
      if (form.id === String(item.id)) setForm(blankForm)
      await load()
    } catch (error) {
      setMessages([{ type: 'warning', message: error instanceof Error ? error.message : 'Unable to delete menu item.' }])
    }
  }

  async function onSaveOrder() {
    const orders = alphabeticalOrdersByParent(payload.items)
    setMessages([])
    try {
      await reorderNavBuilderItems(orders)
      setMessages([{ type: 'success', message: 'Alphabetical menu order saved.' }])
      await load()
    } catch (error) {
      setMessages([{ type: 'warning', message: error instanceof Error ? error.message : 'Unable to reorder menu items.' }])
    }
  }

  function editItem(item: NavBuilderItem) {
    setForm({
      id: String(item.id),
      menu_parent_id: item.menu_parent_id == null ? '' : String(item.menu_parent_id),
      label: item.label ?? '',
      url: item.url ?? '',
      icon_class: item.icon_class ?? '',
      target: item.target ?? '',
      sort_order: String(item.sort_order ?? item.order ?? 0),
      is_enabled: Boolean(item.is_enabled ?? item.enabled),
      roles: item.roles?.ids?.map(String) ?? [],
    })
    window.scrollTo({ top: 0, behavior: 'smooth' })
  }

  const alphabeticalOrders = useMemo(() => alphabeticalOrdersByParent(sortedItems), [sortedItems])

  const editingItem = form.id
    ? payload.items.find((item) => String(item.id) === form.id) ?? null
    : null

  return (
    <div className="container-fluid px-0 nav-builder-page">
      <div className="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
        <div>
          <h3 className="mb-1">Navbar Builder</h3>
        </div>
        <button className="btn btn-outline-secondary btn-sm" type="button" onClick={() => void load()}>
          Refresh
        </button>
      </div>

      <CiFlashMessages messages={messages} />

      <div className="card mb-4 rounded-0 border-0" id="menuTableCard">
        <div className="card-header d-flex justify-content-between align-items-center">
          <span>Current Menu</span>
          <button className="btn btn-sm btn-outline-primary" type="button" onClick={() => void onSaveOrder()}>
            Save alphabetical order
          </button>
        </div>
        <div className="card-body">
          <div id="menuTableWrap" className="table-responsive">
            <table id="menuTable" className="table table-striped table-hover align-middle">
              <thead>
                <tr>
                  <th>Actions</th>
                  <th>Label</th>
                  <th>URL</th>
                  <th>Parent</th>
                  <th>Roles</th>
                  <th>Alphabetical position</th>
                  <th>Enabled</th>
                  <th>Depth</th>
                </tr>
              </thead>
              <tbody>
                {loading ? (
                  <tr>
                    <td colSpan={8} className="text-center text-muted">Loading menu...</td>
                  </tr>
                ) : payload.items.length === 0 ? (
                  <tr>
                    <td colSpan={8} className="text-center text-muted">No menu items found.</td>
                  </tr>
                ) : (
                  sortedItems.map((item) => (
                    <tr key={item.id}>
                      <td className="text-nowrap">
                        <button className="btn btn-sm btn-outline-primary me-1" type="button" onClick={() => editItem(item)}>
                          Edit
                        </button>
                        <button className="btn btn-sm btn-outline-danger" type="button" onClick={() => void onDelete(item)}>
                          Delete
                        </button>
                      </td>
                      <td>
                        <span className="text-muted">{'- '.repeat(item.depth ?? 0)}</span>
                        {item.label}
                        {item.icon_class ? <code className="ms-2 small">{item.icon_class}</code> : null}
                      </td>
                      <td>{item.url ? <code>{item.url}</code> : <span className="text-muted">-</span>}</td>
                      <td>{parentLabel(item, sortedItems)}</td>
                      <td>
                        {(item.roles?.names ?? []).length > 0 ? (
                          item.roles?.names?.map((roleName) => (
                            <span key={roleName} className="badge badge-role me-1 mb-1">
                              {roleName}
                            </span>
                          ))
                        ) : (
                          <span className="text-muted">-</span>
                        )}
                      </td>
                      <td>
                        <span className="badge bg-light text-dark">
                          {alphabeticalOrders[String(item.id)] ?? '-'}
                        </span>
                      </td>
                      <td>
                        {(item.is_enabled ?? item.enabled) ? (
                          <span className="badge bg-success">Yes</span>
                        ) : (
                          <span className="badge bg-light text-dark">No</span>
                        )}
                      </td>
                      <td>{item.depth ?? 0}</td>
                    </tr>
                  ))
                )}
              </tbody>
            </table>
          </div>
        </div>
      </div>

      <div className="row">
        <div className="col-lg-5">
          <div className="card mb-3">
            <div className="card-header d-flex justify-content-between align-items-center">
              <span>{editingItem ? `Edit Item #${editingItem.id}` : 'Add / Edit Item'}</span>
              {editingItem ? <span className="badge bg-primary">Editing</span> : null}
            </div>
            <div className="card-body">
              {editingItem ? (
                <div className="alert alert-info py-2">
                  Editing <strong>{editingItem.label}</strong>. Save will update the existing menu item.
                </div>
              ) : null}
              <form onSubmit={onSubmit}>
                <input type="hidden" name="id" value={form.id} />
                <div className="mb-2">
                  <label className="form-label" htmlFor="menu_parent_id">Parent</label>
                  <select
                    name="menu_parent_id"
                    id="menu_parent_id"
                    className="form-select"
                    value={form.menu_parent_id}
                    onChange={(event) => setForm((current) => ({ ...current, menu_parent_id: event.target.value }))}
                  >
                    <option value="">- Top level -</option>
                    {filteredParentOptions.map((option) => (
                      <option key={option.id} value={option.id}>{option.label}</option>
                    ))}
                  </select>
                </div>

                <div className="mb-2">
                  <label className="form-label" htmlFor="label">Label</label>
                  <input
                    className="form-control"
                    name="label"
                    id="label"
                    value={form.label}
                    onChange={(event) => setForm((current) => ({ ...current, label: event.target.value }))}
                    required
                  />
                </div>

                <div className="mb-2">
                  <label className="form-label" htmlFor="url">URL (relative or absolute)</label>
                  <input
                    className="form-control"
                    name="url"
                    id="url"
                    placeholder="e.g. administrator/student_profiles"
                    value={form.url}
                    onChange={(event) => setForm((current) => ({ ...current, url: event.target.value }))}
                  />
                </div>

                <div className="row">
                  <div className="col-md-4 mb-2">
                    <label className="form-label" htmlFor="target">Target</label>
                    <select
                      name="target"
                      id="target"
                      className="form-select"
                      value={form.target}
                      onChange={(event) => setForm((current) => ({ ...current, target: event.target.value }))}
                    >
                      <option value="">(default)</option>
                      <option value="_blank">_blank</option>
                      <option value="_self">_self</option>
                    </select>
                  </div>
                  <div className="col-md-4 mb-2">
                    <label className="form-label">Order</label>
                    <div className="form-control bg-light text-muted">Alphabetical</div>
                  </div>
                  <div className="col-md-4 mb-2 d-flex align-items-end">
                    <div className="form-check">
                      <input
                        className="form-check-input"
                        type="checkbox"
                        name="is_enabled"
                        id="is_enabled"
                        checked={form.is_enabled}
                        onChange={(event) => setForm((current) => ({ ...current, is_enabled: event.target.checked }))}
                      />
                      <label className="form-check-label" htmlFor="is_enabled">Enabled</label>
                    </div>
                  </div>
                </div>

                <div className="mb-2">
                  <label className="form-label" htmlFor="icon_class">Icon class</label>
                  <input
                    className="form-control"
                    name="icon_class"
                    id="icon_class"
                    placeholder="e.g. fas fa-user-graduate"
                    value={form.icon_class}
                    onChange={(event) => setForm((current) => ({ ...current, icon_class: event.target.value }))}
                  />
                </div>

                <div className="mb-2">
                  <label className="form-label" htmlFor="roles">Roles (who can see)</label>
                  <select
                    name="roles"
                    id="roles"
                    className="form-select"
                    multiple
                    size={6}
                    value={form.roles}
                    onChange={(event) =>
                      setForm((current) => ({
                        ...current,
                        roles: Array.from(event.target.selectedOptions).map((option) => option.value),
                      }))
                    }
                  >
                    {payload.roles.map((role) => (
                      <option key={role.id} value={role.id}>{role.name}</option>
                    ))}
                  </select>
                  <small className="text-muted">Hold Ctrl/Cmd to select multiple.</small>
                </div>

                <button className="btn btn-primary" type="submit" disabled={saving}>
                  {saving ? 'Saving...' : editingItem ? 'Update' : 'Save'}
                </button>{' '}
                <button className="btn btn-secondary" type="button" onClick={() => setForm(blankForm)}>
                  {editingItem ? 'Cancel edit' : 'Reset'}
                </button>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

function parentLabel(item: NavBuilderItem, items: NavBuilderItem[]) {
  if (item.parent_label) return item.parent_label
  const parentId = item.menu_parent_id ?? item.parent_id
  if (!parentId) return '-'
  return items.find((row) => row.id === parentId)?.label ?? `#${parentId}`
}
