import Link from 'next/link'
import { createClient } from '@/lib/supabase/server'
import { getAppOrg } from '@/lib/org'
import { getEffectiveUser } from '@/lib/effective-user'
import { canManageImports } from '@/lib/permissions'
import { SearchableCombobox } from '@/components/searchable-combobox'
import {
  createProject,
  deleteClient,
  deleteOperator,
  deleteProject,
  renameOperator,
  renameClient,
  renameProject,
} from './actions'

type Operator = { id: string; name: string }
type Client = {
  id: string
  name: string
  operator_id: string
  operator: { name: string } | null
}
type ProjectRow = {
  id: string
  name: string
  client_id: string
  income_usd: number | string | null
  client: { name: string; operator: { name: string } | null } | null
}

export default async function ManageEntitiesPage({
  searchParams,
}: {
  searchParams: Promise<{
    error?: string
    info?: string
    edit?: string
    /** "pr:<uuid>" — show inline delete form for that project. */
    delete?: string
    /** "pr" — show inline "+ New project" form at the top of the section. */
    new?: string
  }>
}) {
  const {
    error,
    info,
    edit,
    delete: deleteParam,
    new: newParam,
  } = await searchParams

  const supabase = await createClient()
  const org = await getAppOrg(supabase)
  if (!org) {
    return (
      <main>
        <div className="page-head">
          <h1>Manage entities</h1>
        </div>
        <p className="error">Bowden Works organization not found.</p>
      </main>
    )
  }

  const eu = await getEffectiveUser(supabase, org.id)
  const canEdit = canManageImports(eu)

  // Fetch operators with their (eventual) child counts. The counts are
  // surfaced after the entity tables so the user can see "this rename
  // will touch N entries" before clicking.
  const { data: operators } = await supabase
    .from('operators')
    .select('id, name')
    .eq('org_id', org.id)
    .order('name')
    .returns<Operator[]>()

  const { data: clients } = await supabase
    .from('clients')
    .select('id, name, operator_id, operator:operators(name)')
    .eq('org_id', org.id)
    .order('name')
    .returns<Client[]>()

  const { data: projects } = await supabase
    .from('projects')
    .select(
      'id, name, client_id, income_usd, client:clients(name, operator:operators(name))',
    )
    .eq('org_id', org.id)
    .order('name')
    .returns<ProjectRow[]>()

  // Per-entity entry counts via project_id chain. Doing this in TS is
  // simpler than a custom RPC, and the result set is small (~100 ids).
  // We fetch all time_entries (id, project_id) once and count in JS.
  const { data: entriesRows } = await supabase
    .from('time_entries')
    .select('id, project_id')
    .eq('org_id', org.id)
    .returns<{ id: string; project_id: string | null }[]>()
  const entriesByProject = new Map<string, number>()
  for (const r of entriesRows ?? []) {
    if (!r.project_id) continue
    entriesByProject.set(
      r.project_id,
      (entriesByProject.get(r.project_id) ?? 0) + 1,
    )
  }

  // Project counts per client.
  const projectsByClient = new Map<string, number>()
  for (const p of projects ?? []) {
    projectsByClient.set(
      p.client_id,
      (projectsByClient.get(p.client_id) ?? 0) + 1,
    )
  }
  // Entry counts per client (sum across its projects).
  const entriesByClient = new Map<string, number>()
  for (const p of projects ?? []) {
    const n = entriesByProject.get(p.id) ?? 0
    entriesByClient.set(
      p.client_id,
      (entriesByClient.get(p.client_id) ?? 0) + n,
    )
  }
  // Client counts per operator.
  const clientsByOperator = new Map<string, number>()
  for (const c of clients ?? []) {
    clientsByOperator.set(
      c.operator_id,
      (clientsByOperator.get(c.operator_id) ?? 0) + 1,
    )
  }
  // Entry counts per operator.
  const entriesByOperator = new Map<string, number>()
  for (const c of clients ?? []) {
    const n = entriesByClient.get(c.id) ?? 0
    entriesByOperator.set(
      c.operator_id,
      (entriesByOperator.get(c.operator_id) ?? 0) + n,
    )
  }

  // Each "Edit" key encodes scope + id: "op:<uuid>" / "cl:<uuid>" / "pr:<uuid>".
  const editScope = edit?.startsWith('op:')
    ? ('op' as const)
    : edit?.startsWith('cl:')
      ? ('cl' as const)
      : edit?.startsWith('pr:')
        ? ('pr' as const)
        : null
  const editId = edit?.split(':', 2)[1] ?? null

  // Delete state. Key shape matches edit: "op:<uuid>" / "cl:<uuid>" /
  // "pr:<uuid>".
  const deleteScope = deleteParam?.startsWith('op:')
    ? ('op' as const)
    : deleteParam?.startsWith('cl:')
      ? ('cl' as const)
      : deleteParam?.startsWith('pr:')
        ? ('pr' as const)
        : null
  const deleteTargetId = deleteParam?.split(':', 2)[1] ?? null
  const showNewProject = newParam === 'pr'

  // Combobox options for the inline forms. Built here so all delete
  // / create forms reuse them.
  const clientPickerOptions = (clients ?? [])
    .filter((c) => c.operator?.name)
    .map((c) => ({
      id: c.id,
      label: `${c.operator!.name} : ${c.name}`,
    }))
    .sort((a, b) => a.label.localeCompare(b.label))
  const projectPickerOptions = (projects ?? [])
    .filter((p) => p.client?.name)
    .map((p) => ({
      id: p.id,
      label: `${p.client!.name} : ${p.name}`,
    }))
    .sort((a, b) => a.label.localeCompare(b.label))
  const operatorPickerOptions = (operators ?? [])
    .map((o) => ({ id: o.id, label: o.name }))
    .sort((a, b) => a.label.localeCompare(b.label))

  return (
    <main>
      <div className="page-head">
        <div>
          <h1>Manage entities</h1>
          <p className="subtitle">
            Rename operators, clients, and projects. Renames update the
            entity row AND every existing entry that references it —
            one click fixes typos everywhere.{' '}
            <Link href="/projects" className="muted">
              ← Back to project summary
            </Link>
          </p>
        </div>
      </div>

      {error && <p className="error">{error}</p>}
      {info && <p className="info">{info}</p>}

      <Section title="Operators" emptyMessage="No operators yet.">
        <table className="data">
          <thead>
            <tr>
              <th>Name</th>
              <th className="num">Clients</th>
              <th className="num">Entries</th>
              <th />
            </tr>
          </thead>
          <tbody>
            {(operators ?? []).map((o) =>
              editScope === 'op' && editId === o.id ? (
                <EditRow
                  key={o.id}
                  id={o.id}
                  scope="op"
                  currentName={o.name}
                  context="this operator"
                  affectedEntries={entriesByOperator.get(o.id) ?? 0}
                  action={renameOperator}
                />
              ) : deleteScope === 'op' && deleteTargetId === o.id ? (
                <DeleteOperatorRow
                  key={o.id}
                  id={o.id}
                  label={o.name}
                  clientCount={clientsByOperator.get(o.id) ?? 0}
                  affectedEntries={entriesByOperator.get(o.id) ?? 0}
                  operatorOptions={operatorPickerOptions.filter(
                    (op) => op.id !== o.id,
                  )}
                />
              ) : (
                <tr key={o.id}>
                  <td>{o.name}</td>
                  <td className="num">{clientsByOperator.get(o.id) ?? 0}</td>
                  <td className="num">{entriesByOperator.get(o.id) ?? 0}</td>
                  <td className="right">
                    {canEdit && (
                      <div
                        className="flex-row"
                        style={{ justifyContent: 'flex-end', gap: '0.75rem' }}
                      >
                        <Link
                          href={`/projects/manage?edit=op:${o.id}`}
                          scroll={false}
                        >
                          Rename
                        </Link>
                        <Link
                          href={`/projects/manage?delete=op:${o.id}`}
                          scroll={false}
                          style={{ color: '#b91c1c' }}
                        >
                          Delete
                        </Link>
                      </div>
                    )}
                  </td>
                </tr>
              ),
            )}
          </tbody>
        </table>
      </Section>

      <Section title="Clients" emptyMessage="No clients yet.">
        <table className="data">
          <thead>
            <tr>
              <th>Operator</th>
              <th>Client</th>
              <th className="num">Projects</th>
              <th className="num">Entries</th>
              <th />
            </tr>
          </thead>
          <tbody>
            {(clients ?? []).map((c) =>
              editScope === 'cl' && editId === c.id ? (
                <EditRow
                  key={c.id}
                  id={c.id}
                  scope="cl"
                  currentName={c.name}
                  context={`this client (under ${c.operator?.name ?? '—'})`}
                  affectedEntries={entriesByClient.get(c.id) ?? 0}
                  action={renameClient}
                  prefixCols={1}
                />
              ) : deleteScope === 'cl' && deleteTargetId === c.id ? (
                <DeleteClientRow
                  key={c.id}
                  id={c.id}
                  label={`${c.operator?.name ?? '—'} : ${c.name}`}
                  projectCount={projectsByClient.get(c.id) ?? 0}
                  affectedEntries={entriesByClient.get(c.id) ?? 0}
                  clientOptions={clientPickerOptions.filter(
                    (o) => o.id !== c.id,
                  )}
                />
              ) : (
                <tr key={c.id}>
                  <td className="muted">{c.operator?.name ?? '—'}</td>
                  <td>{c.name}</td>
                  <td className="num">{projectsByClient.get(c.id) ?? 0}</td>
                  <td className="num">{entriesByClient.get(c.id) ?? 0}</td>
                  <td className="right">
                    {canEdit && (
                      <div
                        className="flex-row"
                        style={{ justifyContent: 'flex-end', gap: '0.75rem' }}
                      >
                        <Link
                          href={`/projects/manage?edit=cl:${c.id}`}
                          scroll={false}
                        >
                          Rename
                        </Link>
                        <Link
                          href={`/projects/manage?delete=cl:${c.id}`}
                          scroll={false}
                          style={{ color: '#b91c1c' }}
                        >
                          Delete
                        </Link>
                      </div>
                    )}
                  </td>
                </tr>
              ),
            )}
          </tbody>
        </table>
      </Section>

      <Section
        title="Projects"
        emptyMessage="No projects yet."
        toolbar={
          canEdit && !showNewProject ? (
            <Link
              href="/projects/manage?new=pr"
              className="button-link"
              scroll={false}
              style={{ fontSize: '0.85em' }}
            >
              + New project
            </Link>
          ) : null
        }
      >
        {showNewProject && canEdit && (
          <div
            style={{
              padding: '0.75rem 1rem',
              borderBottom: '1px solid #eee',
              background: '#f8fafc',
            }}
          >
            <form action={createProject}>
              <div
                style={{
                  display: 'grid',
                  gridTemplateColumns: '2fr 2fr auto auto',
                  gap: '0.5rem',
                  alignItems: 'end',
                }}
              >
                <label style={{ fontSize: '0.85em' }}>
                  Under client{' '}
                  <span className="muted">({'{operator}'} : {'{client}'})</span>
                  <SearchableCombobox
                    name="client_id"
                    options={clientPickerOptions}
                    placeholder="search clients…"
                  />
                </label>
                <label style={{ fontSize: '0.85em' }}>
                  Project name
                  <input
                    type="text"
                    name="name"
                    required
                    autoFocus
                    placeholder="e.g. Monthly Services"
                  />
                </label>
                <button type="submit">Create project</button>
                <Link
                  href="/projects/manage"
                  className="button-link-secondary"
                  scroll={false}
                >
                  Cancel
                </Link>
              </div>
              <p
                className="muted"
                style={{ fontSize: '0.8em', margin: '0.4rem 0 0' }}
              >
                Only existing clients are pickable. To create a new
                client / operator, type a new name in the Entries
                edit form (the cascading combobox auto-creates them)
                or use the import resolver.
              </p>
            </form>
          </div>
        )}

        <table className="data">
          <thead>
            <tr>
              <th>Operator</th>
              <th>Client</th>
              <th>Project</th>
              <th className="num">Entries</th>
              <th className="num">Income</th>
              <th />
            </tr>
          </thead>
          <tbody>
            {(projects ?? []).map((p) =>
              editScope === 'pr' && editId === p.id ? (
                <EditRow
                  key={p.id}
                  id={p.id}
                  scope="pr"
                  currentName={p.name}
                  context={`this project (${
                    p.client?.operator?.name ?? '—'
                  } / ${p.client?.name ?? '—'})`}
                  affectedEntries={entriesByProject.get(p.id) ?? 0}
                  action={renameProject}
                  prefixCols={2}
                />
              ) : deleteScope === 'pr' && deleteTargetId === p.id ? (
                <DeleteProjectRow
                  key={p.id}
                  id={p.id}
                  label={`${p.client?.name ?? '—'} : ${p.name}`}
                  affectedEntries={entriesByProject.get(p.id) ?? 0}
                  projectOptions={projectPickerOptions.filter(
                    (o) => o.id !== p.id,
                  )}
                />
              ) : (
                <tr key={p.id}>
                  <td className="muted">{p.client?.operator?.name ?? '—'}</td>
                  <td className="muted">{p.client?.name ?? '—'}</td>
                  <td>{p.name}</td>
                  <td className="num">{entriesByProject.get(p.id) ?? 0}</td>
                  <td className="num">
                    {p.income_usd == null ? (
                      <span className="muted">—</span>
                    ) : (
                      `$${Number(p.income_usd).toFixed(2)}`
                    )}
                  </td>
                  <td className="right">
                    {canEdit && (
                      <div
                        className="flex-row"
                        style={{
                          justifyContent: 'flex-end',
                          gap: '0.75rem',
                        }}
                      >
                        <Link
                          href={`/projects/manage?edit=pr:${p.id}`}
                          scroll={false}
                        >
                          Rename
                        </Link>
                        <Link
                          href={`/projects/manage?delete=pr:${p.id}`}
                          scroll={false}
                          style={{ color: '#b91c1c' }}
                        >
                          Delete
                        </Link>
                      </div>
                    )}
                  </td>
                </tr>
              ),
            )}
          </tbody>
        </table>
      </Section>
    </main>
  )
}

/**
 * Inline delete confirmation. Common shell used by all three
 * entity-level deletes. The picker is hidden when `hasChildren` is
 * false — the action then deletes the source directly. The picker's
 * input `name` differs per scope (`target_project_id` /
 * `target_client_id` / `target_operator_id`) so the corresponding
 * action picks it up cleanly.
 */
function DeleteRowShell({
  action,
  id,
  colSpan,
  title,
  warning,
  hasChildren,
  pickerName,
  pickerLabel,
  pickerOptions,
}: {
  action: (formData: FormData) => Promise<void> | void
  id: string
  colSpan: number
  title: React.ReactNode
  warning: React.ReactNode
  hasChildren: boolean
  pickerName: string
  pickerLabel: React.ReactNode
  pickerOptions: { id: string; label: string }[]
}) {
  return (
    <tr style={{ background: '#fef2f2' }}>
      <td colSpan={colSpan} style={{ padding: '1rem' }}>
        <form action={action}>
          <input type="hidden" name="id" value={id} />
          <p style={{ margin: '0 0 0.5rem' }}>
            {title}{' '}
            <span className="muted">{warning}</span>
          </p>
          <div
            style={{
              display: 'grid',
              gridTemplateColumns: hasChildren ? '2fr auto auto' : '1fr auto auto',
              gap: '0.5rem',
              alignItems: 'end',
            }}
          >
            {hasChildren ? (
              <label style={{ fontSize: '0.85em' }}>
                {pickerLabel}
                <SearchableCombobox
                  name={pickerName}
                  options={pickerOptions}
                  placeholder="search…"
                />
              </label>
            ) : (
              <span
                className="muted"
                style={{ fontSize: '0.85em', alignSelf: 'center' }}
              >
                Nothing references this — straight delete.
              </span>
            )}
            <button
              type="submit"
              style={{ background: '#b91c1c', color: '#fff' }}
            >
              {hasChildren ? 'Delete & reassign' : 'Delete'}
            </button>
            <Link
              href="/projects/manage"
              className="button-link-secondary"
              scroll={false}
            >
              Cancel
            </Link>
          </div>
        </form>
      </td>
    </tr>
  )
}

function DeleteProjectRow({
  id,
  label,
  affectedEntries,
  projectOptions,
}: {
  id: string
  label: string
  affectedEntries: number
  projectOptions: { id: string; label: string }[]
}) {
  const hasChildren = affectedEntries > 0
  return (
    <DeleteRowShell
      action={deleteProject}
      id={id}
      colSpan={6}
      title={
        <>
          Delete <strong>{label}</strong>?
        </>
      }
      warning={
        hasChildren ? (
          <>
            {affectedEntries} entries (and any matching CC expense
            lines) will be reassigned to the target project below.
            Rate overrides on the source are dropped. The source&apos;s
            income / billout adjustments are <strong>lost</strong>.
          </>
        ) : (
          <>No entries point at this project. Safe to remove directly.</>
        )
      }
      hasChildren={hasChildren}
      pickerName="target_project_id"
      pickerLabel={
        <>
          Reassign entries to{' '}
          <span className="muted">({'{client}'} : {'{project}'})</span>
        </>
      }
      pickerOptions={projectOptions}
    />
  )
}

function DeleteClientRow({
  id,
  label,
  projectCount,
  affectedEntries,
  clientOptions,
}: {
  id: string
  label: string
  projectCount: number
  affectedEntries: number
  clientOptions: { id: string; label: string }[]
}) {
  const hasChildren = projectCount > 0
  return (
    <DeleteRowShell
      action={deleteClient}
      id={id}
      colSpan={5}
      title={
        <>
          Delete <strong>{label}</strong>?
        </>
      }
      warning={
        hasChildren ? (
          <>
            {projectCount} project{projectCount === 1 ? '' : 's'} (
            {affectedEntries} entries total) will be re-parented to the
            target client. The target&apos;s operator becomes the
            entries&apos; operator too if it differs from the source&apos;s.
          </>
        ) : (
          <>No projects under this client. Safe to remove directly.</>
        )
      }
      hasChildren={hasChildren}
      pickerName="target_client_id"
      pickerLabel={
        <>
          Reassign projects to client{' '}
          <span className="muted">({'{operator}'} : {'{client}'})</span>
        </>
      }
      pickerOptions={clientOptions}
    />
  )
}

function DeleteOperatorRow({
  id,
  label,
  clientCount,
  affectedEntries,
  operatorOptions,
}: {
  id: string
  label: string
  clientCount: number
  affectedEntries: number
  operatorOptions: { id: string; label: string }[]
}) {
  const hasChildren = clientCount > 0
  return (
    <DeleteRowShell
      action={deleteOperator}
      id={id}
      colSpan={4}
      title={
        <>
          Delete operator <strong>{label}</strong>?
        </>
      }
      warning={
        hasChildren ? (
          <>
            {clientCount} client{clientCount === 1 ? '' : 's'} (
            {affectedEntries} entries total) will be re-parented to
            the target operator.
          </>
        ) : (
          <>No clients under this operator. Safe to remove directly.</>
        )
      }
      hasChildren={hasChildren}
      pickerName="target_operator_id"
      pickerLabel={<>Reassign clients to operator</>}
      pickerOptions={operatorOptions}
    />
  )
}

function Section({
  title,
  emptyMessage,
  children,
  toolbar,
}: {
  title: string
  emptyMessage: string
  children: React.ReactNode
  /** Optional right-aligned actions next to the section heading. */
  toolbar?: React.ReactNode
}) {
  return (
    <div className="panel" style={{ padding: 0, overflow: 'hidden' }}>
      <div
        style={{
          padding: '0.75rem 1rem 0.25rem',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
        }}
      >
        <h2 style={{ margin: 0, fontSize: '1.05rem' }}>{title}</h2>
        {toolbar}
      </div>
      <div className="table-wrap" style={{ border: 'none', borderRadius: 0 }}>
        {children}
      </div>
      <p
        className="muted"
        style={{
          padding: '0 1rem 0.5rem',
          fontSize: '0.8em',
          display: 'none',
        }}
      >
        {emptyMessage}
      </p>
    </div>
  )
}

function EditRow({
  id,
  scope,
  currentName,
  context,
  affectedEntries,
  action,
  prefixCols = 0,
}: {
  id: string
  scope: 'op' | 'cl' | 'pr'
  currentName: string
  context: string
  affectedEntries: number
  action: (formData: FormData) => Promise<void>
  prefixCols?: number
}) {
  const _ = scope
  return (
    <tr style={{ background: '#fff8e1' }}>
      {Array.from({ length: prefixCols }).map((__, i) => (
        <td key={i} className="muted" />
      ))}
      <td colSpan={6 - prefixCols} style={{ padding: '0.75rem 1rem' }}>
        <form
          action={action}
          style={{
            display: 'flex',
            gap: '0.5rem',
            alignItems: 'flex-end',
            flexWrap: 'wrap',
          }}
        >
          <input type="hidden" name="id" value={id} />
          <label
            style={{
              display: 'flex',
              flexDirection: 'column',
              fontSize: '0.85em',
              flex: '1 1 14rem',
            }}
          >
            New name
            <input
              name="name"
              type="text"
              defaultValue={currentName}
              required
              autoFocus
              style={{ minWidth: '14rem' }}
            />
          </label>
          <button type="submit">Save</button>
          <Link
            href="/projects/manage"
            className="button-link-secondary"
            scroll={false}
          >
            Cancel
          </Link>
          <span
            className="muted"
            style={{ fontSize: '0.85em', marginLeft: '0.5rem' }}
          >
            Will rename {context} and update <strong>{affectedEntries}</strong>{' '}
            entr{affectedEntries === 1 ? 'y' : 'ies'}.
          </span>
        </form>
      </td>
    </tr>
  )
}
