import { useEffect, useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import Modal from 'react-bootstrap/Modal'
import Dropdown from 'react-bootstrap/Dropdown'
import {
  listSites,
  siteBatch,
  createSite,
  getSite,
  updateSite,
  setSiteStatus,
  deleteSite,
  createSiteBackup,
  listSiteBackups,
  restoreSiteBackup,
  downloadSiteBackup,
  listSiteRedirects,
  addSiteRedirect,
  deleteSiteRedirect,
  siteGitClone,
  siteGitPull,
  listServices,
  type SiteListItem,
} from '../api/client'
import { PageHeader, AdminButton, AdminAlert, AdminTable, EmptyState } from '../components/admin'

function formatPhpVersion(code: string): string {
  const m: Record<string, string> = { '74': '7.4', '80': '8.0', '81': '8.1', '82': '8.2' }
  return m[code] || code
}

export function SitePage() {
  const [sites, setSites] = useState<SiteListItem[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState('')
  const [showCreate, setShowCreate] = useState(false)
  const [creating, setCreating] = useState(false)
  const [creatingError, setCreatingError] = useState('')
  const [backupSiteId, setBackupSiteId] = useState<number | null>(null)
  const [backups, setBackups] = useState<{ filename: string; size: number }[]>([])
  const [backupLoading, setBackupLoading] = useState(false)
  const [editSiteId, setEditSiteId] = useState<number | null>(null)
  const [editForm, setEditForm] = useState<{
    domains: string
    path: string
    ps: string
    php_version: string
    force_https: boolean
  } | null>(null)
  const [editLoading, setEditLoading] = useState(false)
  const [editError, setEditError] = useState('')
  const [statusLoading, setStatusLoading] = useState<number | null>(null)
  const [redirectSiteId, setRedirectSiteId] = useState<number | null>(null)
  const [redirects, setRedirects] = useState<{ id: number; source: string; target: string; code: number }[]>([])
  const [redirectSource, setRedirectSource] = useState('')
  const [redirectTarget, setRedirectTarget] = useState('')
  const [redirectCode, setRedirectCode] = useState(301)
  const [redirectAdding, setRedirectAdding] = useState(false)
  const [gitSiteId, setGitSiteId] = useState<number | null>(null)
  const [gitUrl, setGitUrl] = useState('')
  const [gitBranch, setGitBranch] = useState('main')
  const [gitAction, setGitAction] = useState<'clone' | 'pull' | null>(null)
  const [gitLoading, setGitLoading] = useState(false)
  const [search, setSearch] = useState('')
  const [selectedIds, setSelectedIds] = useState<Set<number>>(() => new Set())
  const [page, setPage] = useState(1)
  const [pageSize, setPageSize] = useState(25)
  const [nginxStatus, setNginxStatus] = useState<string | null>(null)
  const [batchLoading, setBatchLoading] = useState(false)
  const [rowBackupId, setRowBackupId] = useState<number | null>(null)

  const loadSites = () => {
    setLoading(true)
    listSites()
      .then(setSites)
      .catch((err) => setError(err.message))
      .finally(() => setLoading(false))
  }

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

  useEffect(() => {
    listServices()
      .then((data) => {
        const n = data.services.find((s) => s.id === 'nginx')
        setNginxStatus(n?.status ?? null)
      })
      .catch(() => setNginxStatus(null))
  }, [])

  const filteredSites = useMemo(() => {
    const q = search.trim().toLowerCase()
    if (!q) return sites
    return sites.filter((s) => {
      const blob = [s.name, s.path, s.ps, s.primary_domain, ...(s.domains || [])].join(' ').toLowerCase()
      return blob.includes(q)
    })
  }, [sites, search])

  useEffect(() => setPage(1), [search, pageSize])

  const totalPages = Math.max(1, Math.ceil(filteredSites.length / pageSize))
  const paginatedSites = useMemo(() => {
    const start = (page - 1) * pageSize
    return filteredSites.slice(start, start + pageSize)
  }, [filteredSites, page, pageSize])

  useEffect(() => {
    if (page > totalPages) setPage(totalPages)
  }, [page, totalPages])

  const toggleSelect = (id: number) => {
    setSelectedIds((prev) => {
      const n = new Set(prev)
      if (n.has(id)) n.delete(id)
      else n.add(id)
      return n
    })
  }

  const selectAllOnPage = () => {
    const onPage = new Set(paginatedSites.map((s) => s.id))
    const allSelected = paginatedSites.length > 0 && paginatedSites.every((s) => selectedIds.has(s.id))
    if (allSelected) {
      setSelectedIds((prev) => {
        const n = new Set(prev)
        paginatedSites.forEach((s) => n.delete(s.id))
        return n
      })
    } else {
      setSelectedIds((prev) => new Set([...prev, ...onPage]))
    }
  }

  const runBatch = async (action: 'enable' | 'disable' | 'delete') => {
    const ids = [...selectedIds]
    if (ids.length === 0) return
    const msg =
      action === 'delete'
        ? `Delete ${ids.length} site(s)? This cannot be undone.`
        : `${action === 'enable' ? 'Enable' : 'Disable'} ${ids.length} site(s)?`
    if (!confirm(msg)) return
    setBatchLoading(true)
    try {
      const r = await siteBatch(action, ids)
      const failed = r.results.filter((x) => !x.ok)
      if (failed.length) setError(failed.map((f) => `${f.id}: ${f.msg}`).join('; '))
      else setError('')
      setSelectedIds(new Set())
      loadSites()
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Batch failed')
    } finally {
      setBatchLoading(false)
    }
  }

  const handleQuickBackup = (siteId: number) => {
    setRowBackupId(siteId)
    createSiteBackup(siteId)
      .then(() => loadSites())
      .catch((err) => setError(err.message))
      .finally(() => setRowBackupId(null))
  }

  const handleCreate = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    const form = e.currentTarget
    const name = (form.elements.namedItem('name') as HTMLInputElement).value.trim()
    const domainsStr = (form.elements.namedItem('domains') as HTMLInputElement).value.trim()
    const path = (form.elements.namedItem('path') as HTMLInputElement).value.trim()
    const ps = (form.elements.namedItem('ps') as HTMLInputElement).value.trim()

    if (!name || !domainsStr) {
      setCreatingError('Name and domain(s) are required')
      return
    }
    const domains = domainsStr.split(/[\s,]+/).filter(Boolean)
    const php_version = (form.elements.namedItem('php_version') as HTMLSelectElement)?.value || '74'
    const force_https = (form.elements.namedItem('force_https') as HTMLInputElement)?.checked || false
    setCreating(true)
    setCreatingError('')
    createSite({ name, domains, path: path || undefined, ps: ps || undefined, php_version, force_https })
      .then(() => {
        setShowCreate(false)
        form.reset()
        loadSites()
      })
      .catch((err) => setCreatingError(err.message))
      .finally(() => setCreating(false))
  }

  const handleDelete = (id: number, name: string) => {
    if (!confirm(`Delete site "${name}"? This cannot be undone.`)) return
    deleteSite(id)
      .then(loadSites)
      .catch((err) => setError(err.message))
  }

  const openBackupModal = (siteId: number) => {
    setBackupSiteId(siteId)
    setBackups([])
    listSiteBackups(siteId)
      .then((data) => setBackups(data.backups))
      .catch((err) => setError(err.message))
  }

  const handleCreateBackup = () => {
    if (!backupSiteId) return
    setBackupLoading(true)
    createSiteBackup(backupSiteId)
      .then(() => listSiteBackups(backupSiteId).then((d) => setBackups(d.backups)))
      .catch((err) => setError(err.message))
      .finally(() => setBackupLoading(false))
  }

  const handleRestore = (filename: string) => {
    if (!backupSiteId || !confirm(`Restore from ${filename}? This will overwrite existing files.`)) return
    setBackupLoading(true)
    restoreSiteBackup(backupSiteId, filename)
      .then(() => setBackupSiteId(null))
      .catch((err) => setError(err.message))
      .finally(() => setBackupLoading(false))
  }

  const handleDownloadBackup = (filename: string) => {
    if (!backupSiteId) return
    downloadSiteBackup(backupSiteId, filename).catch((err) => setError(err.message))
  }

  const openEditModal = (siteId: number) => {
    setEditSiteId(siteId)
    setEditError('')
    getSite(siteId)
      .then((s) =>
        setEditForm({
          domains: (s.domains || []).join(', '),
          path: s.path || '',
          ps: s.ps || '',
          php_version: s.php_version || '74',
          force_https: !!(s.force_https && s.force_https !== 0),
        })
      )
      .catch((err) => setEditError(err.message))
  }

  const handleEdit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    if (!editSiteId || !editForm) return
    const domains = editForm.domains.split(/[\s,]+/).filter(Boolean)
    if (domains.length === 0) {
      setEditError('At least one domain is required')
      return
    }
    setEditLoading(true)
    setEditError('')
    updateSite(editSiteId, {
      domains,
      path: editForm.path || undefined,
      ps: editForm.ps || undefined,
      php_version: editForm.php_version,
      force_https: editForm.force_https,
    })
      .then(() => {
        setEditSiteId(null)
        setEditForm(null)
        loadSites()
      })
      .catch((err) => setEditError(err.message))
      .finally(() => setEditLoading(false))
  }

  const handleSetStatus = (siteId: number, enable: boolean) => {
    setStatusLoading(siteId)
    setSiteStatus(siteId, enable)
      .then(loadSites)
      .catch((err) => setError(err.message))
      .finally(() => setStatusLoading(null))
  }

  const openRedirectModal = (siteId: number) => {
    setRedirectSiteId(siteId)
    setRedirectSource('')
    setRedirectTarget('')
    setRedirectCode(301)
    listSiteRedirects(siteId)
      .then(setRedirects)
      .catch((err) => setError(err.message))
  }

  const handleAddRedirect = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    if (!redirectSiteId || !redirectSource.trim() || !redirectTarget.trim()) return
    setRedirectAdding(true)
    addSiteRedirect(redirectSiteId, redirectSource.trim(), redirectTarget.trim(), redirectCode)
      .then(() => listSiteRedirects(redirectSiteId).then(setRedirects))
      .then(() => {
        setRedirectSource('')
        setRedirectTarget('')
      })
      .catch((err) => setError(err.message))
      .finally(() => setRedirectAdding(false))
  }

  const handleDeleteRedirect = (redirectId: number) => {
    if (!redirectSiteId) return
    deleteSiteRedirect(redirectSiteId, redirectId)
      .then(() => listSiteRedirects(redirectSiteId).then(setRedirects))
      .catch((err) => setError(err.message))
  }

  const handleGitClone = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    if (!gitSiteId || !gitUrl.trim()) return
    setGitLoading(true)
    siteGitClone(gitSiteId, gitUrl.trim(), gitBranch.trim() || 'main')
      .then(() => {
        setGitSiteId(null)
        setGitAction(null)
        loadSites()
      })
      .catch((err) => setError(err.message))
      .finally(() => setGitLoading(false))
  }

  const handleGitPull = () => {
    if (!gitSiteId) return
    setGitLoading(true)
    siteGitPull(gitSiteId)
      .then(() => {
        setGitSiteId(null)
        setGitAction(null)
        loadSites()
      })
      .catch((err) => setError(err.message))
      .finally(() => setGitLoading(false))
  }

  const formatSize = (bytes: number) => {
    if (bytes < 1024) return bytes + ' B'
    if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
    return (bytes / 1024 / 1024).toFixed(1) + ' MB'
  }

  if (loading) {
    return (
      <>
        <PageHeader title="Website" />
        <div className="text-center py-5 text-muted">Loading…</div>
      </>
    )
  }
  if (error && !sites.length) {
    return (
      <>
        <PageHeader title="Website" />
        <AdminAlert variant="danger">{error}</AdminAlert>
      </>
    )
  }

  return (
    <>
      <PageHeader
        title="Website"
        actions={
          <AdminButton onClick={() => setShowCreate(true)}>
            <i className="ti ti-plus me-1" />
            Add Site
          </AdminButton>
        }
      />

      {error ? <AdminAlert variant="warning">{error}</AdminAlert> : null}

      <Modal show={showCreate} onHide={() => setShowCreate(false)} centered>
        <Modal.Header closeButton>
          <Modal.Title>Create Site</Modal.Title>
        </Modal.Header>
        <form onSubmit={handleCreate}>
          <Modal.Body>
            {creatingError ? <AdminAlert variant="danger">{creatingError}</AdminAlert> : null}
            <div className="mb-3">
              <label className="form-label">Site Name</label>
              <input name="name" type="text" placeholder="example.com" className="form-control" required />
            </div>
            <div className="mb-3">
              <label className="form-label">Domain(s) (comma or space separated)</label>
              <input
                name="domains"
                type="text"
                placeholder="example.com www.example.com"
                className="form-control"
                required
              />
            </div>
            <div className="mb-3">
              <label className="form-label">Path (optional)</label>
              <input name="path" type="text" placeholder="/www/wwwroot/example.com" className="form-control" />
            </div>
            <div className="mb-3">
              <label className="form-label">PHP Version</label>
              <select name="php_version" className="form-select">
                <option value="74">7.4</option>
                <option value="80">8.0</option>
                <option value="81">8.1</option>
                <option value="82">8.2</option>
              </select>
            </div>
            <div className="form-check mb-3">
              <input name="force_https" type="checkbox" id="create_force_https" className="form-check-input" />
              <label htmlFor="create_force_https" className="form-check-label">
                Force HTTPS (redirect HTTP to HTTPS)
              </label>
            </div>
            <div className="mb-0">
              <label className="form-label">Note (optional)</label>
              <input name="ps" type="text" placeholder="My website" className="form-control" />
            </div>
          </Modal.Body>
          <Modal.Footer>
            <button type="button" className="btn btn-light" onClick={() => setShowCreate(false)}>
              Cancel
            </button>
            <button type="submit" disabled={creating} className="btn btn-primary">
              {creating ? 'Creating…' : 'Create'}
            </button>
          </Modal.Footer>
        </form>
      </Modal>

      <div className="card shadow-sm border-0 mb-3">
        <div className="card-body py-3">
          <div className="row g-2 align-items-center">
            <div className="col-md-4">
              <div className="input-group input-group-sm">
                <span className="input-group-text">
                  <i className="ti ti-search" aria-hidden />
                </span>
                <input
                  type="search"
                  className="form-control"
                  placeholder="Site name, domain, path, or note…"
                  value={search}
                  onChange={(e) => setSearch(e.target.value)}
                  aria-label="Search sites"
                />
              </div>
            </div>
            <div className="col-md-5">
              <div className="d-flex flex-wrap gap-2 align-items-center">
                <AdminButton variant="secondary" size="sm" onClick={() => runBatch('enable')} disabled={batchLoading || selectedIds.size === 0}>
                  Enable
                </AdminButton>
                <AdminButton variant="secondary" size="sm" onClick={() => runBatch('disable')} disabled={batchLoading || selectedIds.size === 0}>
                  Disable
                </AdminButton>
                <AdminButton variant="danger" size="sm" onClick={() => runBatch('delete')} disabled={batchLoading || selectedIds.size === 0}>
                  Delete
                </AdminButton>
                <span className="text-secondary small ms-md-2">
                  {selectedIds.size > 0 ? `${selectedIds.size} selected` : ''}
                </span>
              </div>
            </div>
            <div className="col-md-3 text-md-end">
              <span
                className={`badge ${nginxStatus === 'active' ? 'bg-success' : 'bg-secondary'} me-2`}
                title="Nginx (systemd)"
              >
                Nginx {nginxStatus ?? '—'}
              </span>
              <select
                className="form-select form-select-sm d-inline-block w-auto"
                value={pageSize}
                onChange={(e) => setPageSize(Number(e.target.value))}
                aria-label="Rows per page"
              >
                <option value={10}>10 / page</option>
                <option value={25}>25 / page</option>
                <option value={50}>50 / page</option>
              </select>
            </div>
          </div>
        </div>
      </div>

      <div className="card shadow-sm border-0">
        <div className="card-body p-0">
          <AdminTable>
            <thead className="table-light">
              <tr>
                <th style={{ width: 40 }}>
                  <input
                    type="checkbox"
                    className="form-check-input"
                    checked={paginatedSites.length > 0 && paginatedSites.every((s) => selectedIds.has(s.id))}
                    onChange={selectAllOnPage}
                    aria-label="Select all on page"
                  />
                </th>
                <th>Site</th>
                <th className="text-center">Status</th>
                <th>Backup</th>
                <th className="text-center">Quick</th>
                <th className="text-center">PHP</th>
                <th>SSL</th>
                <th className="d-none d-xl-table-cell">Note</th>
                <th className="text-end">Operate</th>
              </tr>
            </thead>
            <tbody>
              {sites.length === 0 ? (
                <tr>
                  <td colSpan={9} className="p-0">
                    <EmptyState title="No sites yet" description='Click "Add Site" to create one.' />
                  </td>
                </tr>
              ) : filteredSites.length === 0 ? (
                <tr>
                  <td colSpan={9} className="p-0">
                    <EmptyState title="No matches" description="Try a different search." />
                  </td>
                </tr>
              ) : (
                paginatedSites.map((s) => (
                  <tr key={s.id}>
                    <td className="align-middle" onClick={(e) => e.stopPropagation()}>
                      <input
                        type="checkbox"
                        className="form-check-input"
                        checked={selectedIds.has(s.id)}
                        onChange={() => toggleSelect(s.id)}
                        aria-label={`Select ${s.name}`}
                      />
                    </td>
                    <td className="align-middle">
                      <div className="d-flex align-items-start gap-2">
                        {s.primary_domain ? (
                          <a
                            href={`https://${s.primary_domain.split(':')[0]}`}
                            target="_blank"
                            rel="noreferrer"
                            className="text-decoration-none"
                            title="Open site"
                          >
                            <i className="ti ti-external-link text-muted" aria-hidden />
                          </a>
                        ) : null}
                        <div className="small">
                          <div className="fw-medium">{s.primary_domain || s.name}</div>
                          <div className="text-muted text-truncate" style={{ maxWidth: 220 }} title={s.path}>
                            {s.project_type} · {s.path}
                          </div>
                        </div>
                      </div>
                    </td>
                    <td className="align-middle text-center">
                      {s.status === 1 ? (
                        <button
                          type="button"
                          onClick={() => handleSetStatus(s.id, false)}
                          disabled={statusLoading === s.id}
                          className="btn btn-sm btn-link text-success p-0"
                          title="Running — click to stop"
                        >
                          <i className="ti ti-player-play" />
                        </button>
                      ) : (
                        <button
                          type="button"
                          onClick={() => handleSetStatus(s.id, true)}
                          disabled={statusLoading === s.id}
                          className="btn btn-sm btn-link text-secondary p-0"
                          title="Stopped — click to start"
                        >
                          <i className="ti ti-player-stop" />
                        </button>
                      )}
                    </td>
                    <td className="align-middle small">
                      <span className="me-2">{s.backup_count}</span>
                      <button
                        type="button"
                        className="btn btn-link btn-sm text-danger p-0 text-decoration-none"
                        onClick={() => handleQuickBackup(s.id)}
                        disabled={rowBackupId === s.id}
                      >
                        {rowBackupId === s.id ? '…' : 'Backup now'}
                      </button>
                    </td>
                    <td className="align-middle text-center text-nowrap">
                      <Link
                        to={`/files?path=${encodeURIComponent(s.path)}`}
                        className="btn btn-sm btn-light border"
                        title="Site directory"
                      >
                        <i className="ti ti-folder" />
                      </Link>
                    </td>
                    <td className="align-middle text-center small">
                      <span className="badge bg-light text-dark border">{formatPhpVersion(s.php_version || '74')}</span>
                    </td>
                    <td className="align-middle small">
                      {(s.ssl?.status ?? 'none') === 'none' ? (
                        <span className="text-warning">Not set</span>
                      ) : s.ssl?.status === 'expired' ? (
                        <span className="text-danger">Expired</span>
                      ) : s.ssl?.status === 'expiring' ? (
                        <span className="text-warning">{s.ssl?.days_left ?? 0}d left</span>
                      ) : (
                        <span className="text-success">{s.ssl?.days_left ?? 0} days</span>
                      )}
                    </td>
                    <td className="align-middle small text-muted d-none d-xl-table-cell text-truncate" style={{ maxWidth: 140 }} title={s.ps}>
                      {s.ps || '—'}
                    </td>
                    <td className="align-middle text-end">
                      <Dropdown align="end">
                        <Dropdown.Toggle variant="light" size="sm" className="py-0 border">
                          More
                        </Dropdown.Toggle>
                        <Dropdown.Menu>
                          <Dropdown.Item onClick={() => openEditModal(s.id)}>
                            <i className="ti ti-settings me-2" />
                            Config
                          </Dropdown.Item>
                          <Dropdown.Item as={Link} to="/logs">
                            <i className="ti ti-file-text me-2" />
                            Logs
                          </Dropdown.Item>
                          <Dropdown.Item as={Link} to="/ssl_domain">
                            <i className="ti ti-shield-lock me-2" />
                            SSL / Domains
                          </Dropdown.Item>
                          <Dropdown.Divider />
                          <Dropdown.Item
                            onClick={() => {
                              setGitSiteId(s.id)
                              setGitAction('clone')
                              setGitUrl('')
                              setGitBranch('main')
                            }}
                          >
                            <i className="ti ti-git-branch me-2" />
                            Git
                          </Dropdown.Item>
                          <Dropdown.Item onClick={() => openRedirectModal(s.id)}>
                            <i className="ti ti-arrows-right-left me-2" />
                            Redirects
                          </Dropdown.Item>
                          <Dropdown.Item onClick={() => openBackupModal(s.id)}>
                            <i className="ti ti-archive me-2" />
                            Backups
                          </Dropdown.Item>
                          <Dropdown.Divider />
                          <Dropdown.Item className="text-danger" onClick={() => handleDelete(s.id, s.name)}>
                            <i className="ti ti-trash me-2" />
                            Delete
                          </Dropdown.Item>
                        </Dropdown.Menu>
                      </Dropdown>
                    </td>
                  </tr>
                ))
              )}
            </tbody>
          </AdminTable>
        </div>
        {sites.length > 0 && filteredSites.length > 0 ? (
          <div className="card-footer py-2 d-flex flex-wrap align-items-center justify-content-between gap-2 small text-secondary">
            <span>
              Total <strong>{filteredSites.length}</strong> site(s)
              {search.trim() ? ` (filtered from ${sites.length})` : ''}
            </span>
            <div className="d-flex align-items-center gap-2">
              <button
                type="button"
                className="btn btn-sm btn-outline-secondary"
                disabled={page <= 1}
                onClick={() => setPage((p) => Math.max(1, p - 1))}
              >
                Prev
              </button>
              <span>
                Page {page} / {totalPages}
              </span>
              <button
                type="button"
                className="btn btn-sm btn-outline-secondary"
                disabled={page >= totalPages}
                onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
              >
                Next
              </button>
            </div>
          </div>
        ) : null}
      </div>

      <Modal show={!!gitSiteId && !!gitAction} onHide={() => { setGitSiteId(null); setGitAction(null) }} centered>
        <Modal.Header closeButton>
          <Modal.Title>Git Deploy</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <div className="btn-group mb-3" role="group">
            <button
              type="button"
              className={`btn btn-sm ${gitAction === 'clone' ? 'btn-success' : 'btn-outline-secondary'}`}
              onClick={() => setGitAction('clone')}
            >
              Clone
            </button>
            <button
              type="button"
              className={`btn btn-sm ${gitAction === 'pull' ? 'btn-success' : 'btn-outline-secondary'}`}
              onClick={() => setGitAction('pull')}
            >
              Pull
            </button>
          </div>
          {gitAction === 'clone' ? (
            <form onSubmit={handleGitClone}>
              <div className="mb-3">
                <label className="form-label">Repository URL</label>
                <input
                  value={gitUrl}
                  onChange={(e) => setGitUrl(e.target.value)}
                  placeholder="https://github.com/user/repo.git"
                  className="form-control"
                  required
                />
              </div>
              <div className="mb-3">
                <label className="form-label">Branch</label>
                <input
                  value={gitBranch}
                  onChange={(e) => setGitBranch(e.target.value)}
                  placeholder="main"
                  className="form-control"
                />
              </div>
              <p className="small text-muted">Site path must be empty for clone.</p>
              <div className="d-flex justify-content-end gap-2">
                <button
                  type="button"
                  className="btn btn-light"
                  onClick={() => {
                    setGitSiteId(null)
                    setGitAction(null)
                  }}
                >
                  Cancel
                </button>
                <button type="submit" disabled={gitLoading} className="btn btn-success">
                  {gitLoading ? 'Cloning…' : 'Clone'}
                </button>
              </div>
            </form>
          ) : (
            <div>
              <p className="text-muted small">Pull latest changes from the remote repository.</p>
              <div className="d-flex justify-content-end gap-2">
                <button
                  type="button"
                  className="btn btn-light"
                  onClick={() => {
                    setGitSiteId(null)
                    setGitAction(null)
                  }}
                >
                  Cancel
                </button>
                <button type="button" onClick={handleGitPull} disabled={gitLoading} className="btn btn-success">
                  {gitLoading ? 'Pulling…' : 'Pull'}
                </button>
              </div>
            </div>
          )}
        </Modal.Body>
      </Modal>

      <Modal show={redirectSiteId != null} onHide={() => setRedirectSiteId(null)} size="lg" centered scrollable>
        <Modal.Header closeButton>
          <Modal.Title>Redirects</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <form onSubmit={handleAddRedirect} className="row g-2 align-items-end mb-3">
            <div className="col-md-3">
              <label className="form-label small">Source</label>
              <input
                value={redirectSource}
                onChange={(e) => setRedirectSource(e.target.value)}
                placeholder="/old-path"
                className="form-control form-control-sm"
              />
            </div>
            <div className="col-md-3">
              <label className="form-label small">Target</label>
              <input
                value={redirectTarget}
                onChange={(e) => setRedirectTarget(e.target.value)}
                placeholder="/new-path"
                className="form-control form-control-sm"
              />
            </div>
            <div className="col-md-2">
              <label className="form-label small">Code</label>
              <select
                value={redirectCode}
                onChange={(e) => setRedirectCode(Number(e.target.value))}
                className="form-select form-select-sm"
              >
                <option value={301}>301</option>
                <option value={302}>302</option>
              </select>
            </div>
            <div className="col-md-2">
              <button type="submit" disabled={redirectAdding} className="btn btn-primary btn-sm w-100">
                Add
              </button>
            </div>
          </form>
          {redirects.length === 0 ? (
            <p className="text-muted small mb-0">No redirects</p>
          ) : (
            <ul className="list-group list-group-flush">
              {redirects.map((r) => (
                <li key={r.id} className="list-group-item d-flex align-items-center gap-2 small">
                  <code className="text-truncate">{r.source}</code>
                  <span className="text-muted">→</span>
                  <code className="text-truncate flex-grow-1">{r.target}</code>
                  <span className="badge bg-secondary">{r.code}</span>
                  <button type="button" className="btn btn-sm btn-link text-danger p-0" onClick={() => handleDeleteRedirect(r.id)}>
                    <i className="ti ti-trash" />
                  </button>
                </li>
              ))}
            </ul>
          )}
        </Modal.Body>
        <Modal.Footer>
          <button type="button" className="btn btn-light" onClick={() => setRedirectSiteId(null)}>
            Close
          </button>
        </Modal.Footer>
      </Modal>

      <Modal show={editSiteId != null && editForm != null} onHide={() => { setEditSiteId(null); setEditForm(null) }} centered>
        <Modal.Header closeButton>
          <Modal.Title>Edit Site</Modal.Title>
        </Modal.Header>
        {editForm ? (
          <form onSubmit={handleEdit}>
            <Modal.Body>
              {editError ? <AdminAlert variant="danger">{editError}</AdminAlert> : null}
              <div className="mb-3">
                <label className="form-label">Domain(s)</label>
                <input
                  value={editForm.domains}
                  onChange={(e) => setEditForm({ ...editForm, domains: e.target.value })}
                  type="text"
                  className="form-control"
                  required
                />
              </div>
              <div className="mb-3">
                <label className="form-label">Path (optional)</label>
                <input
                  value={editForm.path}
                  onChange={(e) => setEditForm({ ...editForm, path: e.target.value })}
                  type="text"
                  className="form-control"
                />
              </div>
              <div className="mb-3">
                <label className="form-label">PHP Version</label>
                <select
                  value={editForm.php_version}
                  onChange={(e) => setEditForm({ ...editForm, php_version: e.target.value })}
                  className="form-select"
                >
                  <option value="74">7.4</option>
                  <option value="80">8.0</option>
                  <option value="81">8.1</option>
                  <option value="82">8.2</option>
                </select>
              </div>
              <div className="form-check mb-3">
                <input
                  type="checkbox"
                  id="edit_force_https"
                  className="form-check-input"
                  checked={editForm.force_https}
                  onChange={(e) => setEditForm({ ...editForm, force_https: e.target.checked })}
                />
                <label htmlFor="edit_force_https" className="form-check-label">
                  Force HTTPS
                </label>
              </div>
              <div className="mb-0">
                <label className="form-label">Note (optional)</label>
                <input
                  value={editForm.ps}
                  onChange={(e) => setEditForm({ ...editForm, ps: e.target.value })}
                  type="text"
                  className="form-control"
                />
              </div>
            </Modal.Body>
            <Modal.Footer>
              <button type="button" className="btn btn-light" onClick={() => { setEditSiteId(null); setEditForm(null) }}>
                Cancel
              </button>
              <button type="submit" disabled={editLoading} className="btn btn-primary">
                {editLoading ? 'Saving…' : 'Save'}
              </button>
            </Modal.Footer>
          </form>
        ) : null}
      </Modal>

      <Modal show={backupSiteId != null} onHide={() => setBackupSiteId(null)} size="lg" centered>
        <Modal.Header closeButton>
          <Modal.Title>Site Backup</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <div className="mb-3">
            <AdminButton onClick={handleCreateBackup} disabled={backupLoading}>
              <i className="ti ti-archive me-1" />
              {backupLoading ? 'Creating…' : 'Create Backup'}
            </AdminButton>
          </div>
          <h6 className="text-muted small">Existing backups</h6>
          {backups.length === 0 ? (
            <p className="text-muted small">No backups yet</p>
          ) : (
            <ul className="list-group list-group-flush">
              {backups.map((b) => (
                <li key={b.filename} className="list-group-item d-flex align-items-center justify-content-between gap-2">
                  <code className="small text-truncate flex-grow-1">{b.filename}</code>
                  <span className="text-muted small">{formatSize(b.size)}</span>
                  <button
                    type="button"
                    className="btn btn-sm btn-link"
                    onClick={() => handleDownloadBackup(b.filename)}
                    title="Download"
                  >
                    <i className="ti ti-download" />
                  </button>
                  <button
                    type="button"
                    className="btn btn-sm btn-link text-warning"
                    onClick={() => handleRestore(b.filename)}
                    disabled={backupLoading}
                  >
                    <i className="ti ti-restore" />
                  </button>
                </li>
              ))}
            </ul>
          )}
        </Modal.Body>
        <Modal.Footer>
          <button type="button" className="btn btn-light" onClick={() => setBackupSiteId(null)}>
            Close
          </button>
        </Modal.Footer>
      </Modal>
    </>
  )
}
