import { type FormEvent, useCallback, useEffect, useMemo, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { ApiHttpError } from '../../api/http'
import {
  fetchBadgeFormOptions,
  fetchBadgePrintStatus,
  postBadgeGenerate,
  type BadgeUserRow,
} from '../../api/printables'
import { AcademicFilterBar } from '../discounts/AcademicFilterBar'

type BadgeRoleGroup = 'all' | 'teacher' | 'ta' | 'admin' | 'staff' | 'student'
type SortKey = 'firstname' | 'lastname' | 'role' | 'className' | 'prints'
type SortDir = 'asc' | 'desc'

type RowMeta = {
  rowKey: string
  entityType: 'user' | 'student'
  numericId: string
  firstname: string
  lastname: string
  roleLabel: string
  className: string
  roleGroup: BadgeRoleGroup
}

function pickRoleLabel(u: BadgeUserRow): string {
  const raw = (u.role_name ?? u.active_role ?? u.roles ?? '') as string
  if (typeof raw === 'string' && raw.includes(',')) {
    const parts = raw.split(',').map((s) => s.trim()).filter(Boolean)
    return parts[0] ?? ''
  }
  return String(raw || '')
}

function pickNumericId(u: BadgeUserRow): string | null {
  const id =
    u.entity_type === 'student'
      ? u.student_id
      : u.user_id ?? u.id ?? (u as { 'users.id'?: unknown })['users.id']
  if (id == null || id === '') return null
  return String(id)
}

function pickEntityType(u: BadgeUserRow): 'user' | 'student' {
  return u.entity_type === 'student' || u.student_id != null ? 'student' : 'user'
}

function pickRoleGroup(u: BadgeUserRow): BadgeRoleGroup {
  const raw = String(u.role_group ?? '').toLowerCase()
  if (raw === 'teacher' || raw === 'ta' || raw === 'admin' || raw === 'staff' || raw === 'student') {
    return raw
  }
  return pickEntityType(u) === 'student' ? 'student' : 'staff'
}

export function BadgeFormPage() {
  const [searchParams] = useSearchParams()
  const schoolYear = searchParams.get('school_year') ?? undefined
  const semester = searchParams.get('semester') ?? undefined

  const [users, setUsers] = useState<BadgeUserRow[]>([])
  const [rolesTabs, setRolesTabs] = useState<Record<string, string>>({})
  const [activeRole, setActiveRole] = useState<BadgeRoleGroup>('all')
  const [selectedYear, setSelectedYear] = useState('')
  const [filterText, setFilterText] = useState('')
  const [selected, setSelected] = useState<Set<string>>(() => new Set())
  const [printCounts, setPrintCounts] = useState<Record<string, number>>({})
  const [page, setPage] = useState(0)
  const [sortKey, setSortKey] = useState<SortKey>('lastname')
  const [sortDir, setSortDir] = useState<SortDir>('asc')
  const pageSize = 100

  const rows = useMemo((): RowMeta[] => {
    const out: RowMeta[] = []
    for (const u of users) {
      const numericId = pickNumericId(u)
      if (!numericId) continue
      const roleLabel = pickRoleLabel(u)
      const entityType = pickEntityType(u)
      out.push({
        rowKey: `${entityType}:${numericId}`,
        entityType,
        numericId,
        firstname: String(u.firstname ?? ''),
        lastname: String(u.lastname ?? ''),
        roleLabel,
        className: String(u.class_section_name ?? u.registration_grade ?? ''),
        roleGroup: pickRoleGroup(u),
      })
    }
    return out
  }, [users])

  const filtered = useMemo(() => {
    const q = filterText.trim().toLowerCase()
    const visible = activeRole === 'all' ? rows : rows.filter((r) => r.roleGroup === activeRole)
    if (!q) return visible
    return visible.filter((r) => {
      return (
        r.firstname.toLowerCase().includes(q) ||
        r.lastname.toLowerCase().includes(q) ||
        r.roleLabel.toLowerCase().includes(q) ||
        r.className.toLowerCase().includes(q)
      )
    })
  }, [activeRole, filterText, rows])

  const sorted = useMemo(() => {
    const direction = sortDir === 'asc' ? 1 : -1
    return [...filtered].sort((a, b) => {
      const aPrints = printCounts[a.numericId] ?? 0
      const bPrints = printCounts[b.numericId] ?? 0

      const valueFor = (row: RowMeta, prints: number): string | number => {
        switch (sortKey) {
          case 'firstname':
            return row.firstname.toLowerCase()
          case 'lastname':
            return row.lastname.toLowerCase()
          case 'role':
            return row.roleLabel.toLowerCase()
          case 'className':
            return row.className.toLowerCase()
          case 'prints':
            return prints
        }
      }

      const av = valueFor(a, aPrints)
      const bv = valueFor(b, bPrints)

      if (typeof av === 'number' && typeof bv === 'number') {
        if (av === bv) {
          return a.lastname.localeCompare(b.lastname) * direction
        }
        return (av - bv) * direction
      }

      const cmp = String(av).localeCompare(String(bv))
      if (cmp !== 0) return cmp * direction

      return a.lastname.localeCompare(b.lastname) * direction
    })
  }, [filtered, printCounts, sortDir, sortKey])

  const pageRows = useMemo(() => {
    const start = page * pageSize
    return sorted.slice(start, start + pageSize)
  }, [page, sorted])

  useEffect(() => {
    setPage(0)
  }, [activeRole, filterText, sortDir, sortKey])

  const rowMetaMap = useMemo(() => {
    const m: Record<string, RowMeta> = {}
    for (const r of rows) {
      m[r.rowKey] = r
    }
    return m
  }, [rows])

  const refreshStatus = useCallback(async () => {
    const userIds = rows.filter((r) => r.entityType === 'user').map((r) => r.numericId)
    const studentIds = rows.filter((r) => r.entityType === 'student').map((r) => r.numericId)
    if (userIds.length === 0 && studentIds.length === 0) return
    try {
      const res = await fetchBadgePrintStatus({
        user_ids: userIds,
        student_ids: studentIds,
        school_year: selectedYear || schoolYear,
      })
      const data = res.data ?? {}
      const next: Record<string, number> = {}
      for (const id of [...userIds, ...studentIds]) {
        const info = data[id] as { count?: number } | undefined
        next[id] = Number(info?.count ?? 0)
      }
      setPrintCounts(next)
    } catch {
      /* ignore */
    }
  }, [rows, selectedYear, schoolYear])

  useEffect(() => {
    fetchBadgeFormOptions({ school_year: schoolYear, semester })
      .then((d) => {
        setUsers(d.users ?? [])
        setRolesTabs(d.rolesTabs ?? {})
        setActiveRole((d.active_role as BadgeRoleGroup | undefined) ?? 'all')
        setSelectedYear(d.selectedYear ?? schoolYear ?? '')
      })
      .catch(() => {})
  }, [schoolYear, semester])

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

  useEffect(() => {
    function onFocus() {
      refreshStatus()
    }
    window.addEventListener('focus', onFocus)
    return () => window.removeEventListener('focus', onFocus)
  }, [refreshStatus])

  function toggle(id: string, checked: boolean) {
    setSelected((prev) => {
      const n = new Set(prev)
      if (checked) n.add(id)
      else n.delete(id)
      return n
    })
  }

  function selectAllPage(checked: boolean) {
    setSelected((prev) => {
      const n = new Set(prev)
      for (const r of pageRows) {
        if (checked) n.add(r.rowKey)
        else n.delete(r.rowKey)
      }
      return n
    })
  }

  const pageAllSelected =
    pageRows.length > 0 && pageRows.every((r) => selected.has(r.rowKey))
  const pageSome = pageRows.some((r) => selected.has(r.rowKey))

  async function handleSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault()
    if (selected.size === 0) {
      alert('Please select at least one person to generate badges.')
      return
    }
    const fd = new FormData()
    fd.set('school_year', selectedYear || schoolYear || '')
    for (const rowKey of selected) {
      const meta = rowMetaMap[rowKey]
      if (!meta) continue
      if (meta.entityType === 'student') {
        fd.append('student_ids[]', meta.numericId)
        continue
      }
      fd.append('user_ids[]', meta.numericId)
      fd.set(`roles[${meta.numericId}]`, meta.roleLabel)
      fd.set(`classes[${meta.numericId}]`, meta.className)
    }
    try {
      const blob = await postBadgeGenerate(fd)
      window.open(URL.createObjectURL(blob), '_blank', 'noopener')
      window.setTimeout(() => {
        setSelected(new Set())
        refreshStatus()
      }, 5000)
    } catch (err: unknown) {
      alert(err instanceof ApiHttpError ? err.message : 'Generate failed.')
    }
  }

  const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize))

  function changeSort(nextKey: SortKey) {
    if (sortKey === nextKey) {
      setSortDir((prev) => (prev === 'asc' ? 'desc' : 'asc'))
      return
    }

    setSortKey(nextKey)
    setSortDir(nextKey === 'prints' ? 'desc' : 'asc')
  }

  function sortIndicator(key: SortKey): string {
    if (sortKey !== key) return ''
    return sortDir === 'asc' ? ' ▲' : ' ▼'
  }

  function renderSortButton(label: string, key: SortKey) {
    return (
      <button
        type="button"
        className="bg-transparent border-0 p-0 m-0 fw-semibold"
        style={{ cursor: 'pointer', color: '#212529' }}
        onClick={() => changeSort(key)}
      >
        {label}
        {sortIndicator(key)}
      </button>
    )
  }

  return (
    <div className="container-fluid py-3">
      <div className="text-center mx-auto mb-4" style={{ maxWidth: 600 }}>
        <h2 className="text-dark mb-2">Generate School Badges</h2>
      </div>

      <AcademicFilterBar />

      {Object.keys(rolesTabs).length > 0 ? (
        <div className="d-flex flex-wrap gap-2 mb-3">
          {Object.entries(rolesTabs).map(([key, label]) => {
            const normalized = (key as BadgeRoleGroup) || 'all'
            return (
              <button
                key={key}
                type="button"
                className={`btn btn-sm ${activeRole === normalized ? 'btn-primary' : 'btn-outline-primary'}`}
                onClick={() => setActiveRole(normalized)}
              >
                {label}
              </button>
            )
          })}
        </div>
      ) : null}

      <div className="d-flex flex-column flex-md-row justify-content-md-end align-items-md-center gap-2 mb-3">
        <button type="submit" className="btn btn-success" form="badgeForm">
          Generate Badges
        </button>
      </div>

      <div className="row g-2 mb-3">
        <div className="col-md-6">
          <label className="form-label small text-muted" htmlFor="badgeFilter">
            Filter by name, role, or class
          </label>
          <input
            id="badgeFilter"
            type="search"
            className="form-control form-control-sm"
            value={filterText}
            onChange={(e) => setFilterText(e.target.value)}
            placeholder="Search…"
          />
        </div>
        <div className="col-md-6 d-flex align-items-end justify-content-md-end gap-2 small text-muted">
          <span>
            Showing {sorted.length === 0 ? 0 : page * pageSize + 1}–
            {Math.min((page + 1) * pageSize, sorted.length)} of {sorted.length}
          </span>
          <span>
            Page {totalPages === 0 ? 0 : page + 1} of {totalPages}
          </span>
          <button
            type="button"
            className="btn btn-outline-secondary btn-sm"
            disabled={page <= 0}
            onClick={() => setPage(0)}
          >
            First
          </button>
          <button
            type="button"
            className="btn btn-outline-secondary btn-sm"
            disabled={page <= 0}
            onClick={() => setPage((p) => Math.max(0, p - 1))}
          >
            Prev
          </button>
          <button
            type="button"
            className="btn btn-outline-secondary btn-sm"
            disabled={page >= totalPages - 1}
            onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}
          >
            Next
          </button>
          <button
            type="button"
            className="btn btn-outline-secondary btn-sm"
            disabled={page >= totalPages - 1}
            onClick={() => setPage(Math.max(0, totalPages - 1))}
          >
            Last
          </button>
        </div>
      </div>

      <form id="badgeForm" onSubmit={handleSubmit}>
        <table className="table table-bordered table-striped align-middle w-100">
          <thead className="table-dark">
            <tr>
              <th style={{ width: 70 }}>#</th>
              <th>{renderSortButton('Firstname', 'firstname')}</th>
              <th>{renderSortButton('Lastname', 'lastname')}</th>
              <th>{renderSortButton('Role', 'role')}</th>
              <th>{renderSortButton('Class / Section', 'className')}</th>
              <th style={{ width: 140 }}>{renderSortButton('Badge Prints', 'prints')}</th>
              <th style={{ width: 160 }}>
                <div className="form-check m-0">
                  <input
                    className="form-check-input"
                    type="checkbox"
                    checked={pageAllSelected}
                    ref={(el) => {
                      if (el) el.indeterminate = !pageAllSelected && pageSome
                    }}
                    onChange={(e) => selectAllPage(e.target.checked)}
                    id="select-all-page"
                  />
                  <label className="form-check-label" htmlFor="select-all-page">
                    Select All (page)
                  </label>
                </div>
              </th>
            </tr>
          </thead>
          <tbody>
            {pageRows.length === 0 ? (
              <tr>
                <td colSpan={7} className="text-center text-muted">
                  No badge records found for the selected year.
                </td>
              </tr>
            ) : (
              pageRows.map((r, i) => {
                const order = page * pageSize + i + 1
                const count = printCounts[r.numericId] ?? 0
                return (
                  <tr
                    key={r.rowKey}
                    className={count > 0 ? 'table-success' : undefined}
                  >
                    <td className="text-center">{order}</td>
                    <td>{r.firstname}</td>
                    <td>{r.lastname}</td>
                    <td>{r.roleLabel || '—'}</td>
                    <td>{r.className || '—'}</td>
                    <td>
                      <span className={`badge ${count > 0 ? 'bg-success' : 'bg-secondary'}`}>
                        {count > 0 ? `Printed ${count}` : '—'}
                      </span>
                    </td>
                    <td>
                      <div className="form-check m-0">
                        <input
                          type="checkbox"
                          className="form-check-input"
                          checked={selected.has(r.rowKey)}
                          onChange={(e) => toggle(r.rowKey, e.target.checked)}
                          id={`chk-${r.rowKey}`}
                        />
                        <label className="form-check-label" htmlFor={`chk-${r.rowKey}`}>
                          Select
                        </label>
                      </div>
                    </td>
                  </tr>
                )
              })
            )}
          </tbody>
        </table>
      </form>
    </div>
  )
}
