import { useEffect, useMemo, useState } from 'react'
import { Link, Navigate, Route, Routes, useParams } from 'react-router-dom'
import './App.css'
import { projects as baseProjects } from './data/projects'
import type { Project } from './data/projects'

const THEME_STORAGE_KEY = 'portfolio-theme-v1'
const ADMIN_CONTENT_API = '/api/admin-content'

type ThemeName = 'sand' | 'ocean' | 'midnight'

type SiteContent = {
  homeEyebrow: string
  homeTitle: string
  homeIntro: string
  projectEyebrow: string
  quickTipsTitle: string
  quickTipsBody: string
}

const defaultSiteContent: SiteContent = {
  homeEyebrow: 'Siteforge',
  homeTitle: 'Sites and Apps I Build',
  homeIntro:
    'Live links generated from your domain list. Use the Admin panel to update cards and About pages without editing code.',
  projectEyebrow: 'Project Details',
  quickTipsTitle: 'Quick edit tips',
  quickTipsBody:
    'Open /admin to update card and featured page content. Changes are saved in your project data file via the API server.',
}

type ScanResult = {
  domain: string
  url: string
  title: string
  summary: string
  category: string
  access: Project['access']
  iconUrl?: string
  highlights: string[]
  stack: string[]
  source: string
}

function splitLines(value: string): string[] {
  return value
    .split('\n')
    .map((item) => item.trim())
    .filter(Boolean)
}

function joinLines(values: string[]): string {
  return values.join('\n')
}

function slugify(value: string): string {
  return value
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
}

function createUniqueSlug(baseSlug: string, list: Project[]): string {
  const trimmed = slugify(baseSlug) || 'new-project'
  const existing = new Set(list.map((item) => item.slug))

  if (!existing.has(trimmed)) {
    return trimmed
  }

  let index = 2
  while (existing.has(`${trimmed}-${index}`)) {
    index += 1
  }

  return `${trimmed}-${index}`
}

function createEmptyProject(existing: Project[]): Project {
  const slug = createUniqueSlug('new-project', existing)

  return {
    slug,
    title: 'New Project',
    domain: 'new-project.necloud.us',
    url: 'https://new-project.necloud.us',
    category: 'Custom',
    access: 'Public',
    ownership: 'built',
    status: 'Live',
    summary: 'Short card summary for this project.',
    details: 'Detailed description for this project.',
    features: ['Feature one'],
    scanSource: 'manual',
    featured: {
      headline: 'Featured page headline for this project.',
      problem: 'Describe the user problem this project solves.',
      solution: 'Describe how your app solves that problem.',
      stack: ['React', 'TypeScript'],
      highlights: ['Primary capability'],
      nextSteps: ['Next improvement'],
      updateNote: 'Updated from Admin.',
    },
  }
}

function readStoredTheme(): ThemeName {
  const saved = localStorage.getItem(THEME_STORAGE_KEY)
  if (saved === 'ocean' || saved === 'midnight' || saved === 'sand') {
    return saved
  }

  return 'sand'
}

async function fetchAdminContent(): Promise<
  | {
      projects: Project[]
      siteContent: SiteContent
    }
  | null
> {
  try {
    const response = await fetch(ADMIN_CONTENT_API)
    if (!response.ok) {
      return null
    }

    const payload = (await response.json()) as {
      projects?: Project[]
      siteContent?: Partial<SiteContent>
    }

    if (!payload.projects || !Array.isArray(payload.projects) || !payload.siteContent) {
      return null
    }

    return {
      projects: payload.projects,
      siteContent: {
        homeEyebrow: payload.siteContent.homeEyebrow ?? defaultSiteContent.homeEyebrow,
        homeTitle: payload.siteContent.homeTitle ?? defaultSiteContent.homeTitle,
        homeIntro: payload.siteContent.homeIntro ?? defaultSiteContent.homeIntro,
        projectEyebrow: payload.siteContent.projectEyebrow ?? defaultSiteContent.projectEyebrow,
        quickTipsTitle: payload.siteContent.quickTipsTitle ?? defaultSiteContent.quickTipsTitle,
        quickTipsBody: payload.siteContent.quickTipsBody ?? defaultSiteContent.quickTipsBody,
      },
    }
  } catch {
    return null
  }
}

async function persistAdminContent(projects: Project[], siteContent: SiteContent) {
  await fetch(ADMIN_CONTENT_API, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ projects, siteContent }),
  })
}

function normalizeUrl(input: string): string {
  const value = input.trim()
  if (!value) {
    throw new Error('URL is required')
  }

  if (/^https?:\/\//i.test(value)) {
    return new URL(value).href
  }

  return new URL(`https://${value}`).href
}

function titleFromDomain(domain: string): string {
  const base = domain.split('.')[0] ?? domain
  return base
    .replace(/[-_]+/g, ' ')
    .replace(/\b\w/g, (char) => char.toUpperCase())
}

function inferAccess(text: string): Project['access'] {
  const lowered = text.toLowerCase()
  const hasLogin = lowered.includes('log in') || lowered.includes('sign in')
  const hasPublic = lowered.includes('get started') || lowered.includes('home')

  if (hasLogin && hasPublic) {
    return 'Mixed'
  }

  if (hasLogin) {
    return 'Login required'
  }

  return 'Public'
}

function inferCategory(text: string): string {
  const lowered = text.toLowerCase()
  if (lowered.includes('weather') || lowered.includes('forecast')) return 'Weather'
  if (lowered.includes('budget') || lowered.includes('envelope')) return 'Finance'
  if (lowered.includes('bible') || lowered.includes('prayer')) return 'Faith'
  if (lowered.includes('music') || lowered.includes('playlist')) return 'Music'
  if (lowered.includes('recipe') || lowered.includes('meal')) return 'Food'
  if (lowered.includes('file') || lowered.includes('sync') || lowered.includes('storage')) {
    return 'File Tools'
  }

  return 'Custom'
}

function firstUsefulLine(text: string): string | null {
  const lines = text
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean)

  for (const line of lines) {
    if (line.startsWith('#')) continue
    if (line.startsWith('![')) continue
    if (line.startsWith('[')) continue
    if (line.length < 30) continue
    if (line.length > 220) continue
    return line
  }

  return null
}

function uniqueItems(items: string[]): string[] {
  return Array.from(new Set(items.filter(Boolean)))
}

function extractScanResult(text: string, url: string, source: string): ScanResult {
  const parsed = new URL(url)
  const domain = parsed.hostname
  const headingMatch = text.match(/^#\s+(.+)$/m)
  const title = (headingMatch?.[1]?.trim() || titleFromDomain(domain)).replace(/\s+/g, ' ')

  const summary =
    firstUsefulLine(text) ||
    `Project page detected for ${title}. Review and refine this summary in Admin.`

  const imageMatch = text.match(/!\[[^\]]*\]\((https?:\/\/[^)\s]+)\)/i)
  const iconUrl = imageMatch?.[1]

  const lowered = text.toLowerCase()
  const highlights = uniqueItems([
    lowered.includes('dashboard') ? 'Dashboard' : '',
    lowered.includes('playlist') ? 'Playlist support' : '',
    lowered.includes('forecast') ? 'Forecast views' : '',
    lowered.includes('bookmark') ? 'Bookmarks' : '',
    lowered.includes('calendar') ? 'Calendar support' : '',
    lowered.includes('login') ? 'Account access' : '',
  ])

  return {
    domain,
    url: `${parsed.protocol}//${parsed.host}`,
    title,
    summary,
    category: inferCategory(text),
    access: inferAccess(text),
    iconUrl,
    highlights: highlights.length ? highlights : ['Core functionality'],
    stack: ['Web app'],
    source,
  }
}

async function scanMetadataFromUrl(input: string): Promise<ScanResult> {
  const normalized = normalizeUrl(input)
  const parsed = new URL(normalized)
  const domain = parsed.hostname

  const attempts = [
    { url: normalized, source: 'direct site response' },
    { url: `https://r.jina.ai/http://${domain}`, source: 'r.jina.ai http proxy' },
    { url: `https://r.jina.ai/https://${domain}`, source: 'r.jina.ai https proxy' },
  ]

  for (const attempt of attempts) {
    try {
      const response = await fetch(attempt.url)
      if (!response.ok) {
        continue
      }

      const text = await response.text()
      if (text.trim().length < 40) {
        continue
      }

      return extractScanResult(text, normalized, attempt.source)
    } catch {
      // Try the next source.
    }
  }

  throw new Error('Unable to scan this URL automatically.')
}

function HomePage({
  projectList,
  siteContent,
}: {
  projectList: Project[]
  siteContent: SiteContent
}) {
  const [activeFilter, setActiveFilter] = useState<'all' | 'built' | 'hosted'>(
    'all',
  )

  const filteredProjects = useMemo(() => {
    if (activeFilter === 'all') {
      return projectList
    }

    return projectList.filter((project) => project.ownership === activeFilter)
  }, [activeFilter, projectList])

  return (
    <main className="shell">
      <section className="hero">
        <p className="eyebrow">{siteContent.homeEyebrow}</p>
        <h1>{siteContent.homeTitle}</h1>
        <p className="intro">{siteContent.homeIntro}</p>

        <div className="filters" aria-label="Project filters">
          <button
            type="button"
            className={activeFilter === 'all' ? 'active' : ''}
            onClick={() => setActiveFilter('all')}
          >
            All
          </button>
          <button
            type="button"
            className={activeFilter === 'built' ? 'active' : ''}
            onClick={() => setActiveFilter('built')}
          >
            Built by me
          </button>
          <button
            type="button"
            className={activeFilter === 'hosted' ? 'active' : ''}
            onClick={() => setActiveFilter('hosted')}
          >
            Hosted by me
          </button>
          <Link to="/admin">Admin</Link>
        </div>
      </section>

      <section className="stats" aria-label="Project summary">
        <article>
          <p>Total</p>
          <strong>{projectList.length}</strong>
        </article>
        <article>
          <p>Built</p>
          <strong>{projectList.filter((project) => project.ownership === 'built').length}</strong>
        </article>
        <article>
          <p>Hosted</p>
          <strong>{projectList.filter((project) => project.ownership === 'hosted').length}</strong>
        </article>
      </section>

      <section className="grid" aria-label="Project cards">
        {filteredProjects.map((project) => (
          <article className="card" key={project.title}>
            <div className="card-top">
              <span className="pill">{project.domain}</span>
              <span className="status live">{project.status}</span>
            </div>

            <h2 className="title-row">
              {project.iconUrl ? (
                <img className="project-icon" src={project.iconUrl} alt="" />
              ) : (
                <span className="project-icon fallback" aria-hidden="true">
                  {project.title[0]}
                </span>
              )}
              {project.title}
            </h2>
            <p>{project.summary}</p>

            <ul className="stack" aria-label={`${project.title} tags`}>
              <li>{project.ownership === 'built' ? 'Built' : 'Hosted'}</li>
              <li>{project.category}</li>
              <li>{project.access}</li>
              <li>NeCloud</li>
              {project.scanSource === 'manual' ? <li>Manual metadata</li> : null}
            </ul>

            <footer>
              <span>{project.ownership === 'built' ? 'Built by me' : 'Hosted by me'}</span>
              <div className="actions">
                <Link to={`/projects/${project.slug}`}>About</Link>
                <a href={project.url} target="_blank" rel="noreferrer">
                  Open Site
                </a>
              </div>
            </footer>
          </article>
        ))}
      </section>

      <section className="notes">
        <h3>{siteContent.quickTipsTitle}</h3>
        <p>{siteContent.quickTipsBody}</p>
      </section>
    </main>
  )
}

function ProjectPage({
  projectList,
  siteContent,
}: {
  projectList: Project[]
  siteContent: SiteContent
}) {
  const { slug } = useParams()
  const project = slug ? projectList.find((item) => item.slug === slug) : undefined

  if (!project) {
    return (
      <main className="shell">
        <section className="hero">
          <p className="eyebrow">Project</p>
          <h1>Project not found</h1>
          <p className="intro">The requested project page is not available.</p>
          <div className="filters">
            <Link to="/">Back to portfolio</Link>
          </div>
        </section>
      </main>
    )
  }

  return (
    <main className="shell">
      <section className="hero">
        <p className="eyebrow">{siteContent.projectEyebrow}</p>
        <h1>{project.title}</h1>
        <p className="intro">{project.featured.headline}</p>
        <div className="filters">
          <Link to="/">Back to portfolio</Link>
          <a href={project.url} target="_blank" rel="noreferrer">
            Visit live site
          </a>
        </div>
      </section>

      <section className="stats" aria-label="Project facts">
        <article>
          <p>Domain</p>
          <strong>{project.domain}</strong>
        </article>
        <article>
          <p>Ownership</p>
          <strong>{project.ownership === 'built' ? 'Built by me' : 'Hosted by me'}</strong>
        </article>
        <article>
          <p>Category</p>
          <strong>{project.category}</strong>
        </article>
        <article>
          <p>Access</p>
          <strong>{project.access}</strong>
        </article>
        <article>
          <p>Source</p>
          <strong>
            {project.scanSource === 'scanned' ? 'Scanned metadata' : 'Manual fallback'}
          </strong>
        </article>
      </section>

      <section className="detail-grid" aria-label="Project overview">
        <article className="detail-card">
          <h3>Problem</h3>
          <p>{project.featured.problem}</p>
        </article>
        <article className="detail-card">
          <h3>Solution</h3>
          <p>{project.featured.solution}</p>
        </article>
      </section>

      <section className="notes">
        <h3>Feature highlights</h3>
        <ul className="stack" aria-label={`${project.title} feature highlights`}>
          {project.featured.highlights.map((feature) => (
            <li key={`${project.slug}-${feature}`}>{feature}</li>
          ))}
        </ul>
      </section>

      <section className="notes">
        <h3>Stack</h3>
        <ul className="stack" aria-label={`${project.title} stack`}>
          {project.featured.stack.map((item) => (
            <li key={`${project.slug}-stack-${item}`}>{item}</li>
          ))}
        </ul>
      </section>

      <section className="notes">
        <h3>Next steps</h3>
        <ul className="stack" aria-label={`${project.title} next steps`}>
          {project.featured.nextSteps.map((item) => (
            <li key={`${project.slug}-next-${item}`}>{item}</li>
          ))}
        </ul>
        <p className="update-tip">{project.featured.updateNote}</p>
      </section>
    </main>
  )
}

function AdminPage({
  projectList,
  onSave,
  onResetProject,
  onResetAll,
  onDeleteProject,
  onCreateProject,
  theme,
  onThemeChange,
  siteContent,
  onSaveSiteContent,
  onResetSiteContent,
}: {
  projectList: Project[]
  onSave: (sourceSlug: string, project: Project) => void
  onResetProject: (slug: string) => void
  onResetAll: () => void
  onDeleteProject: (slug: string) => void
  onCreateProject: () => Project
  theme: ThemeName
  onThemeChange: (theme: ThemeName) => void
  siteContent: SiteContent
  onSaveSiteContent: (siteContent: SiteContent) => void
  onResetSiteContent: () => void
}) {
  const [selectedSlug, setSelectedSlug] = useState(projectList[0]?.slug ?? '')
  const [draft, setDraft] = useState<Project | null>(projectList[0] ?? null)
  const [scanState, setScanState] = useState<'idle' | 'loading' | 'error' | 'success'>(
    'idle',
  )
  const [scanMessage, setScanMessage] = useState('')
  const [siteDraft, setSiteDraft] = useState<SiteContent>(siteContent)

  const selectedProject = useMemo(
    () => projectList.find((project) => project.slug === selectedSlug),
    [projectList, selectedSlug],
  )

  useEffect(() => {
    if (!selectedProject && projectList[0]) {
      setSelectedSlug(projectList[0].slug)
      setDraft(projectList[0])
      return
    }

    if (selectedProject) {
      setDraft(structuredClone(selectedProject))
      setScanState('idle')
      setScanMessage('')
    }
  }, [selectedProject, projectList])

  useEffect(() => {
    setSiteDraft(siteContent)
  }, [siteContent])

  const handleScanFromUrl = async () => {
    if (!draft) {
      return
    }

    setScanState('loading')
    setScanMessage('Scanning URL metadata...')

    try {
      const result = await scanMetadataFromUrl(draft.url || draft.domain)
      const suggestedSlug = createUniqueSlug(
        slugify(result.domain.split('.')[0] || result.title),
        projectList.filter((item) => item.slug !== selectedSlug),
      )

      setDraft({
        ...draft,
        slug: suggestedSlug,
        title: result.title,
        domain: result.domain,
        url: result.url,
        category: result.category,
        access: result.access,
        iconUrl: result.iconUrl,
        scanSource: 'scanned',
        summary: result.summary,
        details: result.summary,
        features: result.highlights,
        featured: {
          ...draft.featured,
          headline: result.summary,
          highlights: result.highlights,
          stack: result.stack,
        },
      })
      setScanState('success')
      setScanMessage(`Scan complete using ${result.source}. Review fields, then save.`)
    } catch {
      setScanState('error')
      setScanMessage('Could not scan this URL automatically. Fill fields manually and save.')
    }
  }

  if (!draft) {
    return (
      <main className="shell">
        <section className="hero">
          <p className="eyebrow">Admin</p>
          <h1>No projects available</h1>
          <div className="filters">
            <Link to="/">Back to portfolio</Link>
          </div>
        </section>
      </main>
    )
  }

  return (
    <main className="shell">
      <section className="hero">
        <p className="eyebrow">Admin</p>
        <h1>Edit Cards and About Pages</h1>
        <p className="intro">
          Select a project, edit content, then click Save project. Edits persist in your
          browser and immediately update both cards and featured pages.
        </p>
        <div className="filters">
          <Link to="/">Back to portfolio</Link>
          <button
            type="button"
            onClick={() => {
              const created = onCreateProject()
              setSelectedSlug(created.slug)
            }}
          >
            Create new entry
          </button>
          <button type="button" onClick={onResetAll}>
            Reset all
          </button>
        </div>
      </section>

      <section className="notes admin-panel">
        <h3>Main Site Header</h3>
        <div className="admin-grid">
          <label className="admin-label" htmlFor="homeEyebrow">
            Home eyebrow
          </label>
          <input
            id="homeEyebrow"
            className="admin-input"
            value={siteDraft.homeEyebrow}
            onChange={(event) =>
              setSiteDraft({ ...siteDraft, homeEyebrow: event.target.value })
            }
          />

          <label className="admin-label" htmlFor="homeTitle">
            Home title
          </label>
          <input
            id="homeTitle"
            className="admin-input"
            value={siteDraft.homeTitle}
            onChange={(event) => setSiteDraft({ ...siteDraft, homeTitle: event.target.value })}
          />

          <label className="admin-label" htmlFor="homeIntro">
            Home intro
          </label>
          <textarea
            id="homeIntro"
            className="admin-input admin-textarea"
            value={siteDraft.homeIntro}
            onChange={(event) => setSiteDraft({ ...siteDraft, homeIntro: event.target.value })}
          />

          <label className="admin-label" htmlFor="projectEyebrow">
            Project page eyebrow
          </label>
          <input
            id="projectEyebrow"
            className="admin-input"
            value={siteDraft.projectEyebrow}
            onChange={(event) =>
              setSiteDraft({ ...siteDraft, projectEyebrow: event.target.value })
            }
          />

          <label className="admin-label" htmlFor="quickTipsTitle">
            Quick tips title
          </label>
          <input
            id="quickTipsTitle"
            className="admin-input"
            value={siteDraft.quickTipsTitle}
            onChange={(event) =>
              setSiteDraft({ ...siteDraft, quickTipsTitle: event.target.value })
            }
          />

          <label className="admin-label" htmlFor="quickTipsBody">
            Quick tips body
          </label>
          <textarea
            id="quickTipsBody"
            className="admin-input admin-textarea"
            value={siteDraft.quickTipsBody}
            onChange={(event) =>
              setSiteDraft({ ...siteDraft, quickTipsBody: event.target.value })
            }
          />
        </div>

        <div className="actions admin-actions">
          <button type="button" onClick={() => onSaveSiteContent(siteDraft)}>
            Save headers and tips
          </button>
          <button type="button" onClick={onResetSiteContent}>
            Reset headers
          </button>
        </div>
      </section>

      <section className="notes admin-panel">
        <h3>Project Header and Content</h3>
        <label className="admin-label" htmlFor="project-select">
          Project
        </label>
        <select
          id="project-select"
          className="admin-input"
          value={selectedSlug}
          onChange={(event) => setSelectedSlug(event.target.value)}
        >
          {projectList.map((project) => (
            <option key={project.slug} value={project.slug}>
              {project.title}
            </option>
          ))}
        </select>

        <label className="admin-label" htmlFor="theme-select">
          Theme
        </label>
        <select
          id="theme-select"
          className="admin-input"
          value={theme}
          onChange={(event) => onThemeChange(event.target.value as ThemeName)}
        >
          <option value="sand">Sandstone</option>
          <option value="ocean">Ocean</option>
          <option value="midnight">Midnight</option>
        </select>

        <div className="admin-grid">
          <label className="admin-label" htmlFor="slug">
            Slug
          </label>
          <input
            id="slug"
            className="admin-input"
            value={draft.slug}
            onChange={(event) =>
              setDraft({ ...draft, slug: slugify(event.target.value) || draft.slug })
            }
          />

          <label className="admin-label" htmlFor="title">
            Project page title
          </label>
          <input
            id="title"
            className="admin-input"
            value={draft.title}
            onChange={(event) => setDraft({ ...draft, title: event.target.value })}
          />

          <label className="admin-label" htmlFor="summary">
            Card summary
          </label>
          <textarea
            id="summary"
            className="admin-input admin-textarea"
            value={draft.summary}
            onChange={(event) => setDraft({ ...draft, summary: event.target.value })}
          />

          <label className="admin-label" htmlFor="category">
            Category
          </label>
          <input
            id="category"
            className="admin-input"
            value={draft.category}
            onChange={(event) => setDraft({ ...draft, category: event.target.value })}
          />

          <label className="admin-label" htmlFor="access">
            Access
          </label>
          <select
            id="access"
            className="admin-input"
            value={draft.access}
            onChange={(event) =>
              setDraft({ ...draft, access: event.target.value as Project['access'] })
            }
          >
            <option>Public</option>
            <option>Login required</option>
            <option>Mixed</option>
          </select>

          <label className="admin-label" htmlFor="ownership">
            Ownership
          </label>
          <select
            id="ownership"
            className="admin-input"
            value={draft.ownership}
            onChange={(event) =>
              setDraft({ ...draft, ownership: event.target.value as Project['ownership'] })
            }
          >
            <option value="built">Built</option>
            <option value="hosted">Hosted</option>
          </select>

          <label className="admin-label" htmlFor="domain">
            Domain
          </label>
          <input
            id="domain"
            className="admin-input"
            value={draft.domain}
            onChange={(event) => setDraft({ ...draft, domain: event.target.value })}
          />

          <label className="admin-label" htmlFor="url">
            URL
          </label>
          <input
            id="url"
            className="admin-input"
            value={draft.url}
            onChange={(event) => setDraft({ ...draft, url: event.target.value })}
          />

          <div className="scan-row">
            <button
              type="button"
              className="scan-button"
              onClick={handleScanFromUrl}
              disabled={scanState === 'loading'}
            >
              {scanState === 'loading' ? 'Scanning...' : 'Scan URL metadata'}
            </button>
            {scanMessage ? (
              <p className={`scan-status ${scanState}`}>{scanMessage}</p>
            ) : null}
          </div>

          <label className="admin-label" htmlFor="iconUrl">
            Icon URL
          </label>
          <input
            id="iconUrl"
            className="admin-input"
            value={draft.iconUrl ?? ''}
            onChange={(event) => setDraft({ ...draft, iconUrl: event.target.value || undefined })}
          />

          <label className="admin-label" htmlFor="headline">
            Project page subtitle
          </label>
          <textarea
            id="headline"
            className="admin-input admin-textarea"
            value={draft.featured.headline}
            onChange={(event) =>
              setDraft({
                ...draft,
                featured: { ...draft.featured, headline: event.target.value },
              })
            }
          />

          <label className="admin-label" htmlFor="problem">
            Problem
          </label>
          <textarea
            id="problem"
            className="admin-input admin-textarea"
            value={draft.featured.problem}
            onChange={(event) =>
              setDraft({
                ...draft,
                featured: { ...draft.featured, problem: event.target.value },
              })
            }
          />

          <label className="admin-label" htmlFor="solution">
            Solution
          </label>
          <textarea
            id="solution"
            className="admin-input admin-textarea"
            value={draft.featured.solution}
            onChange={(event) =>
              setDraft({
                ...draft,
                featured: { ...draft.featured, solution: event.target.value },
              })
            }
          />

          <label className="admin-label" htmlFor="highlights">
            Highlights (one per line)
          </label>
          <textarea
            id="highlights"
            className="admin-input admin-textarea"
            value={joinLines(draft.featured.highlights)}
            onChange={(event) =>
              setDraft({
                ...draft,
                featured: { ...draft.featured, highlights: splitLines(event.target.value) },
              })
            }
          />

          <label className="admin-label" htmlFor="stack">
            Stack (one per line)
          </label>
          <textarea
            id="stack"
            className="admin-input admin-textarea"
            value={joinLines(draft.featured.stack)}
            onChange={(event) =>
              setDraft({
                ...draft,
                featured: { ...draft.featured, stack: splitLines(event.target.value) },
              })
            }
          />

          <label className="admin-label" htmlFor="nextSteps">
            Next steps (one per line)
          </label>
          <textarea
            id="nextSteps"
            className="admin-input admin-textarea"
            value={joinLines(draft.featured.nextSteps)}
            onChange={(event) =>
              setDraft({
                ...draft,
                featured: { ...draft.featured, nextSteps: splitLines(event.target.value) },
              })
            }
          />
        </div>

        <div className="actions admin-actions">
          <button
            type="button"
            onClick={() => {
              onSave(selectedSlug, draft)
              setSelectedSlug(draft.slug)
            }}
          >
            Save project
          </button>
          <button type="button" onClick={() => onResetProject(selectedSlug)}>
            Reset project
          </button>
          <button
            type="button"
            onClick={() => {
              onDeleteProject(selectedSlug)
            }}
          >
            Remove project
          </button>
        </div>
      </section>
    </main>
  )
}

function App() {
  const [managedProjects, setManagedProjects] = useState<Project[]>(baseProjects)
  const [theme, setTheme] = useState<ThemeName>(readStoredTheme)
  const [siteContent, setSiteContent] = useState<SiteContent>(defaultSiteContent)

  useEffect(() => {
    void (async () => {
      const loaded = await fetchAdminContent()
      if (!loaded) {
        return
      }

      setManagedProjects(loaded.projects)
      setSiteContent(loaded.siteContent)
    })()
  }, [])

  useEffect(() => {
    localStorage.setItem(THEME_STORAGE_KEY, theme)
    document.documentElement.setAttribute('data-theme', theme)
  }, [theme])

  const handleSaveProject = (sourceSlug: string, project: Project) => {
    const safeSlug = createUniqueSlug(
      project.slug,
      managedProjects.filter((item) => item.slug !== sourceSlug),
    )

    const normalizedProject = {
      ...project,
      slug: safeSlug,
      url: project.url.trim(),
      domain: project.domain.trim(),
      title: project.title.trim() || 'Untitled Project',
    }

    const updated = managedProjects.map((item) =>
      item.slug === sourceSlug ? normalizedProject : item,
    )

    setManagedProjects(updated)
    void persistAdminContent(updated, siteContent)
  }

  const handleResetProject = (slug: string) => {
    const original = baseProjects.find((project) => project.slug === slug)
    if (!original) {
      return
    }

    const updated = managedProjects.map((item) =>
      item.slug === slug ? structuredClone(original) : item,
    )

    setManagedProjects(updated)
    void persistAdminContent(updated, siteContent)
  }

  const handleResetAll = () => {
    const updated = structuredClone(baseProjects)
    setManagedProjects(updated)
    void persistAdminContent(updated, siteContent)
  }

  const handleCreateProject = () => {
    const created = createEmptyProject(managedProjects)
    const updated = [...managedProjects, created]
    setManagedProjects(updated)
    void persistAdminContent(updated, siteContent)
    return created
  }

  const handleDeleteProject = (slug: string) => {
    const updated = managedProjects.filter((item) => item.slug !== slug)
    setManagedProjects(updated)
    void persistAdminContent(updated, siteContent)
  }

  const handleResetSiteContent = () => {
    const updatedSiteContent = structuredClone(defaultSiteContent)
    setSiteContent(updatedSiteContent)
    void persistAdminContent(managedProjects, updatedSiteContent)
  }

  const handleSaveSiteContent = (updatedSiteContent: SiteContent) => {
    setSiteContent(updatedSiteContent)
    void persistAdminContent(managedProjects, updatedSiteContent)
  }

  return (
    <Routes>
      <Route
        path="/"
        element={<HomePage projectList={managedProjects} siteContent={siteContent} />}
      />
      <Route
        path="/projects/:slug"
        element={<ProjectPage projectList={managedProjects} siteContent={siteContent} />}
      />
      <Route
        path="/admin"
        element={
          <AdminPage
            projectList={managedProjects}
            onSave={handleSaveProject}
            onResetProject={handleResetProject}
            onResetAll={handleResetAll}
            onDeleteProject={handleDeleteProject}
            onCreateProject={handleCreateProject}
            theme={theme}
            onThemeChange={setTheme}
            siteContent={siteContent}
            onSaveSiteContent={handleSaveSiteContent}
            onResetSiteContent={handleResetSiteContent}
          />
        }
      />
      <Route path="*" element={<Navigate to="/" replace />} />
    </Routes>
  )
}

export default App
