import { readFile } from "node:fs/promises"
import path from "node:path"
import Link from "next/link"

import { AdminShell } from "@/components/admin-shell"
import { requirePermissionForRoute } from "@/lib/route-guards"

export const dynamic = "force-dynamic"

type TodoState = "done" | "partial" | "planned"
type TodoPriority = "P1" | "P2" | "P3" | "NONE"
type StateFilter = "all" | TodoState
type PriorityFilter = "all" | Exclude<TodoPriority, "NONE">
type MetaFilter = "show" | "hide"

type TodoTask = {
  state: TodoState
  text: string
  priority: TodoPriority
}

type TodoBlock = {
  title: string
  tasks: TodoTask[]
  notes: string[]
}

type TodoSection = {
  title: string
  blocks: TodoBlock[]
  notes: string[]
}

type ProgressCounts = {
  done: number
  partial: number
  planned: number
}

type SearchParamsInput = Record<string, string | string[] | undefined>

const metaSections = new Set(["Status Legend", "Priority Legend", "How We Use This File"])

const stateOrder: Record<TodoState, number> = {
  partial: 1,
  planned: 2,
  done: 3,
}

const priorityOrder: Record<TodoPriority, number> = {
  P1: 1,
  P2: 2,
  P3: 3,
  NONE: 4,
}

function isMetaSection(section: TodoSection | null): boolean {
  return section ? metaSections.has(section.title) : false
}

function toTaskState(token: string): TodoState {
  const value = token.toLowerCase()

  if (value === "x") {
    return "done"
  }

  if (value === "~") {
    return "partial"
  }

  return "planned"
}

function parsePriority(rawText: string): { priority: TodoPriority; text: string } {
  const match = rawText.match(/^\[(P[1-3])\]\s+(.+)$/i)

  if (!match) {
    return {
      priority: "NONE",
      text: rawText.trim(),
    }
  }

  return {
    priority: match[1].toUpperCase() as Exclude<TodoPriority, "NONE">,
    text: match[2].trim(),
  }
}

function parseTodo(content: string): TodoSection[] {
  const sections: TodoSection[] = []
  let currentSection: TodoSection | null = null
  let currentBlock: TodoBlock | null = null

  const ensureSection = (title = "General"): TodoSection => {
    if (!currentSection) {
      currentSection = {
        title,
        blocks: [],
        notes: [],
      }
      sections.push(currentSection)
    }

    return currentSection
  }

  const ensureBlock = (title = "Overview"): TodoBlock => {
    const section = ensureSection()

    if (!currentBlock) {
      currentBlock = {
        title,
        tasks: [],
        notes: [],
      }
      section.blocks.push(currentBlock)
    }

    return currentBlock
  }

  for (const rawLine of content.split("\n")) {
    const line = rawLine.trim()

    if (!line) {
      continue
    }

    if (line.startsWith("# ")) {
      continue
    }

    if (line.startsWith("## ")) {
      currentSection = {
        title: line.replace("## ", "").trim(),
        blocks: [],
        notes: [],
      }
      sections.push(currentSection)
      currentBlock = null
      continue
    }

    if (line.startsWith("### ")) {
      const section = ensureSection()

      currentBlock = {
        title: line.replace("### ", "").trim(),
        tasks: [],
        notes: [],
      }

      section.blocks.push(currentBlock)
      continue
    }

    const taskMatch = line.match(/^- \[([ x~X])\] (.+)$/)

    if (taskMatch) {
      if (isMetaSection(currentSection)) {
        ensureSection().notes.push(taskMatch[0].replace("- ", ""))
        continue
      }

      const task = parsePriority(taskMatch[2].trim())

      ensureBlock().tasks.push({
        state: toTaskState(taskMatch[1]),
        text: task.text,
        priority: task.priority,
      })
      continue
    }

    const bulletMatch = line.match(/^- (.+)$/)

    if (bulletMatch) {
      if (currentBlock) {
        currentBlock.notes.push(bulletMatch[1].trim())
      } else {
        ensureSection().notes.push(bulletMatch[1].trim())
      }
    }
  }

  return sections
}

function getProgressCounts(sections: TodoSection[]): ProgressCounts {
  const counts: ProgressCounts = {
    done: 0,
    partial: 0,
    planned: 0,
  }

  for (const section of sections) {
    if (isMetaSection(section)) {
      continue
    }

    for (const block of section.blocks) {
      for (const task of block.tasks) {
        counts[task.state] += 1
      }
    }
  }

  return counts
}

function getProgressPercent(progress: ProgressCounts): number {
  const total = progress.done + progress.partial + progress.planned

  if (total === 0) {
    return 0
  }

  return Math.round(((progress.done + progress.partial * 0.5) / total) * 100)
}

async function getTodoMarkdown(): Promise<string> {
  const candidates = [
    path.resolve(process.cwd(), "TODO.md"),
    path.resolve(process.cwd(), "../TODO.md"),
    path.resolve(process.cwd(), "../../TODO.md"),
  ]

  for (const filePath of candidates) {
    try {
      return await readFile(filePath, "utf8")
    } catch {
      // Try next candidate path.
    }
  }

  return "# TODO file not found\n\nCreate `/TODO.md` in the repository root to track progress."
}

function statusBadgeClass(state: TodoState): string {
  if (state === "done") {
    return "bg-emerald-50 text-emerald-700 border-emerald-200"
  }

  if (state === "partial") {
    return "bg-amber-50 text-amber-700 border-amber-200"
  }

  return "bg-slate-50 text-slate-700 border-slate-200"
}

function statusLabel(state: TodoState): string {
  if (state === "done") {
    return "Done"
  }

  if (state === "partial") {
    return "Partial"
  }

  return "Planned"
}

function priorityBadgeClass(priority: TodoPriority): string {
  if (priority === "P1") {
    return "bg-rose-50 text-rose-700 border-rose-200"
  }

  if (priority === "P2") {
    return "bg-indigo-50 text-indigo-700 border-indigo-200"
  }

  if (priority === "P3") {
    return "bg-teal-50 text-teal-700 border-teal-200"
  }

  return "bg-neutral-100 text-neutral-700 border-neutral-200"
}

function normalizeStateFilter(value: string | undefined): StateFilter {
  if (value === "done" || value === "partial" || value === "planned") {
    return value
  }

  return "all"
}

function normalizePriorityFilter(value: string | undefined): PriorityFilter {
  if (value === "P1" || value === "P2" || value === "P3") {
    return value
  }

  return "all"
}

function normalizeMetaFilter(value: string | undefined): MetaFilter {
  if (value === "show") {
    return "show"
  }

  return "hide"
}

function toSingleQueryValue(value: string | string[] | undefined): string | undefined {
  if (Array.isArray(value)) {
    return value[0]
  }

  return value
}

function sortTasks(tasks: TodoTask[]): TodoTask[] {
  return [...tasks].sort((left, right) => {
    const priorityDelta = priorityOrder[left.priority] - priorityOrder[right.priority]

    if (priorityDelta !== 0) {
      return priorityDelta
    }

    const stateDelta = stateOrder[left.state] - stateOrder[right.state]

    if (stateDelta !== 0) {
      return stateDelta
    }

    return left.text.localeCompare(right.text)
  })
}

function applyFilters(
  sections: TodoSection[],
  stateFilter: StateFilter,
  priorityFilter: PriorityFilter,
  metaFilter: MetaFilter,
): TodoSection[] {
  return sections
    .filter((section) => metaFilter === "show" || !isMetaSection(section))
    .map((section) => {
      const filteredBlocks = section.blocks
        .map((block) => {
          const filteredTasks = sortTasks(
            block.tasks.filter((task) => {
              const stateMatches = stateFilter === "all" || task.state === stateFilter
              const priorityMatches = priorityFilter === "all" || task.priority === priorityFilter

              return stateMatches && priorityMatches
            }),
          )

          return {
            ...block,
            tasks: filteredTasks,
          }
        })
        .filter((block) => block.tasks.length > 0 || block.notes.length > 0)

      return {
        ...section,
        blocks: filteredBlocks,
      }
    })
    .filter((section) => section.blocks.length > 0 || section.notes.length > 0)
}

function buildFilterHref(
  current: { state: StateFilter; priority: PriorityFilter; meta: MetaFilter },
  overrides: Partial<{ state: StateFilter; priority: PriorityFilter; meta: MetaFilter }>,
): string {
  const next = {
    ...current,
    ...overrides,
  }

  const params = new URLSearchParams()

  if (next.state !== "all") {
    params.set("state", next.state)
  }

  if (next.priority !== "all") {
    params.set("priority", next.priority)
  }

  if (next.meta !== "hide") {
    params.set("meta", next.meta)
  }

  const query = params.toString()

  if (!query) {
    return "/todo"
  }

  return `/todo?${query}`
}

function filterButtonClass(active: boolean): string {
  return active
    ? "bg-neutral-900 text-white border-neutral-900"
    : "bg-white text-neutral-700 border-neutral-300 hover:bg-neutral-100"
}

export default async function AdminTodoPage(props: {
  searchParams?: SearchParamsInput | Promise<SearchParamsInput>
}) {
  const role = await requirePermissionForRoute({
    nextPath: "/todo",
    permission: "roadmap:read",
    scope: "global",
  })

  const content = await getTodoMarkdown()
  const sections = parseTodo(content)
  const progress = getProgressCounts(sections)
  const completionPercent = getProgressPercent(progress)

  const rawSearchParams = props.searchParams ? await props.searchParams : {}
  const stateFilter = normalizeStateFilter(toSingleQueryValue(rawSearchParams.state))
  const priorityFilter = normalizePriorityFilter(toSingleQueryValue(rawSearchParams.priority))
  const metaFilter = normalizeMetaFilter(toSingleQueryValue(rawSearchParams.meta))

  const filteredSections = applyFilters(sections, stateFilter, priorityFilter, metaFilter)

  const currentFilters = {
    state: stateFilter,
    priority: priorityFilter,
    meta: metaFilter,
  }

  return (
    <AdminShell
      role={role}
      activePath="/todo"
      badge="Admin App"
      title="Roadmap and Progress"
      description="Structured view from root TODO.md (single source of truth)."
      actions={
        <Link
          href="/"
          className="inline-flex rounded-md border border-neutral-300 px-4 py-2 text-sm font-medium hover:bg-neutral-100"
        >
          Back to dashboard
        </Link>
      }
    >
      <section className="rounded-xl border border-neutral-200 bg-neutral-50 p-5">
        <div className="mb-4 flex items-center justify-between gap-4">
          <p className="text-sm font-medium text-neutral-600">Weighted completion</p>
          <p className="text-sm font-semibold text-neutral-900">{completionPercent}%</p>
        </div>

        <div className="h-2 w-full rounded-full bg-neutral-200">
          <div
            className="h-2 rounded-full bg-neutral-900"
            style={{ width: `${completionPercent}%` }}
          />
        </div>
      </section>

      <section className="grid gap-4 sm:grid-cols-3">
        <article className="rounded-xl border border-emerald-200 bg-emerald-50/50 p-4">
          <p className="text-sm text-emerald-700">Completed items</p>
          <p className="mt-2 text-3xl font-semibold text-emerald-900">{progress.done}</p>
        </article>

        <article className="rounded-xl border border-amber-200 bg-amber-50/60 p-4">
          <p className="text-sm text-amber-700">Partially done items</p>
          <p className="mt-2 text-3xl font-semibold text-amber-900">{progress.partial}</p>
        </article>

        <article className="rounded-xl border border-slate-300 bg-slate-50 p-4">
          <p className="text-sm text-slate-700">Planned items</p>
          <p className="mt-2 text-3xl font-semibold text-slate-900">{progress.planned}</p>
        </article>
      </section>

      <section className="rounded-xl border border-neutral-200 p-5">
        <div className="flex flex-wrap items-center gap-2">
          <p className="mr-2 text-xs font-semibold uppercase tracking-wide text-neutral-500">
            Status
          </p>

          {(["all", "planned", "partial", "done"] as const).map((value) => (
            <Link
              key={value}
              href={buildFilterHref(currentFilters, { state: value })}
              className={`rounded-full border px-3 py-1.5 text-xs font-medium ${filterButtonClass(stateFilter === value)}`}
            >
              {value === "all" ? "All" : statusLabel(value)}
            </Link>
          ))}
        </div>

        <div className="mt-3 flex flex-wrap items-center gap-2">
          <p className="mr-2 text-xs font-semibold uppercase tracking-wide text-neutral-500">
            Priority
          </p>

          {(["all", "P1", "P2", "P3"] as const).map((value) => (
            <Link
              key={value}
              href={buildFilterHref(currentFilters, { priority: value })}
              className={`rounded-full border px-3 py-1.5 text-xs font-medium ${filterButtonClass(priorityFilter === value)}`}
            >
              {value}
            </Link>
          ))}

          <Link
            href={buildFilterHref(currentFilters, {
              meta: metaFilter === "show" ? "hide" : "show",
            })}
            className={`ml-auto rounded-full border px-3 py-1.5 text-xs font-medium ${filterButtonClass(metaFilter === "show")}`}
          >
            {metaFilter === "show" ? "Hide Meta Sections" : "Show Meta Sections"}
          </Link>
        </div>
      </section>

      <section className="grid gap-5">
        {filteredSections.length === 0 ? (
          <article className="rounded-xl border border-neutral-200 p-5 text-sm text-neutral-600">
            No items match the current filter.
          </article>
        ) : (
          filteredSections.map((section) => (
            <article key={section.title} className="rounded-xl border border-neutral-200 p-5">
              <h2 className="text-xl font-semibold tracking-tight text-neutral-900">
                {section.title}
              </h2>

              {section.notes.length > 0 ? (
                <ul className="mt-3 list-disc space-y-1 pl-5 text-sm text-neutral-600">
                  {section.notes.map((note) => (
                    <li key={note}>{note}</li>
                  ))}
                </ul>
              ) : null}

              <div className="mt-4 grid gap-4">
                {section.blocks.map((block) => (
                  <section
                    key={`${section.title}-${block.title}`}
                    className="rounded-lg bg-neutral-50 p-4"
                  >
                    <h3 className="text-sm font-semibold uppercase tracking-wide text-neutral-500">
                      {block.title}
                    </h3>

                    <ul className="mt-3 space-y-2">
                      {block.tasks.map((task) => (
                        <li
                          key={`${block.title}-${task.priority}-${task.text}`}
                          className="flex flex-wrap items-center justify-between gap-2 rounded-md border border-neutral-200 bg-white px-3 py-2"
                        >
                          <div className="flex flex-wrap items-center gap-2">
                            {task.priority !== "NONE" ? (
                              <span
                                className={`rounded-full border px-2 py-0.5 text-[11px] font-semibold ${priorityBadgeClass(task.priority)}`}
                              >
                                {task.priority}
                              </span>
                            ) : null}
                            <p className="text-sm text-neutral-800">{task.text}</p>
                          </div>

                          <span
                            className={`rounded-full border px-2.5 py-1 text-xs font-medium ${statusBadgeClass(task.state)}`}
                          >
                            {statusLabel(task.state)}
                          </span>
                        </li>
                      ))}
                    </ul>

                    {block.notes.length > 0 ? (
                      <ul className="mt-3 list-disc space-y-1 pl-5 text-sm text-neutral-600">
                        {block.notes.map((note) => (
                          <li key={note}>{note}</li>
                        ))}
                      </ul>
                    ) : null}
                  </section>
                ))}
              </div>
            </article>
          ))
        )}
      </section>

      <details className="rounded-xl border border-neutral-200 p-5">
        <summary className="cursor-pointer text-sm font-medium text-neutral-700">
          View raw markdown source (`TODO.md`)
        </summary>
        <pre className="mt-4 overflow-x-auto whitespace-pre-wrap rounded-lg bg-neutral-50 p-4 text-sm leading-6 text-neutral-800">
          {content}
        </pre>
      </details>
    </AdminShell>
  )
}
